New addon: Portredirect 1.0

Message ID 20210627134819.2088-1-matthias.fischer@ipfire.org
State Dropped
Headers
Series New addon: Portredirect 1.0 |

Commit Message

Matthias Fischer June 27, 2021, 1:48 p.m. UTC
  From: Marcel Lorenz <marcel.lorenz@ipfire.org>

Please note:
This is a new addon written by Marcel Lorenz <marcel.lorenz@ipfire.org>.

It adds a new GUI to IPFire for DNS/NTP *and* user specific port redirections.

How its working:
It has exactly the same functionalities as "Forcing DNS/NTP..."  - and some more.

By setting switches, DNS/NTP requests are automatically
redirected to the local IPFire DNS/NTP servers.

Additionally, the user can specify custom redirections.

These rules are added to a new chain in PREROUTING => PORT_REDIRECT.

To avoid problems with (e.g.) transparent 'squid' configurations,
redirection rules are added automatically before existing 'squid' rules.

Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
---
 config/portredir/EX-portredir.menu    |   6 +
 config/portredir/lang/portredir.de.pl |  19 +
 config/portredir/lang/portredir.en.pl |  19 +
 config/portredir/portredir-backup     |   1 +
 config/portredir/portredir.cgi        | 525 ++++++++++++++++++++++++++
 config/rootfiles/common/misc-progs    |   1 +
 config/rootfiles/packages/portredir   |  11 +
 lfs/portredir                         |  85 +++++
 make.sh                               |   1 +
 src/initscripts/packages/portredir    | 191 ++++++++++
 src/misc-progs/Makefile               |   2 +-
 src/misc-progs/portredirctrl.c        |  47 +++
 src/paks/portredir/install.sh         |  32 ++
 src/paks/portredir/uninstall.sh       |  28 ++
 src/paks/portredir/update.sh          |  26 ++
 15 files changed, 993 insertions(+), 1 deletion(-)
 create mode 100644 config/portredir/EX-portredir.menu
 create mode 100644 config/portredir/lang/portredir.de.pl
 create mode 100644 config/portredir/lang/portredir.en.pl
 create mode 100644 config/portredir/portredir-backup
 create mode 100644 config/portredir/portredir.cgi
 create mode 100644 config/rootfiles/packages/portredir
 create mode 100644 lfs/portredir
 create mode 100644 src/initscripts/packages/portredir
 create mode 100644 src/misc-progs/portredirctrl.c
 create mode 100644 src/paks/portredir/install.sh
 create mode 100644 src/paks/portredir/uninstall.sh
 create mode 100644 src/paks/portredir/update.sh
  

Comments

Michael Tremer June 28, 2021, 4:04 p.m. UTC | #1
Hello Matthias,

> On 27 Jun 2021, at 14:48, Matthias Fischer <matthias.fischer@ipfire.org> wrote:
> 
> From: Marcel Lorenz <marcel.lorenz@ipfire.org>

Thank you for sending this patch on Marcel’s behalf, but I would much more prefer if he would submit his patches on his own. I do not see why that isn’t possible.

> Please note:
> This is a new addon written by Marcel Lorenz <marcel.lorenz@ipfire.org>.
> 
> It adds a new GUI to IPFire for DNS/NTP *and* user specific port redirections.
> 
> How its working:
> It has exactly the same functionalities as "Forcing DNS/NTP..."  - and some more.
> 
> By setting switches, DNS/NTP requests are automatically
> redirected to the local IPFire DNS/NTP servers.
> 
> Additionally, the user can specify custom redirections.
> 
> These rules are added to a new chain in PREROUTING => PORT_REDIRECT.
> 
> To avoid problems with (e.g.) transparent 'squid' configurations,
> redirection rules are added automatically before existing 'squid' rules.

This message does unfortunately not say why this add-on would be useful. I am emphasising this again and again that it is not very important how something is done specially. That should be commented in the code and other implementation details should also be documented there.

As I have stated on this functionality many times before, I do not see why this is necessary at all.

Why is this an add-on?

Why do we not extend the firewall UI probably by about 20 lines of code instead of adding many hundreds of lines?

Please can someone elaborate on this more?

-Michael

> Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
> ---
> config/portredir/EX-portredir.menu    |   6 +
> config/portredir/lang/portredir.de.pl |  19 +
> config/portredir/lang/portredir.en.pl |  19 +
> config/portredir/portredir-backup     |   1 +
> config/portredir/portredir.cgi        | 525 ++++++++++++++++++++++++++
> config/rootfiles/common/misc-progs    |   1 +
> config/rootfiles/packages/portredir   |  11 +
> lfs/portredir                         |  85 +++++
> make.sh                               |   1 +
> src/initscripts/packages/portredir    | 191 ++++++++++
> src/misc-progs/Makefile               |   2 +-
> src/misc-progs/portredirctrl.c        |  47 +++
> src/paks/portredir/install.sh         |  32 ++
> src/paks/portredir/uninstall.sh       |  28 ++
> src/paks/portredir/update.sh          |  26 ++
> 15 files changed, 993 insertions(+), 1 deletion(-)
> create mode 100644 config/portredir/EX-portredir.menu
> create mode 100644 config/portredir/lang/portredir.de.pl
> create mode 100644 config/portredir/lang/portredir.en.pl
> create mode 100644 config/portredir/portredir-backup
> create mode 100644 config/portredir/portredir.cgi
> create mode 100644 config/rootfiles/packages/portredir
> create mode 100644 lfs/portredir
> create mode 100644 src/initscripts/packages/portredir
> create mode 100644 src/misc-progs/portredirctrl.c
> create mode 100644 src/paks/portredir/install.sh
> create mode 100644 src/paks/portredir/uninstall.sh
> create mode 100644 src/paks/portredir/update.sh
> 
> diff --git a/config/portredir/EX-portredir.menu b/config/portredir/EX-portredir.menu
> new file mode 100644
> index 000000000..8376e8053
> --- /dev/null
> +++ b/config/portredir/EX-portredir.menu
> @@ -0,0 +1,6 @@
> +    $subfirewall->{'95.portredir'} = {
> +				'caption' => $Lang::tr{'portredir port redirections'},
> +				'uri' => '/cgi-bin/portredir.cgi',
> +				'title' => "$Lang::tr{'portredir port redirections'}",
> +				'enabled' => 1
> +				};
> diff --git a/config/portredir/lang/portredir.de.pl b/config/portredir/lang/portredir.de.pl
> new file mode 100644
> index 000000000..b932d4a85
> --- /dev/null
> +++ b/config/portredir/lang/portredir.de.pl
> @@ -0,0 +1,19 @@
> +%tr = (
> +%tr,
> +'portredir enable addon' => 'Addon aktivieren',
> +'portredir common settings' => 'Allgemeine Einstellungen',
> +'portredir port redirections' => 'Portumleitungen',
> +'portredir fw for interface' => 'Firewalloptionen für das Interface',
> +'portredir enable user redirections' => 'Aktiviere benutzerdefinierte Portumleitungen',
> +'portredir force local dns' => 'Erzwinge lokale DNS-Server',
> +'portredir force local ntp' => 'Erzwinge lokale NTP-Server',
> +'portredir custom redirections' => 'Benutzerdefinierte Portumleitungen',
> +'portredir remove rule' => 'Entferne Regel',
> +'portredir add rule' => 'Hinzufügen',
> +'portredir no entries' => 'Keine Einträge vorhanden.',
> +'portredir invalid address' => 'Ungültige Host-Addresse.',
> +'portredir empty input' => 'Fehlende Angabe: Bitte geben Sie einen gültigen Host an.',
> +'portredir save to activate' => 'Speichern, um Änderungen zu aktivieren',
> +);
> +
> +#EOF
> diff --git a/config/portredir/lang/portredir.en.pl b/config/portredir/lang/portredir.en.pl
> new file mode 100644
> index 000000000..f442f3eaa
> --- /dev/null
> +++ b/config/portredir/lang/portredir.en.pl
> @@ -0,0 +1,19 @@
> +%tr = (
> +%tr,
> +'portredir enable addon' => 'Enable addon',
> +'portredir common settings' => 'Common settings',
> +'portredir port redirections' => 'Port redirections',
> +'portredir fw for interface' => 'Firewall options for interface',
> +'portredir enable user redirections' => 'Enable user port redirections',
> +'portredir force local dns' => 'Enforce local DNS servers',
> +'portredir force local ntp' => 'Enforce local NTP servers',
> +'portredir custom redirections' => 'Custom port redirections',
> +'portredir remove rule' => 'Remove rule',
> +'portredir add rule' => 'Add new',
> +'portredir no entries' => 'No entries at the moment.',
> +'portredir invalid address' => 'Invalid host address.',
> +'portredir empty input' => 'Empty input: Please enter a valid host.',
> +'portredir save to activate' => 'Save to activate changes',
> +);
> +
> +#EOF
> diff --git a/config/portredir/portredir-backup b/config/portredir/portredir-backup
> new file mode 100644
> index 000000000..bd2ada742
> --- /dev/null
> +++ b/config/portredir/portredir-backup
> @@ -0,0 +1 @@
> +/var/ipfire/portredir
> diff --git a/config/portredir/portredir.cgi b/config/portredir/portredir.cgi
> new file mode 100644
> index 000000000..4913dda3f
> --- /dev/null
> +++ b/config/portredir/portredir.cgi
> @@ -0,0 +1,525 @@
> +#!/usr/bin/perl
> +###############################################################################
> +#                                                                             #
> +# IPFire.org - A linux based firewall                                         #
> +# Copyright (C) 2021  IPFire Team  <info@ipfire.org>                          #
> +#                                                                             #
> +# This program is free software: you can redistribute it and/or modify        #
> +# it under the terms of the GNU General Public License as published by        #
> +# the Free Software Foundation, either version 3 of the License, or           #
> +# (at your option) any later version.                                         #
> +#                                                                             #
> +# This program is distributed in the hope that it will be useful,             #
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
> +# GNU General Public License for more details.                                #
> +#                                                                             #
> +# You should have received a copy of the GNU General Public License           #
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
> +#                                                                             #
> +###############################################################################
> +
> +use strict;
> +
> +# enable only the following on debugging purpose
> +use warnings;
> +use CGI::Carp 'fatalsToBrowser';
> +
> +require '/var/ipfire/general-functions.pl';
> +require "${General::swroot}/lang.pl";
> +require "${General::swroot}/header.pl";
> +
> +# File declarations
> +my $settingsfile = "${General::swroot}/portredir/settings";
> +my $redirectsfile = "${General::swroot}/portredir/redirects";
> +
> +# Create empty settingsfiles if they does not exist yet
> +unless (-e "$settingsfile") { system ("touch $settingsfile"); }
> +unless (-e "$redirectsfile") { system ("touch $redirectsfile"); }
> +
> +# load ipfire settings
> +our %netsettings = ();
> +our %color = ();
> +&General::readhash("${General::swroot}/ethernet/settings", \%netsettings);
> +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color);
> +
> +my %settings=();
> +my %portredirs=();
> +my %checked=(); # Checkbox manipulations
> +my $errormessage='';
> +my %selected=();
> +our %redirects=();
> +
> +$settings{'ACTION'} = '';
> +$settings{'REDIR_ENABLE_ADDON'}="off";
> +$settings{'REDIR_CUSTOM_GREEN'}="off";
> +$settings{'REDIR_CUSTOM_BLUE'}="off";
> +$settings{'REDIR_CUSTOM_ORANGE'}="off";
> +$settings{'REDIR_DNS_GREEN'}="off";
> +$settings{'REDIR_NTP_GREEN'}="off";
> +$settings{'REDIR_DNS_BLUE'}="off";
> +$settings{'REDIR_NTP_BLUE'}="off";
> +$settings{'REDIR_DNS_ORANGE'}="off";
> +$settings{'REDIR_NTP_ORANGE'}="off";
> +
> +&Header::showhttpheaders();
> +
> +# Get GUI values
> +&Header::getcgihash(\%settings);
> +
> +# Save action
> +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
> +
> +	# If custom rules enabled, deactivate default rules on interface
> +	if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
> +		$settings{'REDIR_DNS_GREEN'}="off";
> +		$settings{'REDIR_NTP_GREEN'}="off";
> +	}
> +
> +	if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
> +		$settings{'REDIR_DNS_BLUE'}="off";
> +		$settings{'REDIR_NTP_BLUE'}="off";
> +	}
> +
> +	if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
> +		$settings{'REDIR_DNS_ORANGE'}="off";
> +		$settings{'REDIR_NTP_ORANGE'}="off";
> +	}
> +
> +	&General::writehash($settingsfile, \%settings);
> +
> +	if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
> +		system ('/usr/local/bin/portredirctrl restart >/dev/null 2>&1');
> +		system ('/usr/local/bin/portredirctrl enable >/dev/null 2>&1');
> +		&General::log('portredir addon: port redirections enabled');
> +	}
> +	if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
> +		system ('/usr/local/bin/portredirctrl disable >/dev/null 2>&1');
> +		system ('/usr/local/bin/portredirctrl stop >/dev/null 2>&1');
> +		&General::log('portredir addon: port redirections disabled');
> +	}
> +
> +# Add/edit an entry to the redirectsfile.
> +
> +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) || ($settings{'ACTION'} eq $Lang::tr{'update'})) {
> +
> +	# Check if any input has been performed.
> +	if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
> +
> +		# Check if the given input is no valid IP-address, display an error message.
> +		if (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
> +			$errormessage = "$Lang::tr{'portredir invalid address'}";
> +		}
> +	} else {
> +		$errormessage = "$Lang::tr{'portredir empty input'}";
> +	}
> +
> +	# Go further if there was no error.
> +	if ($errormessage eq '') {
> +		my %redirects = ();
> +		my $id;
> +		my $status;
> +
> +		# Assign hash values.
> +		my $new_entry_interface = $settings{'REDIR_ENTRY_INTERFACE'};
> +		my $new_entry_protocol = $settings{'REDIR_ENTRY_PROTOCOL'};
> +		my $new_entry_port = $settings{'REDIR_ENTRY_PORT'};
> +		my $new_entry_address = $settings{'REDIR_ENTRY_ADDRESS'};
> +		my $new_entry_remark = $settings{'REDIR_ENTRY_REMARK'};
> +
> +		# Read-in redirectsfile.
> +		&General::readhasharray($redirectsfile, \%redirects);
> +
> +		# Check if we should edit an existing entry and got an ID.
> +		if (($settings{'ACTION'} eq $Lang::tr{'update'}) && ($settings{'ID'})) {
> +			# Assin the provided id.
> +			$id = $settings{'ID'};
> +
> +			# Undef the given ID.
> +			undef($settings{'ID'});
> +
> +			# Grab the configured status of the corresponding entry.
> +			$status = $redirects{$id}[4];
> +		} else {
> +			# Each newly added entry automatically should be enabled.
> +			$status = "enabled";
> +
> +			# Generate the ID for the new entry.
> +			#
> +			# Sort the keys by their ID and store them in an array.
> +			my @keys = sort { $a <=> $b } keys %redirects;
> +
> +			# Reverse the key array.
> +			my @reversed = reverse(@keys);
> +
> +			# Obtain the last used id.
> +			my $last_id = @reversed[0];
> +
> +			# Increase the last id by one and use it as id for the new entry.
> +			$id = ++$last_id;
> +		}
> +
> +		# Add/Modify the entry to/in the redirects hash.
> +		$redirects{$id} = ["$new_entry_interface", "$new_entry_protocol", "$new_entry_port", "$new_entry_address","$status", "$new_entry_remark"];
> +
> +		# Write the changed redirects hash to the redirects file.
> +		&General::writehasharray($redirectsfile, \%redirects);
> +	}
> +
> +# Toggle Enabled/Disabled for an existing entry on the redirects list.
> +
> +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable disable'}) {
> +	my %redirects = ();
> +
> +	# Only go further, if an ID has been passed.
> +	if ($settings{'ID'}) {
> +		# Assign the given ID.
> +		my $id = $settings{'ID'};
> +
> +		# Undef the given ID.
> +		undef($settings{'ID'});
> +
> +		# Read-in ignoredfile.
> +		&General::readhasharray($redirectsfile, \%redirects);
> +
> +		# Grab the configured status of the corresponding entry.
> +		my $status = $redirects{$id}[4];
> +
> +		# Switch the status.
> +		if ($status eq "disabled") {
> +			$status = "enabled";
> +		} else {
> +			$status = "disabled";
> +		}
> +
> +		# Modify the status of the existing entry.
> +		$redirects{$id} = ["$redirects{$id}[0]", "$redirects{$id}[1]", "$redirects{$id}[2]", "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
> +
> +		# Write the changed ignored hash to the redirects file.
> +		&General::writehasharray($redirectsfile, \%redirects);
> +	}
> +
> +# Remove entry from redirects list.
> +
> +} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
> +	my %redirects = ();
> +
> +	# Read-in redirectsfile.
> +	&General::readhasharray($redirectsfile, \%redirects);
> +
> +	# move data on key up
> +	foreach my $key (sort keys %redirects) {
> +		if ($key >= $settings{'ID'}) {
> +			my $next = $key + 1;
> +			if (exists $redirects{$next}) {
> +				foreach my $i (0 .. $#{$redirects{$next}}) { $redirects{$key}[$i] = $redirects{$next}[$i]; }
> +			}
> +		}
> +	}
> +
> +	my $last_key = (sort {$a <=> $b} keys %redirects)[-1];
> +	delete $redirects{$last_key};
> +
> +	# Undef the given ID.
> +	undef($settings{'ID'});
> +
> +	# Write the changed redirects hash to file.
> +	&General::writehasharray($redirectsfile, \%redirects);
> +}
> +
> +# Load settings from file
> +&General::readhash($settingsfile, \%settings);
> +&General::readhasharray($redirectsfile, \%redirects);
> +
> +# Call functions to generate whole page.
> +&Header::openpage($Lang::tr{'portredir port redirections'}, 1, '');
> +&Header::openbigbox('100%', 'left', '', $errormessage);
> +
> +if ($errormessage) {
> +        &Header::openbox('100%', 'left', $Lang::tr{'warning messages'});
> +        print "<font color='red'>$errormessage&nbsp;</font>";
> +        &Header::closebox();
> +}
> +
> +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
> +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
> +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_ADDON'}} = "checked='checked'";
> +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
> +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
> +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_GREEN'}} = "checked='checked'";
> +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
> +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
> +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BLUE'}} = "checked='checked'";
> +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
> +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
> +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_ORANGE'}} = "checked='checked'";
> +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
> +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
> +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}} = "checked='checked'";
> +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
> +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
> +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}} = "checked='checked'";
> +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
> +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
> +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}} = "checked='checked'";
> +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
> +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
> +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}} = "checked='checked'";
> +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
> +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
> +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE'}} = "checked='checked'";
> +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
> +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
> +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE'}} = "checked='checked'";
> +
> +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTRY_INTERFACE'}} = 'selected';
> +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY_PROTOCOL'}} = 'selected';
> +
> +&showMainBox();
> +&showRedirectsBox();
> +
> +&Header::closebigbox();
> +&Header::closepage();
> +
> +# Function to show main settings and options.
> +sub showMainBox() {
> +
> +	&Header::openbox('100%', 'center', "$Lang::tr{'settings'}");
> +	print "<form method='post' action='$ENV{'SCRIPT_NAME'}'>";
> +
> +print <<END;
> +<table width='80%' cellspacing='0' border='0'>
> +	<tr><td colspan='2' class='base' bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir common settings'}</b></td></tr
> +	<tr>
> +		<td width='25%' class='base'>$Lang::tr{'portredir enable addon'}:</td>
> +		<td><input type='checkbox' name='REDIR_ENABLE_ADDON' $checked{'REDIR_ENABLE_ADDON'}{'on'} /></td>
> +	</tr>
> +	<tr><td colspan='2'></td></tr>
> +	<tr><td colspan='2'>&nbsp;</td></tr>
> +END
> +
> +	# create html table with header line 1
> +	print "<table width='80%' cellspacing='0' border='0'><tr>";
> +	print "<th class='base' width='40%' align='left'><b>$Lang::tr{'portredir fw for interface'}</th>";
> +	if ($netsettings{'GREEN_DEV'})  {print "<th class='base' width='10%'><b><font color=green>$Lang::tr{'green'}</font></th>";
> +		} else {		 print "<th class='base' width='10%'></font></th>"; }
> +	if ($netsettings{'BLUE_DEV'})   {print "<th class='base' width='10%'><b><font color=blue>$Lang::tr{'blue'}</font></th>";
> +		} else {		 print "<th class='base' width='10%'></font></th>"; }
> +	if ($netsettings{'ORANGE_DEV'}) {print "<th class='base' width='10%'><b><font color=orange>$Lang::tr{'orange'}</font></th>";
> +		} else {		 print "<th class='base' width='10%'></font></th>"; }
> +
> +	# the empty right row
> +	print "<th class='base' width='30%'><td></td></th></tr>";
> +
> +	# line 2
> +	print "<tr><td>$Lang::tr{'portredir force local dns'}</td>";
> +	if ($netsettings{'GREEN_DEV'})  {print "<td class='base' align='center'><input type='checkbox' name='REDIR_DNS_GREEN' $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print "<td></td>";}
> +	if ($netsettings{'BLUE_DEV'})   {print "<td class='base' align='center'><input type='checkbox' name='REDIR_DNS_BLUE' $checked{'REDIR_DNS_BLUE'}{'on'}></td>";} else { print "<td></td>";}
> +	if ($netsettings{'ORANGE_DEV'}) {print "<td class='base' align='center'><input type='checkbox' name='REDIR_DNS_ORANGE' $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print "<td></td>";}
> +
> +	# line 3
> +	print "</tr><tr><td>$Lang::tr{'portredir force local ntp'}</td>";
> +	if ($netsettings{'GREEN_DEV'})  {print "<td class='base' align='center'><input type='checkbox' name='REDIR_NTP_GREEN' $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print "<td></td>";}
> +	if ($netsettings{'BLUE_DEV'})   {print "<td class='base' align='center'><input type='checkbox' name='REDIR_NTP_BLUE' $checked{'REDIR_NTP_BLUE'}{'on'}></td>";} else { print "<td></td>";}
> +	if ($netsettings{'ORANGE_DEV'}) {print "<td class='base' align='center'><input type='checkbox' name='REDIR_NTP_ORANGE' $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print "<td></td>";}
> +
> +	# line 4
> +	print "</tr><tr><td>$Lang::tr{'portredir enable user redirections'}</td>";
> +	if ($netsettings{'GREEN_DEV'})  {print "<td class='base' align='center'><input type='checkbox' name='REDIR_CUSTOM_GREEN' $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else { print "<td></td>";}
> +	if ($netsettings{'BLUE_DEV'})   {print "<td class='base' align='center'><input type='checkbox' name='REDIR_CUSTOM_BLUE' $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print "<td></td>";}
> +	if ($netsettings{'ORANGE_DEV'}) {print "<td class='base' align='center'><input type='checkbox' name='REDIR_CUSTOM_ORANGE' $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else { print "<td></td>";}
> +
> +	print <<END;
> +	</tr></table>
> +	<table width='80%' cellspacing='0' border='0'>
> +		<tr><td colspan='2'>&nbsp;</td></tr>
> +		<tr><td align='left'><b>$Lang::tr{'portredir save to activate'}</b></td><td width='5%' align='center'><input type='submit' name='ACTION' value='  $Lang::tr{'save'}  '></td></tr>
> +	</table></form>
> +END
> +
> +&Header::closebox();
> +}
> +
> +# Function to show elements of the redirects file and allow to add or remove single members of it.
> +sub showRedirectsBox() {
> +        &Header::openbox('100%', 'center', "$Lang::tr{'portredir custom redirections'}");
> +
> +	print <<END;
> +		<table width='80%' cellspacing='1' border='0'>
> +			<tr>
> +				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'interface'}</b></td>
> +				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'protocol'}</b></td>
> +				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'port'}</b></td>
> +				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'ip address'}</b></td>
> +				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'remark'}</b></td>
> +				<td class='base' colspan='3' bgcolor='$color{'color20'}'></td>
> +			</tr>
> +END
> +			# Check if some rules have been added to be redirects.
> +			if (keys (%redirects)) {
> +				my $col = "";
> +
> +				# List all entries of the hash.
> +				foreach my $key (sort keys %redirects){
> +
> +					# Assign data array positions to some nice variable names.
> +					my $interface = $redirects{$key}[0];
> +					my $protocol = $redirects{$key}[1];
> +					my $port  = $redirects{$key}[2];
> +					my $address = $redirects{$key}[3];
> +					my $status  = $redirects{$key}[4];
> +					my $remark  = $redirects{$key}[5];
> +
> +					# Check if the key (id) number is even or not.
> +					if ($settings{'ID'} eq $key) {
> +						$col="bgcolor='${Header::colouryellow}'";
> +					} elsif ($key % 2) {
> +						$col="bgcolor='$color{'color22'}'";
> +					} else {
> +						$col="bgcolor='$color{'color20'}'";
> +					}
> +
> +					# Choose icon for the checkbox.
> +					my $gif;
> +					my $gdesc;
> +
> +					# Check if the status is enabled and select the correct image and description.
> +					if ($status eq 'enabled' ) {
> +						$gif = 'on.gif';
> +						$gdesc = $Lang::tr{'click to disable'};
> +					} else {
> +						$gif = 'off.gif';
> +						$gdesc = $Lang::tr{'click to enable'};
> +					}
> +
> +					print <<END;
> +					<tr>
> +						<td width='15%' class='base' align='center' $col><b><font color=$interface>$Lang::tr{$interface}</font></b></td>
> +						<td width='10%' class='base' align='center' $col>$protocol</td>
> +						<td width='10%' class='base' align='center' $col>$port</td>
> +						<td width='15%' class='base' align='center' $col>&nbsp;$address</td>
> +						<td width='40%' class='base' align='center' $col>&nbsp;$remark</td>
> +						<td align='center' $col>
> +							<form method='post' action='$ENV{'SCRIPT_NAME'}'>
> +								<input type='hidden' name='ACTION' value='$Lang::tr{'toggle enable disable'}' />
> +								<input type='image' name='$Lang::tr{'toggle enable disable'}' src='/images/$gif' alt='$gdesc' title='$gdesc' />
> +								<input type='hidden' name='ID' value='$key' />
> +							</form>
> +						</td>
> +						<td align='center' $col>
> +							<form method='post' action='$ENV{'SCRIPT_NAME'}'>
> +								<input type='hidden' name='ACTION' value='$Lang::tr{'edit'}' />
> +								<input type='image' name='$Lang::tr{'edit'}' src='/images/edit.gif' alt='$Lang::tr{'edit'}' title='$Lang::tr{'edit'}' />
> +								<input type='hidden' name='ID' value='$key' />
> +							</form>
> +						</td>
> +						<td align='center' $col>
> +							<form method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
> +								<input type='image' name='$Lang::tr{'remove'}' src='/images/delete.gif' title='$Lang::tr{'remove'}' alt='$Lang::tr{'remove'}'>
> +								<input type='hidden' name='ID' value='$key'>
> +								<input type='hidden' name='ACTION' value='$Lang::tr{'remove'}'>
> +							</form>
> +						</td>
> +					</tr>
> +END
> +				}
> +			} else {
> +				# Print notice that currently no ports are redirected.
> +				print "<tr>\n";
> +				print "<td class='base' colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
> +				print "</tr>\n";
> +			}
> +
> +		print "</table>\n";
> +
> +	# Section to add new elements or edit existing ones.
> +	print <<END;
> +	<br>
> +	<hr>
> +	<br>
> +	<div align='center'>
> +		<table width='100%' cellspacing='0' border='0'>
> +END
> +
> +	# Assign correct headline and button text.
> +	my $buttontext;
> +	my $entry_interface;
> +	my $entry_protocol;
> +	my $entry_port;
> +	my $entry_address;
> +	my $entry_remark;
> +
> +	# Check if an ID (key) has been given, in this case an existing entry should be edited.
> +	if ($settings{'ID'} ne '') {
> +		$buttontext = $Lang::tr{'update'};
> +		print "<tr><td class='boldbase' colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
> +
> +		# Grab address and remark for the given key.
> +		$entry_interface = $redirects{$settings{'ID'}}[0];
> +		$entry_protocol = $redirects{$settings{'ID'}}[1];
> +		$entry_port = $redirects{$settings{'ID'}}[2];
> +		$entry_address = $redirects{$settings{'ID'}}[3];
> +		$entry_remark = $redirects{$settings{'ID'}}[5];
> +
> +	} else {
> +		$buttontext = $Lang::tr{'add'};
> +		print "<tr><td class='boldbase' colspan='11'><b>$Lang::tr{'dnsforward add a new entry'}</b></td></tr>\n";
> +		print "<tr><td>&nbsp</td></tr>\n";
> +	}
> +
> +	print <<END;
> +			<tr>
> +				<td class='base' width='1%'  bgcolor='$color{'color22'}'></td>
> +				<td class='base' width='15%' bgcolor='$color{'color22'}' align='left'><b>$Lang::tr{'interface'}</b></td>
> +				<td class='base' width='10%' bgcolor='$color{'color22'}' align='left'><b>$Lang::tr{'protocol'}</b></td>
> +				<td class='base' width='10%' bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
> +				<td class='base' width='13%' bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'ip address'}</b></td>
> +				<td class='base' width='30%' bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
> +				<td class='base' width='15%' bgcolor='$color{'color22'}'></td>
> +				<td class='base' width='1%'  bgcolor='$color{'color22'}'></td>
> +			</tr>
> +
> +			<form method='post' action='$ENV{'SCRIPT_NAME'}'>
> +			<input type='hidden' name='ID' value='$settings{'ID'}'>
> +			<tr>
> +				<td class='base'></td>
> +				<td><select style='width:90px;' id='interface' name='REDIR_ENTRY_INTERFACE'>
> +END
> +				if ($netsettings{'GREEN_DEV'}) {
> +					if ($entry_interface eq "green") {
> +						print "<option value='green' selected='selected' {'green'}>$Lang::tr{'green'}</option>";
> +					} else { print "<option value='green' {'green'}>$Lang::tr{'green'}</option>";}
> +				}
> +				if ($netsettings{'BLUE_DEV'}) {
> +					if ($entry_interface eq "blue") { 
> +						print "<option value='blue' selected='selected' {'blue'}>$Lang::tr{'blue'}</option>";
> +					} else { print "<option value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
> +				}
> +				if ($netsettings{'ORANGE_DEV'}) {
> +					if ($entry_interface eq "orange") { 
> +						print "<option value='orange' selected='selected' {'orange'}>$Lang::tr{'orange'}</option>";
> +					} else { print "<option value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
> +				}
> +
> +			print "</select><td><select style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
> +			if ((!$entry_protocol) || ($entry_protocol eq "tcp")) {
> +				print "<option selected='selected' id='protocol' value='tcp' {'tcp'}>tcp</option>";
> +				print "<option value='udp' {'udp'}>udp</option>";
> +			} elsif ($entry_protocol eq "udp") {
> +				print "<option value='tcp' {'tcp'}>tcp</option>";
> +				print "<option selected='selected' value='udp' {'udp'}>udp</option>";
> +			}
> +	print <<END;
> +				</select></td>
> +				<td><input type='text' name='REDIR_ENTRY_PORT'    value='$entry_port'    size='4'></td>
> +				<td><input type='text' name='REDIR_ENTRY_ADDRESS' value='$entry_address' size='14'></td>
> +				<td><input type='text' name='REDIR_ENTRY_REMARK'  value='$entry_remark'  size='35'></td>
> +				<td width='10%' align='center'><input type='submit' name='ACTION' value='  $buttontext  '></td>
> +				<td class='base'></td>
> +			</tr>
> +			</form>
> +		</table>
> +	</div>
> +END
> +	&Header::closebox();
> +}
> diff --git a/config/rootfiles/common/misc-progs b/config/rootfiles/common/misc-progs
> index d6594b3f8..fbad2af8b 100644
> --- a/config/rootfiles/common/misc-progs
> +++ b/config/rootfiles/common/misc-progs
> @@ -17,6 +17,7 @@ usr/local/bin/logwatch
> #usr/local/bin/mpfirectrl
> usr/local/bin/openvpnctrl
> usr/local/bin/pakfire
> +#usr/local/bin/portredirctrl
> usr/local/bin/qosctrl
> usr/local/bin/rebuildhosts
> usr/local/bin/rebuildroutes
> diff --git a/config/rootfiles/packages/portredir b/config/rootfiles/packages/portredir
> new file mode 100644
> index 000000000..4b4ba8366
> --- /dev/null
> +++ b/config/rootfiles/packages/portredir
> @@ -0,0 +1,11 @@
> +etc/rc.d/init.d/portredir
> +etc/rc.d/rc0.d/K77portredir
> +etc/rc.d/rc3.d/S23portredir
> +etc/rc.d/rc6.d/K77portredir
> +srv/web/ipfire/cgi-bin/portredir.cgi
> +usr/local/bin/portredirctrl
> +var/ipfire/addon-lang/portredir.de.pl
> +var/ipfire/addon-lang/portredir.en.pl
> +var/ipfire/backup/addons/includes/portredir
> +var/ipfire/menu.d/EX-portredir.menu
> +var/ipfire/portredir
> diff --git a/lfs/portredir b/lfs/portredir
> new file mode 100644
> index 000000000..a4911f71f
> --- /dev/null
> +++ b/lfs/portredir
> @@ -0,0 +1,85 @@
> +###############################################################################
> +#                                                                             #
> +# IPFire.org - A linux based firewall                                         #
> +# Copyright (C) 2007-2021  IPFire Team  <info@ipfire.org>                     #
> +#                                                                             #
> +# This program is free software: you can redistribute it and/or modify        #
> +# it under the terms of the GNU General Public License as published by        #
> +# the Free Software Foundation, either version 3 of the License, or           #
> +# (at your option) any later version.                                         #
> +#                                                                             #
> +# This program is distributed in the hope that it will be useful,             #
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
> +# GNU General Public License for more details.                                #
> +#                                                                             #
> +# You should have received a copy of the GNU General Public License           #
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
> +#                                                                             #
> +###############################################################################
> +
> +###############################################################################
> +# Definitions
> +###############################################################################
> +
> +include Config
> +
> +VER        = 1.0
> +
> +THISAPP    = portredir-$(VER)
> +DIR_APP    = $(DIR_SRC)/$(THISAPP)
> +TARGET     = $(DIR_INFO)/$(THISAPP)
> +PROG       = portredir
> +PAK_VER    = 1
> +
> +###############################################################################
> +# Top-level Rules
> +###############################################################################
> +
> +install : $(TARGET)
> +
> +check :
> +
> +download :
> +
> +md5 :
> +
> +dist: 
> +	@$(PAK)
> +
> +###############################################################################
> +# Installation Details
> +###############################################################################
> +
> +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
> +	@$(PREBUILD)
> +	@rm -rf $(DIR_APP) && cd $(DIR_SRC)
> +
> +	#install cgi 
> +	install -v -m 755 $(DIR_CONF)/portredir/portredir.cgi /srv/web/ipfire/cgi-bin/
> +
> +	#create configuration dir 
> +	-mkdir -pv /var/ipfire/portredir/
> +	chown -R nobody:nobody /var/ipfire/portredir/
> +
> +	# Install include file for backup
> +	install -v -m 644 $(DIR_CONF)/portredir/portredir-backup /var/ipfire/backup/addons/includes/portredir
> +
> +	# Install menu file
> +	install -v -m 644 $(DIR_CONF)/portredir/EX-portredir.menu /var/ipfire/menu.d/
> +	chown nobody:nobody /var/ipfire/menu.d/EX-portredir.menu
> +
> +	# Install addon-specific language-files
> +	install -v -m 644 $(DIR_CONF)/portredir/lang/portredir.*.pl /var/ipfire/addon-lang/
> +
> +	#install initscripts
> +	$(call INSTALL_INITSCRIPT,portredir)
> +
> +	# Create symlinks for runlevel interaction.
> +	ln -svf /etc/rc.d/init.d/portredir /etc/rc.d/rc3.d/S23portredir
> +	ln -svf /etc/rc.d/init.d/portredir /etc/rc.d/rc0.d/K77portredir
> +	ln -svf /etc/rc.d/init.d/portredir /etc/rc.d/rc6.d/K77portredir
> +
> +	@rm -rf $(DIR_APP)
> +	@$(POSTBUILD)
> +
> diff --git a/make.sh b/make.sh
> index fc03ebcd5..ab9fe881a 100755
> --- a/make.sh
> +++ b/make.sh
> @@ -1623,6 +1623,7 @@ buildipfire() {
>   lfsmake2 socat
>   lfsmake2 libcdada
>   lfsmake2 pmacct
> +  lfsmake2 portredir
> }
> 
> buildinstaller() {
> diff --git a/src/initscripts/packages/portredir b/src/initscripts/packages/portredir
> new file mode 100644
> index 000000000..cc57fb9cc
> --- /dev/null
> +++ b/src/initscripts/packages/portredir
> @@ -0,0 +1,191 @@
> +#!/bin/sh
> +########################################################################
> +# Begin $rc_base/init.d/portredir
> +#
> +# Description : portredir init script for DNS/NTP and custom 
> +#		port redirection rules
> +#
> +########################################################################
> +
> +. /etc/sysconfig/rc
> +. ${rc_functions}
> +
> +IPT="/sbin/iptables";
> +parent_chain="PREROUTING";
> +chain="PORT_REDIRECT";
> +
> +confdir="/var/ipfire/portredir";
> +settingsfile="${confdir}/settings";
> +redirectsfile="${confdir}/redirects";
> +SYSLOG="NO";
> +VERBOSE="NO";
> +
> +eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings);
> +eval $(/usr/local/bin/readhash ${settingsfile});
> +
> +logtext() {
> +	if [ "${SYSLOG}" = "YES" ]; then logger -t "portredir" ${1}; fi;
> +	if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
> +
> +create_chain() {
> +
> +	local line=$(${IPT} -t nat -L ${parent_chain} --line-numbers |grep "SQUID" |awk '{printf($1)}');
> +
> +	if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z "${REDIR_ENABLE_ADDON}" ]]; then
> +		logtext "addon not enabled in web interface...";
> +		echo "Portredir addon not enabled in web interface...";
> +		exit 0;
> +	fi;
> +
> +	if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep ${chain})" ]; then
> +		${IPT} -t nat -N ${chain};
> +
> +		if [ ! -z "${line}" ]; then
> +			logtext "create chain ${chain} and link in ${parent_chain} at position ${line}...";
> +			${IPT} -t nat -I ${parent_chain} ${line} -j ${chain};
> +		else
> +			logtext "create chain ${chain} and link in ${parent_chain} at last position...";
> +			${IPT} -t nat -A ${parent_chain} -j ${chain};
> +		fi
> +	else
> +		return 1;
> +	fi;
> +	return 0;
> +}
> +
> +remove_chain() {
> +	if [ ! -z "$(${IPT} -t nat -L ${parent_chain} |grep ${chain})" ]; then
> +		logtext "remove chain ${chain} and link in ${parent_chain} from system...";
> +		${IPT} -t nat -D "${parent_chain}" -j ${chain};
> +		${IPT} -t nat -F ${chain};
> +		${IPT} -t nat -X ${chain};
> +	else
> +		return 1;
> +	fi;
> +	return 0;
> +}
> +
> +activate_custom_redirections() {
> +	
> +	local array=();
> +	local redirects=();
> +	local i;
> +	index=();
> +	iface=();
> +	protocol=();
> +	port=();
> +	targetip=();
> +	enabled=();
> +
> +	IFS=$'\n' read -d '' -ra redirects < ${redirectsfile};
> +
> +	for i in "${!redirects[@]}"
> +	do
> +		IFS=$',' read -ra array <<< ${redirects[i]};
> +		index[i]=${array[0]};
> +		iface[i]=${array[1]};
> +		protocol[i]=${array[2]};
> +		port[i]=${array[3]};
> +		targetip[i]=${array[4]};
> +		enabled[i]=${array[5]};
> +	done
> +
> +	for i in "${!index[@]}"
> +	do
> +		if [[ ! -z "${GREEN_DEV}" &&  "${iface[i]}" = "green" && "${enabled[i]}" = "enabled" ]]; then
> +
> +			logtext "add redirect in ${chain} on ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]} port ${port[i]} ";
> +			${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j RETURN;
> +			${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j REDIRECT;
> +		fi
> +		if [[ ! -z "${BLUE_DEV}" &&  "${iface[i]}" = "blue" && "${enabled[i]}" = "enabled" ]]; then
> +			logtext "add redirect in ${chain} on ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port ${port[i]} ";
> +			${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j RETURN;
> +			${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j REDIRECT;
> +		fi
> +		if [[ ! -z "${ORANGE_DEV}" &&  "${iface[i]}" = "orange" && "${enabled[i]}" = "enabled" ]]; then
> +			logtext "add redirect in ${chain} on ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port ${port[i]} ";
> +			${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j RETURN;
> +			${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j REDIRECT;
> +		fi
> +	done
> +	unset array redirects i index iface protocol port targetip enabled;
> +	return 0;
> +}
> +
> +activate_redirections() {
> +
> +	if ! create_chain; then return 1; fi;
> +	
> +	# Force DNS REDIRECTs on GREEN (udp, tcp, 53)
> +	if [[ "${REDIR_DNS_GREEN}" == "on" &&  "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p udp -m udp --dport domain -j REDIRECT;
> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p tcp -m tcp --dport domain -j REDIRECT;
> +	fi
> +
> +	# Force DNS REDIRECTs on BLUE (udp, tcp, 53)
> +	if [[ "${REDIR_DNS_BLUE}" == "on" &&  "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp -m udp --dport domain -j REDIRECT
> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p tcp -m tcp --dport domain -j REDIRECT
> +	fi
> +
> +	# Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
> +	if [[ "${REDIR_DNS_ORANGE}" == "on" &&  "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p udp -m udp --dport domain -j REDIRECT
> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p tcp -m tcp --dport domain -j REDIRECT
> +	fi
> +
> +	# Force NTP REDIRECTs on GREEN (udp, 123)
> +	if [[ "${REDIR_NTP_GREEN}" == "on" &&  "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p udp -m udp --dport ntp -j REDIRECT
> +	fi
> +
> +	# Force NTP REDIRECTs on BLUE (udp, 123)
> +	if [[ "${REDIR_NTP_BLUE}" == "on" &&  "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp -m udp --dport ntp -j REDIRECT
> +	fi
> +
> +	# Force NTP REDIRECTs on ORANGE (udp, 123)
> +	if [[ "${REDIR_NTP_ORANGE}" == "on" &&  "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p udp -m udp --dport ntp -j REDIRECT
> +	fi
> +
> +	if ! activate_custom_redirections; then return 1; fi;
> +
> +	return 0;
> +}
> +
> +case "${1}" in
> +	start)
> +		boot_mesg "Loading port redirections..."
> +		activate_redirections;
> +		evaluate_retval;
> +		;;
> +
> +	stop)	
> +		boot_mesg "Removing port redirections..."
> +		remove_chain;
> +		evaluate_retval;
> +		;;
> +
> +	restart)
> +		${0} stop
> +		${0} start
> +		;;
> +
> +	*)
> +		echo "Usage: ${0} {start|stop|restart}"
> +		exit 1
> +		;;
> +esac
> +
> +# End $rc_base/init.d/portredir
> diff --git a/src/misc-progs/Makefile b/src/misc-progs/Makefile
> index 7c3ef7529..850f8fdcc 100644
> --- a/src/misc-progs/Makefile
> +++ b/src/misc-progs/Makefile
> @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
> 	wirelessctrl getipstat qosctrl \
> 	redctrl syslogdctrl extrahdctrl sambactrl \
> 	smartctrl clamavctrl addonctrl pakfire mpfirectrl wlanapctrl \
> -	setaliases urlfilterctrl updxlratorctrl fireinfoctrl rebuildroutes \
> +	setaliases urlfilterctrl updxlratorctrl fireinfoctrl rebuildroutes portredirctrl \
> 	getconntracktable wirelessclient torctrl ddnsctrl unboundctrl \
> 	captivectrl
> 
> diff --git a/src/misc-progs/portredirctrl.c b/src/misc-progs/portredirctrl.c
> new file mode 100644
> index 000000000..7897d711c
> --- /dev/null
> +++ b/src/misc-progs/portredirctrl.c
> @@ -0,0 +1,47 @@
> +/* This file is part of the IPFire Firewall.
> + *
> + * This program is distributed under the terms of the GNU General Public
> + * Licence.  See the file COPYING for details.
> + *
> + */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <sys/types.h>
> +#include <fcntl.h>
> +#include "setuid.h"
> +
> +int main(int argc, char *argv[]) {
> +	if (!(initsetuid()))
> +		exit(1);
> +
> +	// Check what command is asked
> +        if (argc < 2) {
> +                fprintf(stderr, "\nNo argument given.\n\nportredirctrl (start|stop|restart|enable|disable)\n\n");
> +                exit(1);
> +        }
> +
> +        if (strcmp(argv[1], "start") == 0) {
> +                safe_system("/etc/rc.d/init.d/portredir start");
> +        } else if (strcmp(argv[1], "stop") == 0) {
> +                safe_system("/etc/rc.d/init.d/portredir stop");
> +        } else if (strcmp(argv[1], "restart") == 0) {
> +                safe_system("/etc/rc.d/init.d/portredir restart");
> +	} else if (strcmp(argv[1], "enable") == 0) {
> +		safe_system("touch /var/ipfire/portredir/enable");
> +		safe_system("ln -snf /etc/rc.d/init.d/portredir /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
> +		safe_system("ln -snf /etc/rc.d/init.d/portredir /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
> +		safe_system("ln -snf /etc/rc.d/init.d/portredir /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
> +	} else if (strcmp(argv[1], "disable") == 0) {
> +		safe_system("/etc/rc.d/init.d/portredir stop");
> +		safe_system("unlink /var/ipfire/portredir/enable");
> +		safe_system("rm -rf /etc/rc.d/rc*.d/*portredir >/dev/null 2>&1");
> +        } else {
> +                fprintf(stderr, "\nBad argument given.\n\nportredirctrl (start|stop|restart|enable|disable)\n\n");
> +                exit(1);
> +        }
> +
> +	return 0;
> +}
> diff --git a/src/paks/portredir/install.sh b/src/paks/portredir/install.sh
> new file mode 100644
> index 000000000..9f69aeae2
> --- /dev/null
> +++ b/src/paks/portredir/install.sh
> @@ -0,0 +1,32 @@
> +#!/bin/bash
> +############################################################################
> +#                                                                          #
> +# This file is part of the IPFire Firewall.                                #
> +#                                                                          #
> +# IPFire is free software; you can redistribute it and/or modify           #
> +# it under the terms of the GNU General Public License as published by     #
> +# the Free Software Foundation; either version 2 of the License, or        #
> +# (at your option) any later version.                                      #
> +#                                                                          #
> +# IPFire is distributed in the hope that it will be useful,                #
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of           #
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
> +# GNU General Public License for more details.                             #
> +#                                                                          #
> +# You should have received a copy of the GNU General Public License        #
> +# along with IPFire; if not, write to the Free Software                    #
> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA #
> +#                                                                          #
> +# Copyright (C) 2021 IPFire-Team <info@ipfire.org>.                        #
> +#                                                                          #
> +############################################################################
> +#
> +. /opt/pakfire/lib/functions.sh
> +extract_files
> +restore_backup ${NAME}
> +
> +/usr/local/bin/update-lang-cache
> +
> +chown root:nobody /usr/local/bin/portredirctrl
> +chmod 4750 /usr/local/bin/portredirctrl
> +chmod u+s /usr/local/bin/portredirctrl
> diff --git a/src/paks/portredir/uninstall.sh b/src/paks/portredir/uninstall.sh
> new file mode 100644
> index 000000000..df9270125
> --- /dev/null
> +++ b/src/paks/portredir/uninstall.sh
> @@ -0,0 +1,28 @@
> +#!/bin/bash
> +############################################################################
> +#                                                                          #
> +# This file is part of the IPFire Firewall.                                #
> +#                                                                          #
> +# IPFire is free software; you can redistribute it and/or modify           #
> +# it under the terms of the GNU General Public License as published by     #
> +# the Free Software Foundation; either version 2 of the License, or        #
> +# (at your option) any later version.                                      #
> +#                                                                          #
> +# IPFire is distributed in the hope that it will be useful,                #
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of           #
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
> +# GNU General Public License for more details.                             #
> +#                                                                          #
> +# You should have received a copy of the GNU General Public License        #
> +# along with IPFire; if not, write to the Free Software                    #
> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA #
> +#                                                                          #
> +# Copyright (C) 2007 IPFire-Team <info@ipfire.org>.                        #
> +#                                                                          #
> +############################################################################
> +#
> +. /opt/pakfire/lib/functions.sh
> +make_backup ${NAME}
> +remove_files
> +
> +/usr/local/bin/update-lang-cache
> diff --git a/src/paks/portredir/update.sh b/src/paks/portredir/update.sh
> new file mode 100644
> index 000000000..89c40d0d7
> --- /dev/null
> +++ b/src/paks/portredir/update.sh
> @@ -0,0 +1,26 @@
> +#!/bin/bash
> +############################################################################
> +#                                                                          #
> +# This file is part of the IPFire Firewall.                                #
> +#                                                                          #
> +# IPFire is free software; you can redistribute it and/or modify           #
> +# it under the terms of the GNU General Public License as published by     #
> +# the Free Software Foundation; either version 2 of the License, or        #
> +# (at your option) any later version.                                      #
> +#                                                                          #
> +# IPFire is distributed in the hope that it will be useful,                #
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of           #
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
> +# GNU General Public License for more details.                             #
> +#                                                                          #
> +# You should have received a copy of the GNU General Public License        #
> +# along with IPFire; if not, write to the Free Software                    #
> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA #
> +#                                                                          #
> +# Copyright (C) 2007 IPFire-Team <info@ipfire.org>.                        #
> +#                                                                          #
> +############################################################################
> +#
> +. /opt/pakfire/lib/functions.sh
> +./uninstall.sh
> +./install.sh
> -- 
> 2.18.0
>
  
Jon Murphy June 28, 2021, 5:53 p.m. UTC | #2
Hi Michael!  Happy Monday!


> Why do we not extend the firewall UI probably by about 20 lines of code instead of adding many hundreds of lines?
> 
> Please can someone elaborate on this more?


Doing a DNS redirect, via the WegBUI, has been an issue since 2015.  I found this quote in the old forum:

"Having investigated a bit more I have concluded that it's not currently possible to create such rules through the WUI.

There are a number of obstacles:
1. It is not allowed to create a rule where source IP and destination nat IP is on the same subnetwork (e.g. GREEN), WUI error message: "Source and destination IP addresses are from the same subnet."

2. WUI will not allow you to create a rule without a destination (the filtered packet must adhere to a destination, not only a port) and the destination MUST be an IP address of one of the IPFire interfaces, which limits whats possible a great deal." 


And I found this from 2016:
https://bugzilla.ipfire.org/show_bug.cgi?id=11168 <https://bugzilla.ipfire.org/show_bug.cgi?id=11168>

So I am guessing that no one has been able to determine a way to extend the WebGUI.  

I am curious - Who created the https://ipfire:444/cgi-bin/firewall.cgi <https://ipfire:444/cgi-bin/firewall.cgi> page?  And could they help?


Jon


> On Jun 28, 2021, at 11:04 AM, Michael Tremer <michael.tremer@ipfire.org> wrote:
> 
> Hello Matthias,
> 
>> On 27 Jun 2021, at 14:48, Matthias Fischer <matthias.fischer@ipfire.org> wrote:
>> 
>> From: Marcel Lorenz <marcel.lorenz@ipfire.org>
> 
> Thank you for sending this patch on Marcel’s behalf, but I would much more prefer if he would submit his patches on his own. I do not see why that isn’t possible.
> 
>> Please note:
>> This is a new addon written by Marcel Lorenz <marcel.lorenz@ipfire.org>.
>> 
>> It adds a new GUI to IPFire for DNS/NTP *and* user specific port redirections.
>> 
>> How its working:
>> It has exactly the same functionalities as "Forcing DNS/NTP..."  - and some more.
>> 
>> By setting switches, DNS/NTP requests are automatically
>> redirected to the local IPFire DNS/NTP servers.
>> 
>> Additionally, the user can specify custom redirections.
>> 
>> These rules are added to a new chain in PREROUTING => PORT_REDIRECT.
>> 
>> To avoid problems with (e.g.) transparent 'squid' configurations,
>> redirection rules are added automatically before existing 'squid' rules.
> 
> This message does unfortunately not say why this add-on would be useful. I am emphasising this again and again that it is not very important how something is done specially. That should be commented in the code and other implementation details should also be documented there.
> 
> As I have stated on this functionality many times before, I do not see why this is necessary at all.
> 
> Why is this an add-on?
> 
> Why do we not extend the firewall UI probably by about 20 lines of code instead of adding many hundreds of lines?
> 
> Please can someone elaborate on this more?
> 
> -Michael
> 
>> Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
>> ---
>> config/portredir/EX-portredir.menu    |   6 +
>> config/portredir/lang/portredir.de.pl |  19 +
>> config/portredir/lang/portredir.en.pl |  19 +
>> config/portredir/portredir-backup     |   1 +
>> config/portredir/portredir.cgi        | 525 ++++++++++++++++++++++++++
>> config/rootfiles/common/misc-progs    |   1 +
>> config/rootfiles/packages/portredir   |  11 +
>> lfs/portredir                         |  85 +++++
>> make.sh                               |   1 +
>> src/initscripts/packages/portredir    | 191 ++++++++++
>> src/misc-progs/Makefile               |   2 +-
>> src/misc-progs/portredirctrl.c        |  47 +++
>> src/paks/portredir/install.sh         |  32 ++
>> src/paks/portredir/uninstall.sh       |  28 ++
>> src/paks/portredir/update.sh          |  26 ++
>> 15 files changed, 993 insertions(+), 1 deletion(-)
>> create mode 100644 config/portredir/EX-portredir.menu
>> create mode 100644 config/portredir/lang/portredir.de.pl
>> create mode 100644 config/portredir/lang/portredir.en.pl
>> create mode 100644 config/portredir/portredir-backup
>> create mode 100644 config/portredir/portredir.cgi
>> create mode 100644 config/rootfiles/packages/portredir
>> create mode 100644 lfs/portredir
>> create mode 100644 src/initscripts/packages/portredir
>> create mode 100644 src/misc-progs/portredirctrl.c
>> create mode 100644 src/paks/portredir/install.sh
>> create mode 100644 src/paks/portredir/uninstall.sh
>> create mode 100644 src/paks/portredir/update.sh
>> 
>> diff --git a/config/portredir/EX-portredir.menu b/config/portredir/EX-portredir.menu
>> new file mode 100644
>> index 000000000..8376e8053
>> --- /dev/null
>> +++ b/config/portredir/EX-portredir.menu
>> @@ -0,0 +1,6 @@
>> +    $subfirewall->{'95.portredir'} = {
>> +				'caption' => $Lang::tr{'portredir port redirections'},
>> +				'uri' => '/cgi-bin/portredir.cgi',
>> +				'title' => "$Lang::tr{'portredir port redirections'}",
>> +				'enabled' => 1
>> +				};
>> diff --git a/config/portredir/lang/portredir.de.pl b/config/portredir/lang/portredir.de.pl
>> new file mode 100644
>> index 000000000..b932d4a85
>> --- /dev/null
>> +++ b/config/portredir/lang/portredir.de.pl
>> @@ -0,0 +1,19 @@
>> +%tr = (
>> +%tr,
>> +'portredir enable addon' => 'Addon aktivieren',
>> +'portredir common settings' => 'Allgemeine Einstellungen',
>> +'portredir port redirections' => 'Portumleitungen',
>> +'portredir fw for interface' => 'Firewalloptionen für das Interface',
>> +'portredir enable user redirections' => 'Aktiviere benutzerdefinierte Portumleitungen',
>> +'portredir force local dns' => 'Erzwinge lokale DNS-Server',
>> +'portredir force local ntp' => 'Erzwinge lokale NTP-Server',
>> +'portredir custom redirections' => 'Benutzerdefinierte Portumleitungen',
>> +'portredir remove rule' => 'Entferne Regel',
>> +'portredir add rule' => 'Hinzufügen',
>> +'portredir no entries' => 'Keine Einträge vorhanden.',
>> +'portredir invalid address' => 'Ungültige Host-Addresse.',
>> +'portredir empty input' => 'Fehlende Angabe: Bitte geben Sie einen gültigen Host an.',
>> +'portredir save to activate' => 'Speichern, um Änderungen zu aktivieren',
>> +);
>> +
>> +#EOF
>> diff --git a/config/portredir/lang/portredir.en.pl b/config/portredir/lang/portredir.en.pl
>> new file mode 100644
>> index 000000000..f442f3eaa
>> --- /dev/null
>> +++ b/config/portredir/lang/portredir.en.pl
>> @@ -0,0 +1,19 @@
>> +%tr = (
>> +%tr,
>> +'portredir enable addon' => 'Enable addon',
>> +'portredir common settings' => 'Common settings',
>> +'portredir port redirections' => 'Port redirections',
>> +'portredir fw for interface' => 'Firewall options for interface',
>> +'portredir enable user redirections' => 'Enable user port redirections',
>> +'portredir force local dns' => 'Enforce local DNS servers',
>> +'portredir force local ntp' => 'Enforce local NTP servers',
>> +'portredir custom redirections' => 'Custom port redirections',
>> +'portredir remove rule' => 'Remove rule',
>> +'portredir add rule' => 'Add new',
>> +'portredir no entries' => 'No entries at the moment.',
>> +'portredir invalid address' => 'Invalid host address.',
>> +'portredir empty input' => 'Empty input: Please enter a valid host.',
>> +'portredir save to activate' => 'Save to activate changes',
>> +);
>> +
>> +#EOF
>> diff --git a/config/portredir/portredir-backup b/config/portredir/portredir-backup
>> new file mode 100644
>> index 000000000..bd2ada742
>> --- /dev/null
>> +++ b/config/portredir/portredir-backup
>> @@ -0,0 +1 @@
>> +/var/ipfire/portredir
>> diff --git a/config/portredir/portredir.cgi b/config/portredir/portredir.cgi
>> new file mode 100644
>> index 000000000..4913dda3f
>> --- /dev/null
>> +++ b/config/portredir/portredir.cgi
>> @@ -0,0 +1,525 @@
>> +#!/usr/bin/perl
>> +###############################################################################
>> +#                                                                             #
>> +# IPFire.org - A linux based firewall                                         #
>> +# Copyright (C) 2021  IPFire Team  <info@ipfire.org>                          #
>> +#                                                                             #
>> +# This program is free software: you can redistribute it and/or modify        #
>> +# it under the terms of the GNU General Public License as published by        #
>> +# the Free Software Foundation, either version 3 of the License, or           #
>> +# (at your option) any later version.                                         #
>> +#                                                                             #
>> +# This program is distributed in the hope that it will be useful,             #
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
>> +# GNU General Public License for more details.                                #
>> +#                                                                             #
>> +# You should have received a copy of the GNU General Public License           #
>> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
>> +#                                                                             #
>> +###############################################################################
>> +
>> +use strict;
>> +
>> +# enable only the following on debugging purpose
>> +use warnings;
>> +use CGI::Carp 'fatalsToBrowser';
>> +
>> +require '/var/ipfire/general-functions.pl';
>> +require "${General::swroot}/lang.pl";
>> +require "${General::swroot}/header.pl";
>> +
>> +# File declarations
>> +my $settingsfile = "${General::swroot}/portredir/settings";
>> +my $redirectsfile = "${General::swroot}/portredir/redirects";
>> +
>> +# Create empty settingsfiles if they does not exist yet
>> +unless (-e "$settingsfile") { system ("touch $settingsfile"); }
>> +unless (-e "$redirectsfile") { system ("touch $redirectsfile"); }
>> +
>> +# load ipfire settings
>> +our %netsettings = ();
>> +our %color = ();
>> +&General::readhash("${General::swroot}/ethernet/settings", \%netsettings);
>> +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color);
>> +
>> +my %settings=();
>> +my %portredirs=();
>> +my %checked=(); # Checkbox manipulations
>> +my $errormessage='';
>> +my %selected=();
>> +our %redirects=();
>> +
>> +$settings{'ACTION'} = '';
>> +$settings{'REDIR_ENABLE_ADDON'}="off";
>> +$settings{'REDIR_CUSTOM_GREEN'}="off";
>> +$settings{'REDIR_CUSTOM_BLUE'}="off";
>> +$settings{'REDIR_CUSTOM_ORANGE'}="off";
>> +$settings{'REDIR_DNS_GREEN'}="off";
>> +$settings{'REDIR_NTP_GREEN'}="off";
>> +$settings{'REDIR_DNS_BLUE'}="off";
>> +$settings{'REDIR_NTP_BLUE'}="off";
>> +$settings{'REDIR_DNS_ORANGE'}="off";
>> +$settings{'REDIR_NTP_ORANGE'}="off";
>> +
>> +&Header::showhttpheaders();
>> +
>> +# Get GUI values
>> +&Header::getcgihash(\%settings);
>> +
>> +# Save action
>> +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
>> +
>> +	# If custom rules enabled, deactivate default rules on interface
>> +	if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
>> +		$settings{'REDIR_DNS_GREEN'}="off";
>> +		$settings{'REDIR_NTP_GREEN'}="off";
>> +	}
>> +
>> +	if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
>> +		$settings{'REDIR_DNS_BLUE'}="off";
>> +		$settings{'REDIR_NTP_BLUE'}="off";
>> +	}
>> +
>> +	if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
>> +		$settings{'REDIR_DNS_ORANGE'}="off";
>> +		$settings{'REDIR_NTP_ORANGE'}="off";
>> +	}
>> +
>> +	&General::writehash($settingsfile, \%settings);
>> +
>> +	if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
>> +		system ('/usr/local/bin/portredirctrl restart >/dev/null 2>&1');
>> +		system ('/usr/local/bin/portredirctrl enable >/dev/null 2>&1');
>> +		&General::log('portredir addon: port redirections enabled');
>> +	}
>> +	if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
>> +		system ('/usr/local/bin/portredirctrl disable >/dev/null 2>&1');
>> +		system ('/usr/local/bin/portredirctrl stop >/dev/null 2>&1');
>> +		&General::log('portredir addon: port redirections disabled');
>> +	}
>> +
>> +# Add/edit an entry to the redirectsfile.
>> +
>> +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) || ($settings{'ACTION'} eq $Lang::tr{'update'})) {
>> +
>> +	# Check if any input has been performed.
>> +	if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
>> +
>> +		# Check if the given input is no valid IP-address, display an error message.
>> +		if (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
>> +			$errormessage = "$Lang::tr{'portredir invalid address'}";
>> +		}
>> +	} else {
>> +		$errormessage = "$Lang::tr{'portredir empty input'}";
>> +	}
>> +
>> +	# Go further if there was no error.
>> +	if ($errormessage eq '') {
>> +		my %redirects = ();
>> +		my $id;
>> +		my $status;
>> +
>> +		# Assign hash values.
>> +		my $new_entry_interface = $settings{'REDIR_ENTRY_INTERFACE'};
>> +		my $new_entry_protocol = $settings{'REDIR_ENTRY_PROTOCOL'};
>> +		my $new_entry_port = $settings{'REDIR_ENTRY_PORT'};
>> +		my $new_entry_address = $settings{'REDIR_ENTRY_ADDRESS'};
>> +		my $new_entry_remark = $settings{'REDIR_ENTRY_REMARK'};
>> +
>> +		# Read-in redirectsfile.
>> +		&General::readhasharray($redirectsfile, \%redirects);
>> +
>> +		# Check if we should edit an existing entry and got an ID.
>> +		if (($settings{'ACTION'} eq $Lang::tr{'update'}) && ($settings{'ID'})) {
>> +			# Assin the provided id.
>> +			$id = $settings{'ID'};
>> +
>> +			# Undef the given ID.
>> +			undef($settings{'ID'});
>> +
>> +			# Grab the configured status of the corresponding entry.
>> +			$status = $redirects{$id}[4];
>> +		} else {
>> +			# Each newly added entry automatically should be enabled.
>> +			$status = "enabled";
>> +
>> +			# Generate the ID for the new entry.
>> +			#
>> +			# Sort the keys by their ID and store them in an array.
>> +			my @keys = sort { $a <=> $b } keys %redirects;
>> +
>> +			# Reverse the key array.
>> +			my @reversed = reverse(@keys);
>> +
>> +			# Obtain the last used id.
>> +			my $last_id = @reversed[0];
>> +
>> +			# Increase the last id by one and use it as id for the new entry.
>> +			$id = ++$last_id;
>> +		}
>> +
>> +		# Add/Modify the entry to/in the redirects hash.
>> +		$redirects{$id} = ["$new_entry_interface", "$new_entry_protocol", "$new_entry_port", "$new_entry_address","$status", "$new_entry_remark"];
>> +
>> +		# Write the changed redirects hash to the redirects file.
>> +		&General::writehasharray($redirectsfile, \%redirects);
>> +	}
>> +
>> +# Toggle Enabled/Disabled for an existing entry on the redirects list.
>> +
>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable disable'}) {
>> +	my %redirects = ();
>> +
>> +	# Only go further, if an ID has been passed.
>> +	if ($settings{'ID'}) {
>> +		# Assign the given ID.
>> +		my $id = $settings{'ID'};
>> +
>> +		# Undef the given ID.
>> +		undef($settings{'ID'});
>> +
>> +		# Read-in ignoredfile.
>> +		&General::readhasharray($redirectsfile, \%redirects);
>> +
>> +		# Grab the configured status of the corresponding entry.
>> +		my $status = $redirects{$id}[4];
>> +
>> +		# Switch the status.
>> +		if ($status eq "disabled") {
>> +			$status = "enabled";
>> +		} else {
>> +			$status = "disabled";
>> +		}
>> +
>> +		# Modify the status of the existing entry.
>> +		$redirects{$id} = ["$redirects{$id}[0]", "$redirects{$id}[1]", "$redirects{$id}[2]", "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
>> +
>> +		# Write the changed ignored hash to the redirects file.
>> +		&General::writehasharray($redirectsfile, \%redirects);
>> +	}
>> +
>> +# Remove entry from redirects list.
>> +
>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
>> +	my %redirects = ();
>> +
>> +	# Read-in redirectsfile.
>> +	&General::readhasharray($redirectsfile, \%redirects);
>> +
>> +	# move data on key up
>> +	foreach my $key (sort keys %redirects) {
>> +		if ($key >= $settings{'ID'}) {
>> +			my $next = $key + 1;
>> +			if (exists $redirects{$next}) {
>> +				foreach my $i (0 .. $#{$redirects{$next}}) { $redirects{$key}[$i] = $redirects{$next}[$i]; }
>> +			}
>> +		}
>> +	}
>> +
>> +	my $last_key = (sort {$a <=> $b} keys %redirects)[-1];
>> +	delete $redirects{$last_key};
>> +
>> +	# Undef the given ID.
>> +	undef($settings{'ID'});
>> +
>> +	# Write the changed redirects hash to file.
>> +	&General::writehasharray($redirectsfile, \%redirects);
>> +}
>> +
>> +# Load settings from file
>> +&General::readhash($settingsfile, \%settings);
>> +&General::readhasharray($redirectsfile, \%redirects);
>> +
>> +# Call functions to generate whole page.
>> +&Header::openpage($Lang::tr{'portredir port redirections'}, 1, '');
>> +&Header::openbigbox('100%', 'left', '', $errormessage);
>> +
>> +if ($errormessage) {
>> +        &Header::openbox('100%', 'left', $Lang::tr{'warning messages'});
>> +        print "<font color='red'>$errormessage&nbsp;</font>";
>> +        &Header::closebox();
>> +}
>> +
>> +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
>> +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
>> +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_ADDON'}} = "checked='checked'";
>> +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
>> +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
>> +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_GREEN'}} = "checked='checked'";
>> +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
>> +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
>> +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BLUE'}} = "checked='checked'";
>> +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
>> +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
>> +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_ORANGE'}} = "checked='checked'";
>> +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
>> +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
>> +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}} = "checked='checked'";
>> +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
>> +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
>> +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}} = "checked='checked'";
>> +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
>> +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
>> +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}} = "checked='checked'";
>> +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
>> +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
>> +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}} = "checked='checked'";
>> +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
>> +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
>> +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE'}} = "checked='checked'";
>> +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
>> +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
>> +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE'}} = "checked='checked'";
>> +
>> +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTRY_INTERFACE'}} = 'selected';
>> +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY_PROTOCOL'}} = 'selected';
>> +
>> +&showMainBox();
>> +&showRedirectsBox();
>> +
>> +&Header::closebigbox();
>> +&Header::closepage();
>> +
>> +# Function to show main settings and options.
>> +sub showMainBox() {
>> +
>> +	&Header::openbox('100%', 'center', "$Lang::tr{'settings'}");
>> +	print "<form method='post' action='$ENV{'SCRIPT_NAME'}'>";
>> +
>> +print <<END;
>> +<table width='80%' cellspacing='0' border='0'>
>> +	<tr><td colspan='2' class='base' bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir common settings'}</b></td></tr
>> +	<tr>
>> +		<td width='25%' class='base'>$Lang::tr{'portredir enable addon'}:</td>
>> +		<td><input type='checkbox' name='REDIR_ENABLE_ADDON' $checked{'REDIR_ENABLE_ADDON'}{'on'} /></td>
>> +	</tr>
>> +	<tr><td colspan='2'></td></tr>
>> +	<tr><td colspan='2'>&nbsp;</td></tr>
>> +END
>> +
>> +	# create html table with header line 1
>> +	print "<table width='80%' cellspacing='0' border='0'><tr>";
>> +	print "<th class='base' width='40%' align='left'><b>$Lang::tr{'portredir fw for interface'}</th>";
>> +	if ($netsettings{'GREEN_DEV'})  {print "<th class='base' width='10%'><b><font color=green>$Lang::tr{'green'}</font></th>";
>> +		} else {		 print "<th class='base' width='10%'></font></th>"; }
>> +	if ($netsettings{'BLUE_DEV'})   {print "<th class='base' width='10%'><b><font color=blue>$Lang::tr{'blue'}</font></th>";
>> +		} else {		 print "<th class='base' width='10%'></font></th>"; }
>> +	if ($netsettings{'ORANGE_DEV'}) {print "<th class='base' width='10%'><b><font color=orange>$Lang::tr{'orange'}</font></th>";
>> +		} else {		 print "<th class='base' width='10%'></font></th>"; }
>> +
>> +	# the empty right row
>> +	print "<th class='base' width='30%'><td></td></th></tr>";
>> +
>> +	# line 2
>> +	print "<tr><td>$Lang::tr{'portredir force local dns'}</td>";
>> +	if ($netsettings{'GREEN_DEV'})  {print "<td class='base' align='center'><input type='checkbox' name='REDIR_DNS_GREEN' $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print "<td></td>";}
>> +	if ($netsettings{'BLUE_DEV'})   {print "<td class='base' align='center'><input type='checkbox' name='REDIR_DNS_BLUE' $checked{'REDIR_DNS_BLUE'}{'on'}></td>";} else { print "<td></td>";}
>> +	if ($netsettings{'ORANGE_DEV'}) {print "<td class='base' align='center'><input type='checkbox' name='REDIR_DNS_ORANGE' $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print "<td></td>";}
>> +
>> +	# line 3
>> +	print "</tr><tr><td>$Lang::tr{'portredir force local ntp'}</td>";
>> +	if ($netsettings{'GREEN_DEV'})  {print "<td class='base' align='center'><input type='checkbox' name='REDIR_NTP_GREEN' $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print "<td></td>";}
>> +	if ($netsettings{'BLUE_DEV'})   {print "<td class='base' align='center'><input type='checkbox' name='REDIR_NTP_BLUE' $checked{'REDIR_NTP_BLUE'}{'on'}></td>";} else { print "<td></td>";}
>> +	if ($netsettings{'ORANGE_DEV'}) {print "<td class='base' align='center'><input type='checkbox' name='REDIR_NTP_ORANGE' $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print "<td></td>";}
>> +
>> +	# line 4
>> +	print "</tr><tr><td>$Lang::tr{'portredir enable user redirections'}</td>";
>> +	if ($netsettings{'GREEN_DEV'})  {print "<td class='base' align='center'><input type='checkbox' name='REDIR_CUSTOM_GREEN' $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else { print "<td></td>";}
>> +	if ($netsettings{'BLUE_DEV'})   {print "<td class='base' align='center'><input type='checkbox' name='REDIR_CUSTOM_BLUE' $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print "<td></td>";}
>> +	if ($netsettings{'ORANGE_DEV'}) {print "<td class='base' align='center'><input type='checkbox' name='REDIR_CUSTOM_ORANGE' $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else { print "<td></td>";}
>> +
>> +	print <<END;
>> +	</tr></table>
>> +	<table width='80%' cellspacing='0' border='0'>
>> +		<tr><td colspan='2'>&nbsp;</td></tr>
>> +		<tr><td align='left'><b>$Lang::tr{'portredir save to activate'}</b></td><td width='5%' align='center'><input type='submit' name='ACTION' value='  $Lang::tr{'save'}  '></td></tr>
>> +	</table></form>
>> +END
>> +
>> +&Header::closebox();
>> +}
>> +
>> +# Function to show elements of the redirects file and allow to add or remove single members of it.
>> +sub showRedirectsBox() {
>> +        &Header::openbox('100%', 'center', "$Lang::tr{'portredir custom redirections'}");
>> +
>> +	print <<END;
>> +		<table width='80%' cellspacing='1' border='0'>
>> +			<tr>
>> +				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'interface'}</b></td>
>> +				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'protocol'}</b></td>
>> +				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'port'}</b></td>
>> +				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'ip address'}</b></td>
>> +				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'remark'}</b></td>
>> +				<td class='base' colspan='3' bgcolor='$color{'color20'}'></td>
>> +			</tr>
>> +END
>> +			# Check if some rules have been added to be redirects.
>> +			if (keys (%redirects)) {
>> +				my $col = "";
>> +
>> +				# List all entries of the hash.
>> +				foreach my $key (sort keys %redirects){
>> +
>> +					# Assign data array positions to some nice variable names.
>> +					my $interface = $redirects{$key}[0];
>> +					my $protocol = $redirects{$key}[1];
>> +					my $port  = $redirects{$key}[2];
>> +					my $address = $redirects{$key}[3];
>> +					my $status  = $redirects{$key}[4];
>> +					my $remark  = $redirects{$key}[5];
>> +
>> +					# Check if the key (id) number is even or not.
>> +					if ($settings{'ID'} eq $key) {
>> +						$col="bgcolor='${Header::colouryellow}'";
>> +					} elsif ($key % 2) {
>> +						$col="bgcolor='$color{'color22'}'";
>> +					} else {
>> +						$col="bgcolor='$color{'color20'}'";
>> +					}
>> +
>> +					# Choose icon for the checkbox.
>> +					my $gif;
>> +					my $gdesc;
>> +
>> +					# Check if the status is enabled and select the correct image and description.
>> +					if ($status eq 'enabled' ) {
>> +						$gif = 'on.gif';
>> +						$gdesc = $Lang::tr{'click to disable'};
>> +					} else {
>> +						$gif = 'off.gif';
>> +						$gdesc = $Lang::tr{'click to enable'};
>> +					}
>> +
>> +					print <<END;
>> +					<tr>
>> +						<td width='15%' class='base' align='center' $col><b><font color=$interface>$Lang::tr{$interface}</font></b></td>
>> +						<td width='10%' class='base' align='center' $col>$protocol</td>
>> +						<td width='10%' class='base' align='center' $col>$port</td>
>> +						<td width='15%' class='base' align='center' $col>&nbsp;$address</td>
>> +						<td width='40%' class='base' align='center' $col>&nbsp;$remark</td>
>> +						<td align='center' $col>
>> +							<form method='post' action='$ENV{'SCRIPT_NAME'}'>
>> +								<input type='hidden' name='ACTION' value='$Lang::tr{'toggle enable disable'}' />
>> +								<input type='image' name='$Lang::tr{'toggle enable disable'}' src='/images/$gif' alt='$gdesc' title='$gdesc' />
>> +								<input type='hidden' name='ID' value='$key' />
>> +							</form>
>> +						</td>
>> +						<td align='center' $col>
>> +							<form method='post' action='$ENV{'SCRIPT_NAME'}'>
>> +								<input type='hidden' name='ACTION' value='$Lang::tr{'edit'}' />
>> +								<input type='image' name='$Lang::tr{'edit'}' src='/images/edit.gif' alt='$Lang::tr{'edit'}' title='$Lang::tr{'edit'}' />
>> +								<input type='hidden' name='ID' value='$key' />
>> +							</form>
>> +						</td>
>> +						<td align='center' $col>
>> +							<form method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
>> +								<input type='image' name='$Lang::tr{'remove'}' src='/images/delete.gif' title='$Lang::tr{'remove'}' alt='$Lang::tr{'remove'}'>
>> +								<input type='hidden' name='ID' value='$key'>
>> +								<input type='hidden' name='ACTION' value='$Lang::tr{'remove'}'>
>> +							</form>
>> +						</td>
>> +					</tr>
>> +END
>> +				}
>> +			} else {
>> +				# Print notice that currently no ports are redirected.
>> +				print "<tr>\n";
>> +				print "<td class='base' colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
>> +				print "</tr>\n";
>> +			}
>> +
>> +		print "</table>\n";
>> +
>> +	# Section to add new elements or edit existing ones.
>> +	print <<END;
>> +	<br>
>> +	<hr>
>> +	<br>
>> +	<div align='center'>
>> +		<table width='100%' cellspacing='0' border='0'>
>> +END
>> +
>> +	# Assign correct headline and button text.
>> +	my $buttontext;
>> +	my $entry_interface;
>> +	my $entry_protocol;
>> +	my $entry_port;
>> +	my $entry_address;
>> +	my $entry_remark;
>> +
>> +	# Check if an ID (key) has been given, in this case an existing entry should be edited.
>> +	if ($settings{'ID'} ne '') {
>> +		$buttontext = $Lang::tr{'update'};
>> +		print "<tr><td class='boldbase' colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
>> +
>> +		# Grab address and remark for the given key.
>> +		$entry_interface = $redirects{$settings{'ID'}}[0];
>> +		$entry_protocol = $redirects{$settings{'ID'}}[1];
>> +		$entry_port = $redirects{$settings{'ID'}}[2];
>> +		$entry_address = $redirects{$settings{'ID'}}[3];
>> +		$entry_remark = $redirects{$settings{'ID'}}[5];
>> +
>> +	} else {
>> +		$buttontext = $Lang::tr{'add'};
>> +		print "<tr><td class='boldbase' colspan='11'><b>$Lang::tr{'dnsforward add a new entry'}</b></td></tr>\n";
>> +		print "<tr><td>&nbsp</td></tr>\n";
>> +	}
>> +
>> +	print <<END;
>> +			<tr>
>> +				<td class='base' width='1%'  bgcolor='$color{'color22'}'></td>
>> +				<td class='base' width='15%' bgcolor='$color{'color22'}' align='left'><b>$Lang::tr{'interface'}</b></td>
>> +				<td class='base' width='10%' bgcolor='$color{'color22'}' align='left'><b>$Lang::tr{'protocol'}</b></td>
>> +				<td class='base' width='10%' bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
>> +				<td class='base' width='13%' bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'ip address'}</b></td>
>> +				<td class='base' width='30%' bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
>> +				<td class='base' width='15%' bgcolor='$color{'color22'}'></td>
>> +				<td class='base' width='1%'  bgcolor='$color{'color22'}'></td>
>> +			</tr>
>> +
>> +			<form method='post' action='$ENV{'SCRIPT_NAME'}'>
>> +			<input type='hidden' name='ID' value='$settings{'ID'}'>
>> +			<tr>
>> +				<td class='base'></td>
>> +				<td><select style='width:90px;' id='interface' name='REDIR_ENTRY_INTERFACE'>
>> +END
>> +				if ($netsettings{'GREEN_DEV'}) {
>> +					if ($entry_interface eq "green") {
>> +						print "<option value='green' selected='selected' {'green'}>$Lang::tr{'green'}</option>";
>> +					} else { print "<option value='green' {'green'}>$Lang::tr{'green'}</option>";}
>> +				}
>> +				if ($netsettings{'BLUE_DEV'}) {
>> +					if ($entry_interface eq "blue") { 
>> +						print "<option value='blue' selected='selected' {'blue'}>$Lang::tr{'blue'}</option>";
>> +					} else { print "<option value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
>> +				}
>> +				if ($netsettings{'ORANGE_DEV'}) {
>> +					if ($entry_interface eq "orange") { 
>> +						print "<option value='orange' selected='selected' {'orange'}>$Lang::tr{'orange'}</option>";
>> +					} else { print "<option value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
>> +				}
>> +
>> +			print "</select><td><select style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
>> +			if ((!$entry_protocol) || ($entry_protocol eq "tcp")) {
>> +				print "<option selected='selected' id='protocol' value='tcp' {'tcp'}>tcp</option>";
>> +				print "<option value='udp' {'udp'}>udp</option>";
>> +			} elsif ($entry_protocol eq "udp") {
>> +				print "<option value='tcp' {'tcp'}>tcp</option>";
>> +				print "<option selected='selected' value='udp' {'udp'}>udp</option>";
>> +			}
>> +	print <<END;
>> +				</select></td>
>> +				<td><input type='text' name='REDIR_ENTRY_PORT'    value='$entry_port'    size='4'></td>
>> +				<td><input type='text' name='REDIR_ENTRY_ADDRESS' value='$entry_address' size='14'></td>
>> +				<td><input type='text' name='REDIR_ENTRY_REMARK'  value='$entry_remark'  size='35'></td>
>> +				<td width='10%' align='center'><input type='submit' name='ACTION' value='  $buttontext  '></td>
>> +				<td class='base'></td>
>> +			</tr>
>> +			</form>
>> +		</table>
>> +	</div>
>> +END
>> +	&Header::closebox();
>> +}
>> diff --git a/config/rootfiles/common/misc-progs b/config/rootfiles/common/misc-progs
>> index d6594b3f8..fbad2af8b 100644
>> --- a/config/rootfiles/common/misc-progs
>> +++ b/config/rootfiles/common/misc-progs
>> @@ -17,6 +17,7 @@ usr/local/bin/logwatch
>> #usr/local/bin/mpfirectrl
>> usr/local/bin/openvpnctrl
>> usr/local/bin/pakfire
>> +#usr/local/bin/portredirctrl
>> usr/local/bin/qosctrl
>> usr/local/bin/rebuildhosts
>> usr/local/bin/rebuildroutes
>> diff --git a/config/rootfiles/packages/portredir b/config/rootfiles/packages/portredir
>> new file mode 100644
>> index 000000000..4b4ba8366
>> --- /dev/null
>> +++ b/config/rootfiles/packages/portredir
>> @@ -0,0 +1,11 @@
>> +etc/rc.d/init.d/portredir
>> +etc/rc.d/rc0.d/K77portredir
>> +etc/rc.d/rc3.d/S23portredir
>> +etc/rc.d/rc6.d/K77portredir
>> +srv/web/ipfire/cgi-bin/portredir.cgi
>> +usr/local/bin/portredirctrl
>> +var/ipfire/addon-lang/portredir.de.pl
>> +var/ipfire/addon-lang/portredir.en.pl
>> +var/ipfire/backup/addons/includes/portredir
>> +var/ipfire/menu.d/EX-portredir.menu
>> +var/ipfire/portredir
>> diff --git a/lfs/portredir b/lfs/portredir
>> new file mode 100644
>> index 000000000..a4911f71f
>> --- /dev/null
>> +++ b/lfs/portredir
>> @@ -0,0 +1,85 @@
>> +###############################################################################
>> +#                                                                             #
>> +# IPFire.org - A linux based firewall                                         #
>> +# Copyright (C) 2007-2021  IPFire Team  <info@ipfire.org>                     #
>> +#                                                                             #
>> +# This program is free software: you can redistribute it and/or modify        #
>> +# it under the terms of the GNU General Public License as published by        #
>> +# the Free Software Foundation, either version 3 of the License, or           #
>> +# (at your option) any later version.                                         #
>> +#                                                                             #
>> +# This program is distributed in the hope that it will be useful,             #
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
>> +# GNU General Public License for more details.                                #
>> +#                                                                             #
>> +# You should have received a copy of the GNU General Public License           #
>> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
>> +#                                                                             #
>> +###############################################################################
>> +
>> +###############################################################################
>> +# Definitions
>> +###############################################################################
>> +
>> +include Config
>> +
>> +VER        = 1.0
>> +
>> +THISAPP    = portredir-$(VER)
>> +DIR_APP    = $(DIR_SRC)/$(THISAPP)
>> +TARGET     = $(DIR_INFO)/$(THISAPP)
>> +PROG       = portredir
>> +PAK_VER    = 1
>> +
>> +###############################################################################
>> +# Top-level Rules
>> +###############################################################################
>> +
>> +install : $(TARGET)
>> +
>> +check :
>> +
>> +download :
>> +
>> +md5 :
>> +
>> +dist: 
>> +	@$(PAK)
>> +
>> +###############################################################################
>> +# Installation Details
>> +###############################################################################
>> +
>> +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
>> +	@$(PREBUILD)
>> +	@rm -rf $(DIR_APP) && cd $(DIR_SRC)
>> +
>> +	#install cgi 
>> +	install -v -m 755 $(DIR_CONF)/portredir/portredir.cgi /srv/web/ipfire/cgi-bin/
>> +
>> +	#create configuration dir 
>> +	-mkdir -pv /var/ipfire/portredir/
>> +	chown -R nobody:nobody /var/ipfire/portredir/
>> +
>> +	# Install include file for backup
>> +	install -v -m 644 $(DIR_CONF)/portredir/portredir-backup /var/ipfire/backup/addons/includes/portredir
>> +
>> +	# Install menu file
>> +	install -v -m 644 $(DIR_CONF)/portredir/EX-portredir.menu /var/ipfire/menu.d/
>> +	chown nobody:nobody /var/ipfire/menu.d/EX-portredir.menu
>> +
>> +	# Install addon-specific language-files
>> +	install -v -m 644 $(DIR_CONF)/portredir/lang/portredir.*.pl /var/ipfire/addon-lang/
>> +
>> +	#install initscripts
>> +	$(call INSTALL_INITSCRIPT,portredir)
>> +
>> +	# Create symlinks for runlevel interaction.
>> +	ln -svf /etc/rc.d/init.d/portredir /etc/rc.d/rc3.d/S23portredir
>> +	ln -svf /etc/rc.d/init.d/portredir /etc/rc.d/rc0.d/K77portredir
>> +	ln -svf /etc/rc.d/init.d/portredir /etc/rc.d/rc6.d/K77portredir
>> +
>> +	@rm -rf $(DIR_APP)
>> +	@$(POSTBUILD)
>> +
>> diff --git a/make.sh b/make.sh
>> index fc03ebcd5..ab9fe881a 100755
>> --- a/make.sh
>> +++ b/make.sh
>> @@ -1623,6 +1623,7 @@ buildipfire() {
>>  lfsmake2 socat
>>  lfsmake2 libcdada
>>  lfsmake2 pmacct
>> +  lfsmake2 portredir
>> }
>> 
>> buildinstaller() {
>> diff --git a/src/initscripts/packages/portredir b/src/initscripts/packages/portredir
>> new file mode 100644
>> index 000000000..cc57fb9cc
>> --- /dev/null
>> +++ b/src/initscripts/packages/portredir
>> @@ -0,0 +1,191 @@
>> +#!/bin/sh
>> +########################################################################
>> +# Begin $rc_base/init.d/portredir
>> +#
>> +# Description : portredir init script for DNS/NTP and custom 
>> +#		port redirection rules
>> +#
>> +########################################################################
>> +
>> +. /etc/sysconfig/rc
>> +. ${rc_functions}
>> +
>> +IPT="/sbin/iptables";
>> +parent_chain="PREROUTING";
>> +chain="PORT_REDIRECT";
>> +
>> +confdir="/var/ipfire/portredir";
>> +settingsfile="${confdir}/settings";
>> +redirectsfile="${confdir}/redirects";
>> +SYSLOG="NO";
>> +VERBOSE="NO";
>> +
>> +eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings);
>> +eval $(/usr/local/bin/readhash ${settingsfile});
>> +
>> +logtext() {
>> +	if [ "${SYSLOG}" = "YES" ]; then logger -t "portredir" ${1}; fi;
>> +	if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
>> +
>> +create_chain() {
>> +
>> +	local line=$(${IPT} -t nat -L ${parent_chain} --line-numbers |grep "SQUID" |awk '{printf($1)}');
>> +
>> +	if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z "${REDIR_ENABLE_ADDON}" ]]; then
>> +		logtext "addon not enabled in web interface...";
>> +		echo "Portredir addon not enabled in web interface...";
>> +		exit 0;
>> +	fi;
>> +
>> +	if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep ${chain})" ]; then
>> +		${IPT} -t nat -N ${chain};
>> +
>> +		if [ ! -z "${line}" ]; then
>> +			logtext "create chain ${chain} and link in ${parent_chain} at position ${line}...";
>> +			${IPT} -t nat -I ${parent_chain} ${line} -j ${chain};
>> +		else
>> +			logtext "create chain ${chain} and link in ${parent_chain} at last position...";
>> +			${IPT} -t nat -A ${parent_chain} -j ${chain};
>> +		fi
>> +	else
>> +		return 1;
>> +	fi;
>> +	return 0;
>> +}
>> +
>> +remove_chain() {
>> +	if [ ! -z "$(${IPT} -t nat -L ${parent_chain} |grep ${chain})" ]; then
>> +		logtext "remove chain ${chain} and link in ${parent_chain} from system...";
>> +		${IPT} -t nat -D "${parent_chain}" -j ${chain};
>> +		${IPT} -t nat -F ${chain};
>> +		${IPT} -t nat -X ${chain};
>> +	else
>> +		return 1;
>> +	fi;
>> +	return 0;
>> +}
>> +
>> +activate_custom_redirections() {
>> +	
>> +	local array=();
>> +	local redirects=();
>> +	local i;
>> +	index=();
>> +	iface=();
>> +	protocol=();
>> +	port=();
>> +	targetip=();
>> +	enabled=();
>> +
>> +	IFS=$'\n' read -d '' -ra redirects < ${redirectsfile};
>> +
>> +	for i in "${!redirects[@]}"
>> +	do
>> +		IFS=$',' read -ra array <<< ${redirects[i]};
>> +		index[i]=${array[0]};
>> +		iface[i]=${array[1]};
>> +		protocol[i]=${array[2]};
>> +		port[i]=${array[3]};
>> +		targetip[i]=${array[4]};
>> +		enabled[i]=${array[5]};
>> +	done
>> +
>> +	for i in "${!index[@]}"
>> +	do
>> +		if [[ ! -z "${GREEN_DEV}" &&  "${iface[i]}" = "green" && "${enabled[i]}" = "enabled" ]]; then
>> +
>> +			logtext "add redirect in ${chain} on ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]} port ${port[i]} ";
>> +			${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j RETURN;
>> +			${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j REDIRECT;
>> +		fi
>> +		if [[ ! -z "${BLUE_DEV}" &&  "${iface[i]}" = "blue" && "${enabled[i]}" = "enabled" ]]; then
>> +			logtext "add redirect in ${chain} on ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port ${port[i]} ";
>> +			${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j RETURN;
>> +			${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j REDIRECT;
>> +		fi
>> +		if [[ ! -z "${ORANGE_DEV}" &&  "${iface[i]}" = "orange" && "${enabled[i]}" = "enabled" ]]; then
>> +			logtext "add redirect in ${chain} on ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port ${port[i]} ";
>> +			${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j RETURN;
>> +			${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j REDIRECT;
>> +		fi
>> +	done
>> +	unset array redirects i index iface protocol port targetip enabled;
>> +	return 0;
>> +}
>> +
>> +activate_redirections() {
>> +
>> +	if ! create_chain; then return 1; fi;
>> +	
>> +	# Force DNS REDIRECTs on GREEN (udp, tcp, 53)
>> +	if [[ "${REDIR_DNS_GREEN}" == "on" &&  "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
>> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p udp -m udp --dport domain -j REDIRECT;
>> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
>> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p tcp -m tcp --dport domain -j REDIRECT;
>> +	fi
>> +
>> +	# Force DNS REDIRECTs on BLUE (udp, tcp, 53)
>> +	if [[ "${REDIR_DNS_BLUE}" == "on" &&  "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp -m udp --dport domain -j REDIRECT
>> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p tcp -m tcp --dport domain -j REDIRECT
>> +	fi
>> +
>> +	# Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
>> +	if [[ "${REDIR_DNS_ORANGE}" == "on" &&  "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p udp -m udp --dport domain -j REDIRECT
>> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p tcp -m tcp --dport domain -j REDIRECT
>> +	fi
>> +
>> +	# Force NTP REDIRECTs on GREEN (udp, 123)
>> +	if [[ "${REDIR_NTP_GREEN}" == "on" &&  "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p udp -m udp --dport ntp -j REDIRECT
>> +	fi
>> +
>> +	# Force NTP REDIRECTs on BLUE (udp, 123)
>> +	if [[ "${REDIR_NTP_BLUE}" == "on" &&  "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp -m udp --dport ntp -j REDIRECT
>> +	fi
>> +
>> +	# Force NTP REDIRECTs on ORANGE (udp, 123)
>> +	if [[ "${REDIR_NTP_ORANGE}" == "on" &&  "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p udp -m udp --dport ntp -j REDIRECT
>> +	fi
>> +
>> +	if ! activate_custom_redirections; then return 1; fi;
>> +
>> +	return 0;
>> +}
>> +
>> +case "${1}" in
>> +	start)
>> +		boot_mesg "Loading port redirections..."
>> +		activate_redirections;
>> +		evaluate_retval;
>> +		;;
>> +
>> +	stop)	
>> +		boot_mesg "Removing port redirections..."
>> +		remove_chain;
>> +		evaluate_retval;
>> +		;;
>> +
>> +	restart)
>> +		${0} stop
>> +		${0} start
>> +		;;
>> +
>> +	*)
>> +		echo "Usage: ${0} {start|stop|restart}"
>> +		exit 1
>> +		;;
>> +esac
>> +
>> +# End $rc_base/init.d/portredir
>> diff --git a/src/misc-progs/Makefile b/src/misc-progs/Makefile
>> index 7c3ef7529..850f8fdcc 100644
>> --- a/src/misc-progs/Makefile
>> +++ b/src/misc-progs/Makefile
>> @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
>> 	wirelessctrl getipstat qosctrl \
>> 	redctrl syslogdctrl extrahdctrl sambactrl \
>> 	smartctrl clamavctrl addonctrl pakfire mpfirectrl wlanapctrl \
>> -	setaliases urlfilterctrl updxlratorctrl fireinfoctrl rebuildroutes \
>> +	setaliases urlfilterctrl updxlratorctrl fireinfoctrl rebuildroutes portredirctrl \
>> 	getconntracktable wirelessclient torctrl ddnsctrl unboundctrl \
>> 	captivectrl
>> 
>> diff --git a/src/misc-progs/portredirctrl.c b/src/misc-progs/portredirctrl.c
>> new file mode 100644
>> index 000000000..7897d711c
>> --- /dev/null
>> +++ b/src/misc-progs/portredirctrl.c
>> @@ -0,0 +1,47 @@
>> +/* This file is part of the IPFire Firewall.
>> + *
>> + * This program is distributed under the terms of the GNU General Public
>> + * Licence.  See the file COPYING for details.
>> + *
>> + */
>> +
>> +#include <stdlib.h>
>> +#include <stdio.h>
>> +#include <string.h>
>> +#include <unistd.h>
>> +#include <sys/types.h>
>> +#include <fcntl.h>
>> +#include "setuid.h"
>> +
>> +int main(int argc, char *argv[]) {
>> +	if (!(initsetuid()))
>> +		exit(1);
>> +
>> +	// Check what command is asked
>> +        if (argc < 2) {
>> +                fprintf(stderr, "\nNo argument given.\n\nportredirctrl (start|stop|restart|enable|disable)\n\n");
>> +                exit(1);
>> +        }
>> +
>> +        if (strcmp(argv[1], "start") == 0) {
>> +                safe_system("/etc/rc.d/init.d/portredir start");
>> +        } else if (strcmp(argv[1], "stop") == 0) {
>> +                safe_system("/etc/rc.d/init.d/portredir stop");
>> +        } else if (strcmp(argv[1], "restart") == 0) {
>> +                safe_system("/etc/rc.d/init.d/portredir restart");
>> +	} else if (strcmp(argv[1], "enable") == 0) {
>> +		safe_system("touch /var/ipfire/portredir/enable");
>> +		safe_system("ln -snf /etc/rc.d/init.d/portredir /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
>> +		safe_system("ln -snf /etc/rc.d/init.d/portredir /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
>> +		safe_system("ln -snf /etc/rc.d/init.d/portredir /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
>> +	} else if (strcmp(argv[1], "disable") == 0) {
>> +		safe_system("/etc/rc.d/init.d/portredir stop");
>> +		safe_system("unlink /var/ipfire/portredir/enable");
>> +		safe_system("rm -rf /etc/rc.d/rc*.d/*portredir >/dev/null 2>&1");
>> +        } else {
>> +                fprintf(stderr, "\nBad argument given.\n\nportredirctrl (start|stop|restart|enable|disable)\n\n");
>> +                exit(1);
>> +        }
>> +
>> +	return 0;
>> +}
>> diff --git a/src/paks/portredir/install.sh b/src/paks/portredir/install.sh
>> new file mode 100644
>> index 000000000..9f69aeae2
>> --- /dev/null
>> +++ b/src/paks/portredir/install.sh
>> @@ -0,0 +1,32 @@
>> +#!/bin/bash
>> +############################################################################
>> +#                                                                          #
>> +# This file is part of the IPFire Firewall.                                #
>> +#                                                                          #
>> +# IPFire is free software; you can redistribute it and/or modify           #
>> +# it under the terms of the GNU General Public License as published by     #
>> +# the Free Software Foundation; either version 2 of the License, or        #
>> +# (at your option) any later version.                                      #
>> +#                                                                          #
>> +# IPFire is distributed in the hope that it will be useful,                #
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of           #
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
>> +# GNU General Public License for more details.                             #
>> +#                                                                          #
>> +# You should have received a copy of the GNU General Public License        #
>> +# along with IPFire; if not, write to the Free Software                    #
>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA #
>> +#                                                                          #
>> +# Copyright (C) 2021 IPFire-Team <info@ipfire.org>.                        #
>> +#                                                                          #
>> +############################################################################
>> +#
>> +. /opt/pakfire/lib/functions.sh
>> +extract_files
>> +restore_backup ${NAME}
>> +
>> +/usr/local/bin/update-lang-cache
>> +
>> +chown root:nobody /usr/local/bin/portredirctrl
>> +chmod 4750 /usr/local/bin/portredirctrl
>> +chmod u+s /usr/local/bin/portredirctrl
>> diff --git a/src/paks/portredir/uninstall.sh b/src/paks/portredir/uninstall.sh
>> new file mode 100644
>> index 000000000..df9270125
>> --- /dev/null
>> +++ b/src/paks/portredir/uninstall.sh
>> @@ -0,0 +1,28 @@
>> +#!/bin/bash
>> +############################################################################
>> +#                                                                          #
>> +# This file is part of the IPFire Firewall.                                #
>> +#                                                                          #
>> +# IPFire is free software; you can redistribute it and/or modify           #
>> +# it under the terms of the GNU General Public License as published by     #
>> +# the Free Software Foundation; either version 2 of the License, or        #
>> +# (at your option) any later version.                                      #
>> +#                                                                          #
>> +# IPFire is distributed in the hope that it will be useful,                #
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of           #
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
>> +# GNU General Public License for more details.                             #
>> +#                                                                          #
>> +# You should have received a copy of the GNU General Public License        #
>> +# along with IPFire; if not, write to the Free Software                    #
>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA #
>> +#                                                                          #
>> +# Copyright (C) 2007 IPFire-Team <info@ipfire.org>.                        #
>> +#                                                                          #
>> +############################################################################
>> +#
>> +. /opt/pakfire/lib/functions.sh
>> +make_backup ${NAME}
>> +remove_files
>> +
>> +/usr/local/bin/update-lang-cache
>> diff --git a/src/paks/portredir/update.sh b/src/paks/portredir/update.sh
>> new file mode 100644
>> index 000000000..89c40d0d7
>> --- /dev/null
>> +++ b/src/paks/portredir/update.sh
>> @@ -0,0 +1,26 @@
>> +#!/bin/bash
>> +############################################################################
>> +#                                                                          #
>> +# This file is part of the IPFire Firewall.                                #
>> +#                                                                          #
>> +# IPFire is free software; you can redistribute it and/or modify           #
>> +# it under the terms of the GNU General Public License as published by     #
>> +# the Free Software Foundation; either version 2 of the License, or        #
>> +# (at your option) any later version.                                      #
>> +#                                                                          #
>> +# IPFire is distributed in the hope that it will be useful,                #
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of           #
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
>> +# GNU General Public License for more details.                             #
>> +#                                                                          #
>> +# You should have received a copy of the GNU General Public License        #
>> +# along with IPFire; if not, write to the Free Software                    #
>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA #
>> +#                                                                          #
>> +# Copyright (C) 2007 IPFire-Team <info@ipfire.org>.                        #
>> +#                                                                          #
>> +############################################################################
>> +#
>> +. /opt/pakfire/lib/functions.sh
>> +./uninstall.sh
>> +./install.sh
>> -- 
>> 2.18.0
>> 
>
  
Michael Tremer June 28, 2021, 5:56 p.m. UTC | #3
Hello,

> On 28 Jun 2021, at 18:53, Jon Murphy <jcmurphy26@gmail.com> wrote:
> 
> Hi Michael!  Happy Monday!
> 
> 
>> Why do we not extend the firewall UI probably by about 20 lines of code instead of adding many hundreds of lines?
>> 
>> Please can someone elaborate on this more?
> 
> Doing a DNS redirect, via the WegBUI, has been an issue since 2015.  I found this quote in the old forum:
> 
> "Having investigated a bit more I have concluded that it's not currently possible to create such rules through the WUI.
> 
> There are a number of obstacles:
> 1. It is not allowed to create a rule where source IP and destination nat IP is on the same subnetwork (e.g. GREEN), WUI error message: "Source and destination IP addresses are from the same subnet."
> 
> 2. WUI will not allow you to create a rule without a destination (the filtered packet must adhere to a destination, not only a port) and the destination MUST be an IP address of one of the IPFire interfaces, which limits whats possible a great deal." 

And these cannot be changed?

> And I found this from 2016:
> https://bugzilla.ipfire.org/show_bug.cgi?id=11168
> 
> So I am guessing that no one has been able to determine a way to extend the WebGUI.  

Has anyone tried? I do not see any obvious reasons why this should not be possible.

> I am curious - Who created the https://ipfire:444/cgi-bin/firewall.cgi page?  And could they help?

-Michael

> Jon
> 
> 
>> On Jun 28, 2021, at 11:04 AM, Michael Tremer <michael.tremer@ipfire.org> wrote:
>> 
>> Hello Matthias,
>> 
>>> On 27 Jun 2021, at 14:48, Matthias Fischer <matthias.fischer@ipfire.org> wrote:
>>> 
>>> From: Marcel Lorenz <marcel.lorenz@ipfire.org>
>> 
>> Thank you for sending this patch on Marcel’s behalf, but I would much more prefer if he would submit his patches on his own. I do not see why that isn’t possible.
>> 
>>> Please note:
>>> This is a new addon written by Marcel Lorenz <marcel.lorenz@ipfire.org>.
>>> 
>>> It adds a new GUI to IPFire for DNS/NTP *and* user specific port redirections.
>>> 
>>> How its working:
>>> It has exactly the same functionalities as "Forcing DNS/NTP..."  - and some more.
>>> 
>>> By setting switches, DNS/NTP requests are automatically
>>> redirected to the local IPFire DNS/NTP servers.
>>> 
>>> Additionally, the user can specify custom redirections.
>>> 
>>> These rules are added to a new chain in PREROUTING => PORT_REDIRECT.
>>> 
>>> To avoid problems with (e.g.) transparent 'squid' configurations,
>>> redirection rules are added automatically before existing 'squid' rules.
>> 
>> This message does unfortunately not say why this add-on would be useful. I am emphasising this again and again that it is not very important how something is done specially. That should be commented in the code and other implementation details should also be documented there.
>> 
>> As I have stated on this functionality many times before, I do not see why this is necessary at all.
>> 
>> Why is this an add-on?
>> 
>> Why do we not extend the firewall UI probably by about 20 lines of code instead of adding many hundreds of lines?
>> 
>> Please can someone elaborate on this more?
>> 
>> -Michael
>> 
>>> Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
>>> ---
>>> config/portredir/EX-portredir.menu    |   6 +
>>> config/portredir/lang/portredir.de.pl |  19 +
>>> config/portredir/lang/portredir.en.pl |  19 +
>>> config/portredir/portredir-backup     |   1 +
>>> config/portredir/portredir.cgi        | 525 ++++++++++++++++++++++++++
>>> config/rootfiles/common/misc-progs    |   1 +
>>> config/rootfiles/packages/portredir   |  11 +
>>> lfs/portredir                         |  85 +++++
>>> make.sh                               |   1 +
>>> src/initscripts/packages/portredir    | 191 ++++++++++
>>> src/misc-progs/Makefile               |   2 +-
>>> src/misc-progs/portredirctrl.c        |  47 +++
>>> src/paks/portredir/install.sh         |  32 ++
>>> src/paks/portredir/uninstall.sh       |  28 ++
>>> src/paks/portredir/update.sh          |  26 ++
>>> 15 files changed, 993 insertions(+), 1 deletion(-)
>>> create mode 100644 config/portredir/EX-portredir.menu
>>> create mode 100644 config/portredir/lang/portredir.de.pl
>>> create mode 100644 config/portredir/lang/portredir.en.pl
>>> create mode 100644 config/portredir/portredir-backup
>>> create mode 100644 config/portredir/portredir.cgi
>>> create mode 100644 config/rootfiles/packages/portredir
>>> create mode 100644 lfs/portredir
>>> create mode 100644 src/initscripts/packages/portredir
>>> create mode 100644 src/misc-progs/portredirctrl.c
>>> create mode 100644 src/paks/portredir/install.sh
>>> create mode 100644 src/paks/portredir/uninstall.sh
>>> create mode 100644 src/paks/portredir/update.sh
>>> 
>>> diff --git a/config/portredir/EX-portredir.menu b/config/portredir/EX-portredir.menu
>>> new file mode 100644
>>> index 000000000..8376e8053
>>> --- /dev/null
>>> +++ b/config/portredir/EX-portredir.menu
>>> @@ -0,0 +1,6 @@
>>> +    $subfirewall->{'95.portredir'} = {
>>> +				'caption' => $Lang::tr{'portredir port redirections'},
>>> +				'uri' => '/cgi-bin/portredir.cgi',
>>> +				'title' => "$Lang::tr{'portredir port redirections'}",
>>> +				'enabled' => 1
>>> +				};
>>> diff --git a/config/portredir/lang/portredir.de.pl b/config/portredir/lang/portredir.de.pl
>>> new file mode 100644
>>> index 000000000..b932d4a85
>>> --- /dev/null
>>> +++ b/config/portredir/lang/portredir.de.pl
>>> @@ -0,0 +1,19 @@
>>> +%tr = (
>>> +%tr,
>>> +'portredir enable addon' => 'Addon aktivieren',
>>> +'portredir common settings' => 'Allgemeine Einstellungen',
>>> +'portredir port redirections' => 'Portumleitungen',
>>> +'portredir fw for interface' => 'Firewalloptionen für das Interface',
>>> +'portredir enable user redirections' => 'Aktiviere benutzerdefinierte Portumleitungen',
>>> +'portredir force local dns' => 'Erzwinge lokale DNS-Server',
>>> +'portredir force local ntp' => 'Erzwinge lokale NTP-Server',
>>> +'portredir custom redirections' => 'Benutzerdefinierte Portumleitungen',
>>> +'portredir remove rule' => 'Entferne Regel',
>>> +'portredir add rule' => 'Hinzufügen',
>>> +'portredir no entries' => 'Keine Einträge vorhanden.',
>>> +'portredir invalid address' => 'Ungültige Host-Addresse.',
>>> +'portredir empty input' => 'Fehlende Angabe: Bitte geben Sie einen gültigen Host an.',
>>> +'portredir save to activate' => 'Speichern, um Änderungen zu aktivieren',
>>> +);
>>> +
>>> +#EOF
>>> diff --git a/config/portredir/lang/portredir.en.pl b/config/portredir/lang/portredir.en.pl
>>> new file mode 100644
>>> index 000000000..f442f3eaa
>>> --- /dev/null
>>> +++ b/config/portredir/lang/portredir.en.pl
>>> @@ -0,0 +1,19 @@
>>> +%tr = (
>>> +%tr,
>>> +'portredir enable addon' => 'Enable addon',
>>> +'portredir common settings' => 'Common settings',
>>> +'portredir port redirections' => 'Port redirections',
>>> +'portredir fw for interface' => 'Firewall options for interface',
>>> +'portredir enable user redirections' => 'Enable user port redirections',
>>> +'portredir force local dns' => 'Enforce local DNS servers',
>>> +'portredir force local ntp' => 'Enforce local NTP servers',
>>> +'portredir custom redirections' => 'Custom port redirections',
>>> +'portredir remove rule' => 'Remove rule',
>>> +'portredir add rule' => 'Add new',
>>> +'portredir no entries' => 'No entries at the moment.',
>>> +'portredir invalid address' => 'Invalid host address.',
>>> +'portredir empty input' => 'Empty input: Please enter a valid host.',
>>> +'portredir save to activate' => 'Save to activate changes',
>>> +);
>>> +
>>> +#EOF
>>> diff --git a/config/portredir/portredir-backup b/config/portredir/portredir-backup
>>> new file mode 100644
>>> index 000000000..bd2ada742
>>> --- /dev/null
>>> +++ b/config/portredir/portredir-backup
>>> @@ -0,0 +1 @@
>>> +/var/ipfire/portredir
>>> diff --git a/config/portredir/portredir.cgi b/config/portredir/portredir.cgi
>>> new file mode 100644
>>> index 000000000..4913dda3f
>>> --- /dev/null
>>> +++ b/config/portredir/portredir.cgi
>>> @@ -0,0 +1,525 @@
>>> +#!/usr/bin/perl
>>> +###############################################################################
>>> +#                                                                             #
>>> +# IPFire.org - A linux based firewall                                         #
>>> +# Copyright (C) 2021  IPFire Team  <info@ipfire.org>                          #
>>> +#                                                                             #
>>> +# This program is free software: you can redistribute it and/or modify        #
>>> +# it under the terms of the GNU General Public License as published by        #
>>> +# the Free Software Foundation, either version 3 of the License, or           #
>>> +# (at your option) any later version.                                         #
>>> +#                                                                             #
>>> +# This program is distributed in the hope that it will be useful,             #
>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
>>> +# GNU General Public License for more details.                                #
>>> +#                                                                             #
>>> +# You should have received a copy of the GNU General Public License           #
>>> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
>>> +#                                                                             #
>>> +###############################################################################
>>> +
>>> +use strict;
>>> +
>>> +# enable only the following on debugging purpose
>>> +use warnings;
>>> +use CGI::Carp 'fatalsToBrowser';
>>> +
>>> +require '/var/ipfire/general-functions.pl';
>>> +require "${General::swroot}/lang.pl";
>>> +require "${General::swroot}/header.pl";
>>> +
>>> +# File declarations
>>> +my $settingsfile = "${General::swroot}/portredir/settings";
>>> +my $redirectsfile = "${General::swroot}/portredir/redirects";
>>> +
>>> +# Create empty settingsfiles if they does not exist yet
>>> +unless (-e "$settingsfile") { system ("touch $settingsfile"); }
>>> +unless (-e "$redirectsfile") { system ("touch $redirectsfile"); }
>>> +
>>> +# load ipfire settings
>>> +our %netsettings = ();
>>> +our %color = ();
>>> +&General::readhash("${General::swroot}/ethernet/settings", \%netsettings);
>>> +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color);
>>> +
>>> +my %settings=();
>>> +my %portredirs=();
>>> +my %checked=(); # Checkbox manipulations
>>> +my $errormessage='';
>>> +my %selected=();
>>> +our %redirects=();
>>> +
>>> +$settings{'ACTION'} = '';
>>> +$settings{'REDIR_ENABLE_ADDON'}="off";
>>> +$settings{'REDIR_CUSTOM_GREEN'}="off";
>>> +$settings{'REDIR_CUSTOM_BLUE'}="off";
>>> +$settings{'REDIR_CUSTOM_ORANGE'}="off";
>>> +$settings{'REDIR_DNS_GREEN'}="off";
>>> +$settings{'REDIR_NTP_GREEN'}="off";
>>> +$settings{'REDIR_DNS_BLUE'}="off";
>>> +$settings{'REDIR_NTP_BLUE'}="off";
>>> +$settings{'REDIR_DNS_ORANGE'}="off";
>>> +$settings{'REDIR_NTP_ORANGE'}="off";
>>> +
>>> +&Header::showhttpheaders();
>>> +
>>> +# Get GUI values
>>> +&Header::getcgihash(\%settings);
>>> +
>>> +# Save action
>>> +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
>>> +
>>> +	# If custom rules enabled, deactivate default rules on interface
>>> +	if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
>>> +		$settings{'REDIR_DNS_GREEN'}="off";
>>> +		$settings{'REDIR_NTP_GREEN'}="off";
>>> +	}
>>> +
>>> +	if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
>>> +		$settings{'REDIR_DNS_BLUE'}="off";
>>> +		$settings{'REDIR_NTP_BLUE'}="off";
>>> +	}
>>> +
>>> +	if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
>>> +		$settings{'REDIR_DNS_ORANGE'}="off";
>>> +		$settings{'REDIR_NTP_ORANGE'}="off";
>>> +	}
>>> +
>>> +	&General::writehash($settingsfile, \%settings);
>>> +
>>> +	if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
>>> +		system ('/usr/local/bin/portredirctrl restart >/dev/null 2>&1');
>>> +		system ('/usr/local/bin/portredirctrl enable >/dev/null 2>&1');
>>> +		&General::log('portredir addon: port redirections enabled');
>>> +	}
>>> +	if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
>>> +		system ('/usr/local/bin/portredirctrl disable >/dev/null 2>&1');
>>> +		system ('/usr/local/bin/portredirctrl stop >/dev/null 2>&1');
>>> +		&General::log('portredir addon: port redirections disabled');
>>> +	}
>>> +
>>> +# Add/edit an entry to the redirectsfile.
>>> +
>>> +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) || ($settings{'ACTION'} eq $Lang::tr{'update'})) {
>>> +
>>> +	# Check if any input has been performed.
>>> +	if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
>>> +
>>> +		# Check if the given input is no valid IP-address, display an error message.
>>> +		if (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
>>> +			$errormessage = "$Lang::tr{'portredir invalid address'}";
>>> +		}
>>> +	} else {
>>> +		$errormessage = "$Lang::tr{'portredir empty input'}";
>>> +	}
>>> +
>>> +	# Go further if there was no error.
>>> +	if ($errormessage eq '') {
>>> +		my %redirects = ();
>>> +		my $id;
>>> +		my $status;
>>> +
>>> +		# Assign hash values.
>>> +		my $new_entry_interface = $settings{'REDIR_ENTRY_INTERFACE'};
>>> +		my $new_entry_protocol = $settings{'REDIR_ENTRY_PROTOCOL'};
>>> +		my $new_entry_port = $settings{'REDIR_ENTRY_PORT'};
>>> +		my $new_entry_address = $settings{'REDIR_ENTRY_ADDRESS'};
>>> +		my $new_entry_remark = $settings{'REDIR_ENTRY_REMARK'};
>>> +
>>> +		# Read-in redirectsfile.
>>> +		&General::readhasharray($redirectsfile, \%redirects);
>>> +
>>> +		# Check if we should edit an existing entry and got an ID.
>>> +		if (($settings{'ACTION'} eq $Lang::tr{'update'}) && ($settings{'ID'})) {
>>> +			# Assin the provided id.
>>> +			$id = $settings{'ID'};
>>> +
>>> +			# Undef the given ID.
>>> +			undef($settings{'ID'});
>>> +
>>> +			# Grab the configured status of the corresponding entry.
>>> +			$status = $redirects{$id}[4];
>>> +		} else {
>>> +			# Each newly added entry automatically should be enabled.
>>> +			$status = "enabled";
>>> +
>>> +			# Generate the ID for the new entry.
>>> +			#
>>> +			# Sort the keys by their ID and store them in an array.
>>> +			my @keys = sort { $a <=> $b } keys %redirects;
>>> +
>>> +			# Reverse the key array.
>>> +			my @reversed = reverse(@keys);
>>> +
>>> +			# Obtain the last used id.
>>> +			my $last_id = @reversed[0];
>>> +
>>> +			# Increase the last id by one and use it as id for the new entry.
>>> +			$id = ++$last_id;
>>> +		}
>>> +
>>> +		# Add/Modify the entry to/in the redirects hash.
>>> +		$redirects{$id} = ["$new_entry_interface", "$new_entry_protocol", "$new_entry_port", "$new_entry_address","$status", "$new_entry_remark"];
>>> +
>>> +		# Write the changed redirects hash to the redirects file.
>>> +		&General::writehasharray($redirectsfile, \%redirects);
>>> +	}
>>> +
>>> +# Toggle Enabled/Disabled for an existing entry on the redirects list.
>>> +
>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable disable'}) {
>>> +	my %redirects = ();
>>> +
>>> +	# Only go further, if an ID has been passed.
>>> +	if ($settings{'ID'}) {
>>> +		# Assign the given ID.
>>> +		my $id = $settings{'ID'};
>>> +
>>> +		# Undef the given ID.
>>> +		undef($settings{'ID'});
>>> +
>>> +		# Read-in ignoredfile.
>>> +		&General::readhasharray($redirectsfile, \%redirects);
>>> +
>>> +		# Grab the configured status of the corresponding entry.
>>> +		my $status = $redirects{$id}[4];
>>> +
>>> +		# Switch the status.
>>> +		if ($status eq "disabled") {
>>> +			$status = "enabled";
>>> +		} else {
>>> +			$status = "disabled";
>>> +		}
>>> +
>>> +		# Modify the status of the existing entry.
>>> +		$redirects{$id} = ["$redirects{$id}[0]", "$redirects{$id}[1]", "$redirects{$id}[2]", "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
>>> +
>>> +		# Write the changed ignored hash to the redirects file.
>>> +		&General::writehasharray($redirectsfile, \%redirects);
>>> +	}
>>> +
>>> +# Remove entry from redirects list.
>>> +
>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
>>> +	my %redirects = ();
>>> +
>>> +	# Read-in redirectsfile.
>>> +	&General::readhasharray($redirectsfile, \%redirects);
>>> +
>>> +	# move data on key up
>>> +	foreach my $key (sort keys %redirects) {
>>> +		if ($key >= $settings{'ID'}) {
>>> +			my $next = $key + 1;
>>> +			if (exists $redirects{$next}) {
>>> +				foreach my $i (0 .. $#{$redirects{$next}}) { $redirects{$key}[$i] = $redirects{$next}[$i]; }
>>> +			}
>>> +		}
>>> +	}
>>> +
>>> +	my $last_key = (sort {$a <=> $b} keys %redirects)[-1];
>>> +	delete $redirects{$last_key};
>>> +
>>> +	# Undef the given ID.
>>> +	undef($settings{'ID'});
>>> +
>>> +	# Write the changed redirects hash to file.
>>> +	&General::writehasharray($redirectsfile, \%redirects);
>>> +}
>>> +
>>> +# Load settings from file
>>> +&General::readhash($settingsfile, \%settings);
>>> +&General::readhasharray($redirectsfile, \%redirects);
>>> +
>>> +# Call functions to generate whole page.
>>> +&Header::openpage($Lang::tr{'portredir port redirections'}, 1, '');
>>> +&Header::openbigbox('100%', 'left', '', $errormessage);
>>> +
>>> +if ($errormessage) {
>>> +        &Header::openbox('100%', 'left', $Lang::tr{'warning messages'});
>>> +        print "<font color='red'>$errormessage&nbsp;</font>";
>>> +        &Header::closebox();
>>> +}
>>> +
>>> +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
>>> +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
>>> +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_ADDON'}} = "checked='checked'";
>>> +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
>>> +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
>>> +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_GREEN'}} = "checked='checked'";
>>> +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
>>> +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
>>> +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BLUE'}} = "checked='checked'";
>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
>>> +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_ORANGE'}} = "checked='checked'";
>>> +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
>>> +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
>>> +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}} = "checked='checked'";
>>> +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
>>> +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
>>> +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}} = "checked='checked'";
>>> +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
>>> +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
>>> +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}} = "checked='checked'";
>>> +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
>>> +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
>>> +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}} = "checked='checked'";
>>> +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
>>> +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
>>> +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE'}} = "checked='checked'";
>>> +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
>>> +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
>>> +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE'}} = "checked='checked'";
>>> +
>>> +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTRY_INTERFACE'}} = 'selected';
>>> +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY_PROTOCOL'}} = 'selected';
>>> +
>>> +&showMainBox();
>>> +&showRedirectsBox();
>>> +
>>> +&Header::closebigbox();
>>> +&Header::closepage();
>>> +
>>> +# Function to show main settings and options.
>>> +sub showMainBox() {
>>> +
>>> +	&Header::openbox('100%', 'center', "$Lang::tr{'settings'}");
>>> +	print "<form method='post' action='$ENV{'SCRIPT_NAME'}'>";
>>> +
>>> +print <<END;
>>> +<table width='80%' cellspacing='0' border='0'>
>>> +	<tr><td colspan='2' class='base' bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir common settings'}</b></td></tr
>>> +	<tr>
>>> +		<td width='25%' class='base'>$Lang::tr{'portredir enable addon'}:</td>
>>> +		<td><input type='checkbox' name='REDIR_ENABLE_ADDON' $checked{'REDIR_ENABLE_ADDON'}{'on'} /></td>
>>> +	</tr>
>>> +	<tr><td colspan='2'></td></tr>
>>> +	<tr><td colspan='2'>&nbsp;</td></tr>
>>> +END
>>> +
>>> +	# create html table with header line 1
>>> +	print "<table width='80%' cellspacing='0' border='0'><tr>";
>>> +	print "<th class='base' width='40%' align='left'><b>$Lang::tr{'portredir fw for interface'}</th>";
>>> +	if ($netsettings{'GREEN_DEV'})  {print "<th class='base' width='10%'><b><font color=green>$Lang::tr{'green'}</font></th>";
>>> +		} else {		 print "<th class='base' width='10%'></font></th>"; }
>>> +	if ($netsettings{'BLUE_DEV'})   {print "<th class='base' width='10%'><b><font color=blue>$Lang::tr{'blue'}</font></th>";
>>> +		} else {		 print "<th class='base' width='10%'></font></th>"; }
>>> +	if ($netsettings{'ORANGE_DEV'}) {print "<th class='base' width='10%'><b><font color=orange>$Lang::tr{'orange'}</font></th>";
>>> +		} else {		 print "<th class='base' width='10%'></font></th>"; }
>>> +
>>> +	# the empty right row
>>> +	print "<th class='base' width='30%'><td></td></th></tr>";
>>> +
>>> +	# line 2
>>> +	print "<tr><td>$Lang::tr{'portredir force local dns'}</td>";
>>> +	if ($netsettings{'GREEN_DEV'})  {print "<td class='base' align='center'><input type='checkbox' name='REDIR_DNS_GREEN' $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print "<td></td>";}
>>> +	if ($netsettings{'BLUE_DEV'})   {print "<td class='base' align='center'><input type='checkbox' name='REDIR_DNS_BLUE' $checked{'REDIR_DNS_BLUE'}{'on'}></td>";} else { print "<td></td>";}
>>> +	if ($netsettings{'ORANGE_DEV'}) {print "<td class='base' align='center'><input type='checkbox' name='REDIR_DNS_ORANGE' $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print "<td></td>";}
>>> +
>>> +	# line 3
>>> +	print "</tr><tr><td>$Lang::tr{'portredir force local ntp'}</td>";
>>> +	if ($netsettings{'GREEN_DEV'})  {print "<td class='base' align='center'><input type='checkbox' name='REDIR_NTP_GREEN' $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print "<td></td>";}
>>> +	if ($netsettings{'BLUE_DEV'})   {print "<td class='base' align='center'><input type='checkbox' name='REDIR_NTP_BLUE' $checked{'REDIR_NTP_BLUE'}{'on'}></td>";} else { print "<td></td>";}
>>> +	if ($netsettings{'ORANGE_DEV'}) {print "<td class='base' align='center'><input type='checkbox' name='REDIR_NTP_ORANGE' $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print "<td></td>";}
>>> +
>>> +	# line 4
>>> +	print "</tr><tr><td>$Lang::tr{'portredir enable user redirections'}</td>";
>>> +	if ($netsettings{'GREEN_DEV'})  {print "<td class='base' align='center'><input type='checkbox' name='REDIR_CUSTOM_GREEN' $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else { print "<td></td>";}
>>> +	if ($netsettings{'BLUE_DEV'})   {print "<td class='base' align='center'><input type='checkbox' name='REDIR_CUSTOM_BLUE' $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print "<td></td>";}
>>> +	if ($netsettings{'ORANGE_DEV'}) {print "<td class='base' align='center'><input type='checkbox' name='REDIR_CUSTOM_ORANGE' $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else { print "<td></td>";}
>>> +
>>> +	print <<END;
>>> +	</tr></table>
>>> +	<table width='80%' cellspacing='0' border='0'>
>>> +		<tr><td colspan='2'>&nbsp;</td></tr>
>>> +		<tr><td align='left'><b>$Lang::tr{'portredir save to activate'}</b></td><td width='5%' align='center'><input type='submit' name='ACTION' value='  $Lang::tr{'save'}  '></td></tr>
>>> +	</table></form>
>>> +END
>>> +
>>> +&Header::closebox();
>>> +}
>>> +
>>> +# Function to show elements of the redirects file and allow to add or remove single members of it.
>>> +sub showRedirectsBox() {
>>> +        &Header::openbox('100%', 'center', "$Lang::tr{'portredir custom redirections'}");
>>> +
>>> +	print <<END;
>>> +		<table width='80%' cellspacing='1' border='0'>
>>> +			<tr>
>>> +				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'interface'}</b></td>
>>> +				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'protocol'}</b></td>
>>> +				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'port'}</b></td>
>>> +				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'ip address'}</b></td>
>>> +				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'remark'}</b></td>
>>> +				<td class='base' colspan='3' bgcolor='$color{'color20'}'></td>
>>> +			</tr>
>>> +END
>>> +			# Check if some rules have been added to be redirects.
>>> +			if (keys (%redirects)) {
>>> +				my $col = "";
>>> +
>>> +				# List all entries of the hash.
>>> +				foreach my $key (sort keys %redirects){
>>> +
>>> +					# Assign data array positions to some nice variable names.
>>> +					my $interface = $redirects{$key}[0];
>>> +					my $protocol = $redirects{$key}[1];
>>> +					my $port  = $redirects{$key}[2];
>>> +					my $address = $redirects{$key}[3];
>>> +					my $status  = $redirects{$key}[4];
>>> +					my $remark  = $redirects{$key}[5];
>>> +
>>> +					# Check if the key (id) number is even or not.
>>> +					if ($settings{'ID'} eq $key) {
>>> +						$col="bgcolor='${Header::colouryellow}'";
>>> +					} elsif ($key % 2) {
>>> +						$col="bgcolor='$color{'color22'}'";
>>> +					} else {
>>> +						$col="bgcolor='$color{'color20'}'";
>>> +					}
>>> +
>>> +					# Choose icon for the checkbox.
>>> +					my $gif;
>>> +					my $gdesc;
>>> +
>>> +					# Check if the status is enabled and select the correct image and description.
>>> +					if ($status eq 'enabled' ) {
>>> +						$gif = 'on.gif';
>>> +						$gdesc = $Lang::tr{'click to disable'};
>>> +					} else {
>>> +						$gif = 'off.gif';
>>> +						$gdesc = $Lang::tr{'click to enable'};
>>> +					}
>>> +
>>> +					print <<END;
>>> +					<tr>
>>> +						<td width='15%' class='base' align='center' $col><b><font color=$interface>$Lang::tr{$interface}</font></b></td>
>>> +						<td width='10%' class='base' align='center' $col>$protocol</td>
>>> +						<td width='10%' class='base' align='center' $col>$port</td>
>>> +						<td width='15%' class='base' align='center' $col>&nbsp;$address</td>
>>> +						<td width='40%' class='base' align='center' $col>&nbsp;$remark</td>
>>> +						<td align='center' $col>
>>> +							<form method='post' action='$ENV{'SCRIPT_NAME'}'>
>>> +								<input type='hidden' name='ACTION' value='$Lang::tr{'toggle enable disable'}' />
>>> +								<input type='image' name='$Lang::tr{'toggle enable disable'}' src='/images/$gif' alt='$gdesc' title='$gdesc' />
>>> +								<input type='hidden' name='ID' value='$key' />
>>> +							</form>
>>> +						</td>
>>> +						<td align='center' $col>
>>> +							<form method='post' action='$ENV{'SCRIPT_NAME'}'>
>>> +								<input type='hidden' name='ACTION' value='$Lang::tr{'edit'}' />
>>> +								<input type='image' name='$Lang::tr{'edit'}' src='/images/edit.gif' alt='$Lang::tr{'edit'}' title='$Lang::tr{'edit'}' />
>>> +								<input type='hidden' name='ID' value='$key' />
>>> +							</form>
>>> +						</td>
>>> +						<td align='center' $col>
>>> +							<form method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
>>> +								<input type='image' name='$Lang::tr{'remove'}' src='/images/delete.gif' title='$Lang::tr{'remove'}' alt='$Lang::tr{'remove'}'>
>>> +								<input type='hidden' name='ID' value='$key'>
>>> +								<input type='hidden' name='ACTION' value='$Lang::tr{'remove'}'>
>>> +							</form>
>>> +						</td>
>>> +					</tr>
>>> +END
>>> +				}
>>> +			} else {
>>> +				# Print notice that currently no ports are redirected.
>>> +				print "<tr>\n";
>>> +				print "<td class='base' colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
>>> +				print "</tr>\n";
>>> +			}
>>> +
>>> +		print "</table>\n";
>>> +
>>> +	# Section to add new elements or edit existing ones.
>>> +	print <<END;
>>> +	<br>
>>> +	<hr>
>>> +	<br>
>>> +	<div align='center'>
>>> +		<table width='100%' cellspacing='0' border='0'>
>>> +END
>>> +
>>> +	# Assign correct headline and button text.
>>> +	my $buttontext;
>>> +	my $entry_interface;
>>> +	my $entry_protocol;
>>> +	my $entry_port;
>>> +	my $entry_address;
>>> +	my $entry_remark;
>>> +
>>> +	# Check if an ID (key) has been given, in this case an existing entry should be edited.
>>> +	if ($settings{'ID'} ne '') {
>>> +		$buttontext = $Lang::tr{'update'};
>>> +		print "<tr><td class='boldbase' colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
>>> +
>>> +		# Grab address and remark for the given key.
>>> +		$entry_interface = $redirects{$settings{'ID'}}[0];
>>> +		$entry_protocol = $redirects{$settings{'ID'}}[1];
>>> +		$entry_port = $redirects{$settings{'ID'}}[2];
>>> +		$entry_address = $redirects{$settings{'ID'}}[3];
>>> +		$entry_remark = $redirects{$settings{'ID'}}[5];
>>> +
>>> +	} else {
>>> +		$buttontext = $Lang::tr{'add'};
>>> +		print "<tr><td class='boldbase' colspan='11'><b>$Lang::tr{'dnsforward add a new entry'}</b></td></tr>\n";
>>> +		print "<tr><td>&nbsp</td></tr>\n";
>>> +	}
>>> +
>>> +	print <<END;
>>> +			<tr>
>>> +				<td class='base' width='1%'  bgcolor='$color{'color22'}'></td>
>>> +				<td class='base' width='15%' bgcolor='$color{'color22'}' align='left'><b>$Lang::tr{'interface'}</b></td>
>>> +				<td class='base' width='10%' bgcolor='$color{'color22'}' align='left'><b>$Lang::tr{'protocol'}</b></td>
>>> +				<td class='base' width='10%' bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
>>> +				<td class='base' width='13%' bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'ip address'}</b></td>
>>> +				<td class='base' width='30%' bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
>>> +				<td class='base' width='15%' bgcolor='$color{'color22'}'></td>
>>> +				<td class='base' width='1%'  bgcolor='$color{'color22'}'></td>
>>> +			</tr>
>>> +
>>> +			<form method='post' action='$ENV{'SCRIPT_NAME'}'>
>>> +			<input type='hidden' name='ID' value='$settings{'ID'}'>
>>> +			<tr>
>>> +				<td class='base'></td>
>>> +				<td><select style='width:90px;' id='interface' name='REDIR_ENTRY_INTERFACE'>
>>> +END
>>> +				if ($netsettings{'GREEN_DEV'}) {
>>> +					if ($entry_interface eq "green") {
>>> +						print "<option value='green' selected='selected' {'green'}>$Lang::tr{'green'}</option>";
>>> +					} else { print "<option value='green' {'green'}>$Lang::tr{'green'}</option>";}
>>> +				}
>>> +				if ($netsettings{'BLUE_DEV'}) {
>>> +					if ($entry_interface eq "blue") { 
>>> +						print "<option value='blue' selected='selected' {'blue'}>$Lang::tr{'blue'}</option>";
>>> +					} else { print "<option value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
>>> +				}
>>> +				if ($netsettings{'ORANGE_DEV'}) {
>>> +					if ($entry_interface eq "orange") { 
>>> +						print "<option value='orange' selected='selected' {'orange'}>$Lang::tr{'orange'}</option>";
>>> +					} else { print "<option value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
>>> +				}
>>> +
>>> +			print "</select><td><select style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
>>> +			if ((!$entry_protocol) || ($entry_protocol eq "tcp")) {
>>> +				print "<option selected='selected' id='protocol' value='tcp' {'tcp'}>tcp</option>";
>>> +				print "<option value='udp' {'udp'}>udp</option>";
>>> +			} elsif ($entry_protocol eq "udp") {
>>> +				print "<option value='tcp' {'tcp'}>tcp</option>";
>>> +				print "<option selected='selected' value='udp' {'udp'}>udp</option>";
>>> +			}
>>> +	print <<END;
>>> +				</select></td>
>>> +				<td><input type='text' name='REDIR_ENTRY_PORT'    value='$entry_port'    size='4'></td>
>>> +				<td><input type='text' name='REDIR_ENTRY_ADDRESS' value='$entry_address' size='14'></td>
>>> +				<td><input type='text' name='REDIR_ENTRY_REMARK'  value='$entry_remark'  size='35'></td>
>>> +				<td width='10%' align='center'><input type='submit' name='ACTION' value='  $buttontext  '></td>
>>> +				<td class='base'></td>
>>> +			</tr>
>>> +			</form>
>>> +		</table>
>>> +	</div>
>>> +END
>>> +	&Header::closebox();
>>> +}
>>> diff --git a/config/rootfiles/common/misc-progs b/config/rootfiles/common/misc-progs
>>> index d6594b3f8..fbad2af8b 100644
>>> --- a/config/rootfiles/common/misc-progs
>>> +++ b/config/rootfiles/common/misc-progs
>>> @@ -17,6 +17,7 @@ usr/local/bin/logwatch
>>> #usr/local/bin/mpfirectrl
>>> usr/local/bin/openvpnctrl
>>> usr/local/bin/pakfire
>>> +#usr/local/bin/portredirctrl
>>> usr/local/bin/qosctrl
>>> usr/local/bin/rebuildhosts
>>> usr/local/bin/rebuildroutes
>>> diff --git a/config/rootfiles/packages/portredir b/config/rootfiles/packages/portredir
>>> new file mode 100644
>>> index 000000000..4b4ba8366
>>> --- /dev/null
>>> +++ b/config/rootfiles/packages/portredir
>>> @@ -0,0 +1,11 @@
>>> +etc/rc.d/init.d/portredir
>>> +etc/rc.d/rc0.d/K77portredir
>>> +etc/rc.d/rc3.d/S23portredir
>>> +etc/rc.d/rc6.d/K77portredir
>>> +srv/web/ipfire/cgi-bin/portredir.cgi
>>> +usr/local/bin/portredirctrl
>>> +var/ipfire/addon-lang/portredir.de.pl
>>> +var/ipfire/addon-lang/portredir.en.pl
>>> +var/ipfire/backup/addons/includes/portredir
>>> +var/ipfire/menu.d/EX-portredir.menu
>>> +var/ipfire/portredir
>>> diff --git a/lfs/portredir b/lfs/portredir
>>> new file mode 100644
>>> index 000000000..a4911f71f
>>> --- /dev/null
>>> +++ b/lfs/portredir
>>> @@ -0,0 +1,85 @@
>>> +###############################################################################
>>> +#                                                                             #
>>> +# IPFire.org - A linux based firewall                                         #
>>> +# Copyright (C) 2007-2021  IPFire Team  <info@ipfire.org>                     #
>>> +#                                                                             #
>>> +# This program is free software: you can redistribute it and/or modify        #
>>> +# it under the terms of the GNU General Public License as published by        #
>>> +# the Free Software Foundation, either version 3 of the License, or           #
>>> +# (at your option) any later version.                                         #
>>> +#                                                                             #
>>> +# This program is distributed in the hope that it will be useful,             #
>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
>>> +# GNU General Public License for more details.                                #
>>> +#                                                                             #
>>> +# You should have received a copy of the GNU General Public License           #
>>> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
>>> +#                                                                             #
>>> +###############################################################################
>>> +
>>> +###############################################################################
>>> +# Definitions
>>> +###############################################################################
>>> +
>>> +include Config
>>> +
>>> +VER        = 1.0
>>> +
>>> +THISAPP    = portredir-$(VER)
>>> +DIR_APP    = $(DIR_SRC)/$(THISAPP)
>>> +TARGET     = $(DIR_INFO)/$(THISAPP)
>>> +PROG       = portredir
>>> +PAK_VER    = 1
>>> +
>>> +###############################################################################
>>> +# Top-level Rules
>>> +###############################################################################
>>> +
>>> +install : $(TARGET)
>>> +
>>> +check :
>>> +
>>> +download :
>>> +
>>> +md5 :
>>> +
>>> +dist: 
>>> +	@$(PAK)
>>> +
>>> +###############################################################################
>>> +# Installation Details
>>> +###############################################################################
>>> +
>>> +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
>>> +	@$(PREBUILD)
>>> +	@rm -rf $(DIR_APP) && cd $(DIR_SRC)
>>> +
>>> +	#install cgi 
>>> +	install -v -m 755 $(DIR_CONF)/portredir/portredir.cgi /srv/web/ipfire/cgi-bin/
>>> +
>>> +	#create configuration dir 
>>> +	-mkdir -pv /var/ipfire/portredir/
>>> +	chown -R nobody:nobody /var/ipfire/portredir/
>>> +
>>> +	# Install include file for backup
>>> +	install -v -m 644 $(DIR_CONF)/portredir/portredir-backup /var/ipfire/backup/addons/includes/portredir
>>> +
>>> +	# Install menu file
>>> +	install -v -m 644 $(DIR_CONF)/portredir/EX-portredir.menu /var/ipfire/menu.d/
>>> +	chown nobody:nobody /var/ipfire/menu.d/EX-portredir.menu
>>> +
>>> +	# Install addon-specific language-files
>>> +	install -v -m 644 $(DIR_CONF)/portredir/lang/portredir.*.pl /var/ipfire/addon-lang/
>>> +
>>> +	#install initscripts
>>> +	$(call INSTALL_INITSCRIPT,portredir)
>>> +
>>> +	# Create symlinks for runlevel interaction.
>>> +	ln -svf /etc/rc.d/init.d/portredir /etc/rc.d/rc3.d/S23portredir
>>> +	ln -svf /etc/rc.d/init.d/portredir /etc/rc.d/rc0.d/K77portredir
>>> +	ln -svf /etc/rc.d/init.d/portredir /etc/rc.d/rc6.d/K77portredir
>>> +
>>> +	@rm -rf $(DIR_APP)
>>> +	@$(POSTBUILD)
>>> +
>>> diff --git a/make.sh b/make.sh
>>> index fc03ebcd5..ab9fe881a 100755
>>> --- a/make.sh
>>> +++ b/make.sh
>>> @@ -1623,6 +1623,7 @@ buildipfire() {
>>>  lfsmake2 socat
>>>  lfsmake2 libcdada
>>>  lfsmake2 pmacct
>>> +  lfsmake2 portredir
>>> }
>>> 
>>> buildinstaller() {
>>> diff --git a/src/initscripts/packages/portredir b/src/initscripts/packages/portredir
>>> new file mode 100644
>>> index 000000000..cc57fb9cc
>>> --- /dev/null
>>> +++ b/src/initscripts/packages/portredir
>>> @@ -0,0 +1,191 @@
>>> +#!/bin/sh
>>> +########################################################################
>>> +# Begin $rc_base/init.d/portredir
>>> +#
>>> +# Description : portredir init script for DNS/NTP and custom 
>>> +#		port redirection rules
>>> +#
>>> +########################################################################
>>> +
>>> +. /etc/sysconfig/rc
>>> +. ${rc_functions}
>>> +
>>> +IPT="/sbin/iptables";
>>> +parent_chain="PREROUTING";
>>> +chain="PORT_REDIRECT";
>>> +
>>> +confdir="/var/ipfire/portredir";
>>> +settingsfile="${confdir}/settings";
>>> +redirectsfile="${confdir}/redirects";
>>> +SYSLOG="NO";
>>> +VERBOSE="NO";
>>> +
>>> +eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings);
>>> +eval $(/usr/local/bin/readhash ${settingsfile});
>>> +
>>> +logtext() {
>>> +	if [ "${SYSLOG}" = "YES" ]; then logger -t "portredir" ${1}; fi;
>>> +	if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
>>> +
>>> +create_chain() {
>>> +
>>> +	local line=$(${IPT} -t nat -L ${parent_chain} --line-numbers |grep "SQUID" |awk '{printf($1)}');
>>> +
>>> +	if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z "${REDIR_ENABLE_ADDON}" ]]; then
>>> +		logtext "addon not enabled in web interface...";
>>> +		echo "Portredir addon not enabled in web interface...";
>>> +		exit 0;
>>> +	fi;
>>> +
>>> +	if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep ${chain})" ]; then
>>> +		${IPT} -t nat -N ${chain};
>>> +
>>> +		if [ ! -z "${line}" ]; then
>>> +			logtext "create chain ${chain} and link in ${parent_chain} at position ${line}...";
>>> +			${IPT} -t nat -I ${parent_chain} ${line} -j ${chain};
>>> +		else
>>> +			logtext "create chain ${chain} and link in ${parent_chain} at last position...";
>>> +			${IPT} -t nat -A ${parent_chain} -j ${chain};
>>> +		fi
>>> +	else
>>> +		return 1;
>>> +	fi;
>>> +	return 0;
>>> +}
>>> +
>>> +remove_chain() {
>>> +	if [ ! -z "$(${IPT} -t nat -L ${parent_chain} |grep ${chain})" ]; then
>>> +		logtext "remove chain ${chain} and link in ${parent_chain} from system...";
>>> +		${IPT} -t nat -D "${parent_chain}" -j ${chain};
>>> +		${IPT} -t nat -F ${chain};
>>> +		${IPT} -t nat -X ${chain};
>>> +	else
>>> +		return 1;
>>> +	fi;
>>> +	return 0;
>>> +}
>>> +
>>> +activate_custom_redirections() {
>>> +	
>>> +	local array=();
>>> +	local redirects=();
>>> +	local i;
>>> +	index=();
>>> +	iface=();
>>> +	protocol=();
>>> +	port=();
>>> +	targetip=();
>>> +	enabled=();
>>> +
>>> +	IFS=$'\n' read -d '' -ra redirects < ${redirectsfile};
>>> +
>>> +	for i in "${!redirects[@]}"
>>> +	do
>>> +		IFS=$',' read -ra array <<< ${redirects[i]};
>>> +		index[i]=${array[0]};
>>> +		iface[i]=${array[1]};
>>> +		protocol[i]=${array[2]};
>>> +		port[i]=${array[3]};
>>> +		targetip[i]=${array[4]};
>>> +		enabled[i]=${array[5]};
>>> +	done
>>> +
>>> +	for i in "${!index[@]}"
>>> +	do
>>> +		if [[ ! -z "${GREEN_DEV}" &&  "${iface[i]}" = "green" && "${enabled[i]}" = "enabled" ]]; then
>>> +
>>> +			logtext "add redirect in ${chain} on ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]} port ${port[i]} ";
>>> +			${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j RETURN;
>>> +			${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j REDIRECT;
>>> +		fi
>>> +		if [[ ! -z "${BLUE_DEV}" &&  "${iface[i]}" = "blue" && "${enabled[i]}" = "enabled" ]]; then
>>> +			logtext "add redirect in ${chain} on ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port ${port[i]} ";
>>> +			${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j RETURN;
>>> +			${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j REDIRECT;
>>> +		fi
>>> +		if [[ ! -z "${ORANGE_DEV}" &&  "${iface[i]}" = "orange" && "${enabled[i]}" = "enabled" ]]; then
>>> +			logtext "add redirect in ${chain} on ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port ${port[i]} ";
>>> +			${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j RETURN;
>>> +			${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j REDIRECT;
>>> +		fi
>>> +	done
>>> +	unset array redirects i index iface protocol port targetip enabled;
>>> +	return 0;
>>> +}
>>> +
>>> +activate_redirections() {
>>> +
>>> +	if ! create_chain; then return 1; fi;
>>> +	
>>> +	# Force DNS REDIRECTs on GREEN (udp, tcp, 53)
>>> +	if [[ "${REDIR_DNS_GREEN}" == "on" &&  "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
>>> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p udp -m udp --dport domain -j REDIRECT;
>>> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
>>> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p tcp -m tcp --dport domain -j REDIRECT;
>>> +	fi
>>> +
>>> +	# Force DNS REDIRECTs on BLUE (udp, tcp, 53)
>>> +	if [[ "${REDIR_DNS_BLUE}" == "on" &&  "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp -m udp --dport domain -j REDIRECT
>>> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p tcp -m tcp --dport domain -j REDIRECT
>>> +	fi
>>> +
>>> +	# Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
>>> +	if [[ "${REDIR_DNS_ORANGE}" == "on" &&  "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p udp -m udp --dport domain -j REDIRECT
>>> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p tcp -m tcp --dport domain -j REDIRECT
>>> +	fi
>>> +
>>> +	# Force NTP REDIRECTs on GREEN (udp, 123)
>>> +	if [[ "${REDIR_NTP_GREEN}" == "on" &&  "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>> +		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p udp -m udp --dport ntp -j REDIRECT
>>> +	fi
>>> +
>>> +	# Force NTP REDIRECTs on BLUE (udp, 123)
>>> +	if [[ "${REDIR_NTP_BLUE}" == "on" &&  "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>> +		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp -m udp --dport ntp -j REDIRECT
>>> +	fi
>>> +
>>> +	# Force NTP REDIRECTs on ORANGE (udp, 123)
>>> +	if [[ "${REDIR_NTP_ORANGE}" == "on" &&  "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>> +		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p udp -m udp --dport ntp -j REDIRECT
>>> +	fi
>>> +
>>> +	if ! activate_custom_redirections; then return 1; fi;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +case "${1}" in
>>> +	start)
>>> +		boot_mesg "Loading port redirections..."
>>> +		activate_redirections;
>>> +		evaluate_retval;
>>> +		;;
>>> +
>>> +	stop)	
>>> +		boot_mesg "Removing port redirections..."
>>> +		remove_chain;
>>> +		evaluate_retval;
>>> +		;;
>>> +
>>> +	restart)
>>> +		${0} stop
>>> +		${0} start
>>> +		;;
>>> +
>>> +	*)
>>> +		echo "Usage: ${0} {start|stop|restart}"
>>> +		exit 1
>>> +		;;
>>> +esac
>>> +
>>> +# End $rc_base/init.d/portredir
>>> diff --git a/src/misc-progs/Makefile b/src/misc-progs/Makefile
>>> index 7c3ef7529..850f8fdcc 100644
>>> --- a/src/misc-progs/Makefile
>>> +++ b/src/misc-progs/Makefile
>>> @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
>>> 	wirelessctrl getipstat qosctrl \
>>> 	redctrl syslogdctrl extrahdctrl sambactrl \
>>> 	smartctrl clamavctrl addonctrl pakfire mpfirectrl wlanapctrl \
>>> -	setaliases urlfilterctrl updxlratorctrl fireinfoctrl rebuildroutes \
>>> +	setaliases urlfilterctrl updxlratorctrl fireinfoctrl rebuildroutes portredirctrl \
>>> 	getconntracktable wirelessclient torctrl ddnsctrl unboundctrl \
>>> 	captivectrl
>>> 
>>> diff --git a/src/misc-progs/portredirctrl.c b/src/misc-progs/portredirctrl.c
>>> new file mode 100644
>>> index 000000000..7897d711c
>>> --- /dev/null
>>> +++ b/src/misc-progs/portredirctrl.c
>>> @@ -0,0 +1,47 @@
>>> +/* This file is part of the IPFire Firewall.
>>> + *
>>> + * This program is distributed under the terms of the GNU General Public
>>> + * Licence.  See the file COPYING for details.
>>> + *
>>> + */
>>> +
>>> +#include <stdlib.h>
>>> +#include <stdio.h>
>>> +#include <string.h>
>>> +#include <unistd.h>
>>> +#include <sys/types.h>
>>> +#include <fcntl.h>
>>> +#include "setuid.h"
>>> +
>>> +int main(int argc, char *argv[]) {
>>> +	if (!(initsetuid()))
>>> +		exit(1);
>>> +
>>> +	// Check what command is asked
>>> +        if (argc < 2) {
>>> +                fprintf(stderr, "\nNo argument given.\n\nportredirctrl (start|stop|restart|enable|disable)\n\n");
>>> +                exit(1);
>>> +        }
>>> +
>>> +        if (strcmp(argv[1], "start") == 0) {
>>> +                safe_system("/etc/rc.d/init.d/portredir start");
>>> +        } else if (strcmp(argv[1], "stop") == 0) {
>>> +                safe_system("/etc/rc.d/init.d/portredir stop");
>>> +        } else if (strcmp(argv[1], "restart") == 0) {
>>> +                safe_system("/etc/rc.d/init.d/portredir restart");
>>> +	} else if (strcmp(argv[1], "enable") == 0) {
>>> +		safe_system("touch /var/ipfire/portredir/enable");
>>> +		safe_system("ln -snf /etc/rc.d/init.d/portredir /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
>>> +		safe_system("ln -snf /etc/rc.d/init.d/portredir /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
>>> +		safe_system("ln -snf /etc/rc.d/init.d/portredir /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
>>> +	} else if (strcmp(argv[1], "disable") == 0) {
>>> +		safe_system("/etc/rc.d/init.d/portredir stop");
>>> +		safe_system("unlink /var/ipfire/portredir/enable");
>>> +		safe_system("rm -rf /etc/rc.d/rc*.d/*portredir >/dev/null 2>&1");
>>> +        } else {
>>> +                fprintf(stderr, "\nBad argument given.\n\nportredirctrl (start|stop|restart|enable|disable)\n\n");
>>> +                exit(1);
>>> +        }
>>> +
>>> +	return 0;
>>> +}
>>> diff --git a/src/paks/portredir/install.sh b/src/paks/portredir/install.sh
>>> new file mode 100644
>>> index 000000000..9f69aeae2
>>> --- /dev/null
>>> +++ b/src/paks/portredir/install.sh
>>> @@ -0,0 +1,32 @@
>>> +#!/bin/bash
>>> +############################################################################
>>> +#                                                                          #
>>> +# This file is part of the IPFire Firewall.                                #
>>> +#                                                                          #
>>> +# IPFire is free software; you can redistribute it and/or modify           #
>>> +# it under the terms of the GNU General Public License as published by     #
>>> +# the Free Software Foundation; either version 2 of the License, or        #
>>> +# (at your option) any later version.                                      #
>>> +#                                                                          #
>>> +# IPFire is distributed in the hope that it will be useful,                #
>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of           #
>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
>>> +# GNU General Public License for more details.                             #
>>> +#                                                                          #
>>> +# You should have received a copy of the GNU General Public License        #
>>> +# along with IPFire; if not, write to the Free Software                    #
>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA #
>>> +#                                                                          #
>>> +# Copyright (C) 2021 IPFire-Team <info@ipfire.org>.                        #
>>> +#                                                                          #
>>> +############################################################################
>>> +#
>>> +. /opt/pakfire/lib/functions.sh
>>> +extract_files
>>> +restore_backup ${NAME}
>>> +
>>> +/usr/local/bin/update-lang-cache
>>> +
>>> +chown root:nobody /usr/local/bin/portredirctrl
>>> +chmod 4750 /usr/local/bin/portredirctrl
>>> +chmod u+s /usr/local/bin/portredirctrl
>>> diff --git a/src/paks/portredir/uninstall.sh b/src/paks/portredir/uninstall.sh
>>> new file mode 100644
>>> index 000000000..df9270125
>>> --- /dev/null
>>> +++ b/src/paks/portredir/uninstall.sh
>>> @@ -0,0 +1,28 @@
>>> +#!/bin/bash
>>> +############################################################################
>>> +#                                                                          #
>>> +# This file is part of the IPFire Firewall.                                #
>>> +#                                                                          #
>>> +# IPFire is free software; you can redistribute it and/or modify           #
>>> +# it under the terms of the GNU General Public License as published by     #
>>> +# the Free Software Foundation; either version 2 of the License, or        #
>>> +# (at your option) any later version.                                      #
>>> +#                                                                          #
>>> +# IPFire is distributed in the hope that it will be useful,                #
>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of           #
>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
>>> +# GNU General Public License for more details.                             #
>>> +#                                                                          #
>>> +# You should have received a copy of the GNU General Public License        #
>>> +# along with IPFire; if not, write to the Free Software                    #
>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA #
>>> +#                                                                          #
>>> +# Copyright (C) 2007 IPFire-Team <info@ipfire.org>.                        #
>>> +#                                                                          #
>>> +############################################################################
>>> +#
>>> +. /opt/pakfire/lib/functions.sh
>>> +make_backup ${NAME}
>>> +remove_files
>>> +
>>> +/usr/local/bin/update-lang-cache
>>> diff --git a/src/paks/portredir/update.sh b/src/paks/portredir/update.sh
>>> new file mode 100644
>>> index 000000000..89c40d0d7
>>> --- /dev/null
>>> +++ b/src/paks/portredir/update.sh
>>> @@ -0,0 +1,26 @@
>>> +#!/bin/bash
>>> +############################################################################
>>> +#                                                                          #
>>> +# This file is part of the IPFire Firewall.                                #
>>> +#                                                                          #
>>> +# IPFire is free software; you can redistribute it and/or modify           #
>>> +# it under the terms of the GNU General Public License as published by     #
>>> +# the Free Software Foundation; either version 2 of the License, or        #
>>> +# (at your option) any later version.                                      #
>>> +#                                                                          #
>>> +# IPFire is distributed in the hope that it will be useful,                #
>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of           #
>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
>>> +# GNU General Public License for more details.                             #
>>> +#                                                                          #
>>> +# You should have received a copy of the GNU General Public License        #
>>> +# along with IPFire; if not, write to the Free Software                    #
>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA #
>>> +#                                                                          #
>>> +# Copyright (C) 2007 IPFire-Team <info@ipfire.org>.                        #
>>> +#                                                                          #
>>> +############################################################################
>>> +#
>>> +. /opt/pakfire/lib/functions.sh
>>> +./uninstall.sh
>>> +./install.sh
>>> -- 
>>> 2.18.0
>>> 
>> 
>
  
Stefan Schantl June 30, 2021, 7:14 p.m. UTC | #4
Hello Matthias, Hello Michael, Hello Jon, Hello *,

I've followed the conversation on this list since the first mail and
thoughts about forcing DNS traffic to use the local resolver.

It was a very long journey and lot of time and work has been spent to
get to the present point.

As Michael requested here, I've digged through the lines of the perl
script which is responsible for creating the firewall rules and
surprisingly found that everyting which is needed to create generic
REDIRECT rules already was written in the past - it just did not work
as designed/expected.

Finaly I was able to adjust these lines of code and to repair that
feature.

A redirect rule can be created by picking a single host or group of
hosts or entire network(s) as source, selecting NAT (DNAT) and choosing
the Firewall itself as target.

The protocol or service or service group which should be redirected has
to be selected afterwards. If you want to redirect a given port to
another one it can be specified as "Target port".

All created redirect rules are displayed as "input rules".


The patch directly can be accessed here:

https://patchwork.ipfire.org/project/ipfire/patch/20210630184031.7726-1-stefan.schantl@ipfire.org/

Best regards,

-Stefan

> Hello,
> 
> > On 28 Jun 2021, at 18:53, Jon Murphy <jcmurphy26@gmail.com> wrote:
> > 
> > Hi Michael!  Happy Monday!
> > 
> > 
> > > Why do we not extend the firewall UI probably by about 20 lines
> > > of code instead of adding many hundreds of lines?
> > > 
> > > Please can someone elaborate on this more?
> > 
> > Doing a DNS redirect, via the WegBUI, has been an issue since
> > 2015.  I found this quote in the old forum:
> > 
> > "Having investigated a bit more I have concluded that it's not
> > currently possible to create such rules through the WUI.
> > 
> > There are a number of obstacles:
> > 1. It is not allowed to create a rule where source IP and
> > destination nat IP is on the same subnetwork (e.g. GREEN), WUI
> > error message: "Source and destination IP addresses are from the
> > same subnet."
> > 
> > 2. WUI will not allow you to create a rule without a destination
> > (the filtered packet must adhere to a destination, not only a port)
> > and the destination MUST be an IP address of one of the IPFire
> > interfaces, which limits whats possible a great deal." 
> 
> And these cannot be changed?
> 
> > And I found this from 2016:
> > https://bugzilla.ipfire.org/show_bug.cgi?id=11168
> > 
> > So I am guessing that no one has been able to determine a way to
> > extend the WebGUI.  
> 
> Has anyone tried? I do not see any obvious reasons why this should
> not be possible.
> 
> > I am curious - Who created the 
> > https://ipfire:444/cgi-bin/firewall.cgi page?  And could they help?
> 
> -Michael
> 
> > Jon
> > 
> > 
> > > On Jun 28, 2021, at 11:04 AM, Michael Tremer <
> > > michael.tremer@ipfire.org> wrote:
> > > 
> > > Hello Matthias,
> > > 
> > > > On 27 Jun 2021, at 14:48, Matthias Fischer <
> > > > matthias.fischer@ipfire.org> wrote:
> > > > 
> > > > From: Marcel Lorenz <marcel.lorenz@ipfire.org>
> > > 
> > > Thank you for sending this patch on Marcel’s behalf, but I would
> > > much more prefer if he would submit his patches on his own. I do
> > > not see why that isn’t possible.
> > > 
> > > > Please note:
> > > > This is a new addon written by Marcel Lorenz <
> > > > marcel.lorenz@ipfire.org>.
> > > > 
> > > > It adds a new GUI to IPFire for DNS/NTP *and* user specific
> > > > port redirections.
> > > > 
> > > > How its working:
> > > > It has exactly the same functionalities as "Forcing
> > > > DNS/NTP..."  - and some more.
> > > > 
> > > > By setting switches, DNS/NTP requests are automatically
> > > > redirected to the local IPFire DNS/NTP servers.
> > > > 
> > > > Additionally, the user can specify custom redirections.
> > > > 
> > > > These rules are added to a new chain in PREROUTING =>
> > > > PORT_REDIRECT.
> > > > 
> > > > To avoid problems with (e.g.) transparent 'squid'
> > > > configurations,
> > > > redirection rules are added automatically before existing
> > > > 'squid' rules.
> > > 
> > > This message does unfortunately not say why this add-on would be
> > > useful. I am emphasising this again and again that it is not very
> > > important how something is done specially. That should be
> > > commented in the code and other implementation details should
> > > also be documented there.
> > > 
> > > As I have stated on this functionality many times before, I do
> > > not see why this is necessary at all.
> > > 
> > > Why is this an add-on?
> > > 
> > > Why do we not extend the firewall UI probably by about 20 lines
> > > of code instead of adding many hundreds of lines?
> > > 
> > > Please can someone elaborate on this more?
> > > 
> > > -Michael
> > > 
> > > > Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
> > > > ---
> > > > config/portredir/EX-portredir.menu    |   6 +
> > > > config/portredir/lang/portredir.de.pl |  19 +
> > > > config/portredir/lang/portredir.en.pl |  19 +
> > > > config/portredir/portredir-backup     |   1 +
> > > > config/portredir/portredir.cgi        | 525
> > > > ++++++++++++++++++++++++++
> > > > config/rootfiles/common/misc-progs    |   1 +
> > > > config/rootfiles/packages/portredir   |  11 +
> > > > lfs/portredir                         |  85 +++++
> > > > make.sh                               |   1 +
> > > > src/initscripts/packages/portredir    | 191 ++++++++++
> > > > src/misc-progs/Makefile               |   2 +-
> > > > src/misc-progs/portredirctrl.c        |  47 +++
> > > > src/paks/portredir/install.sh         |  32 ++
> > > > src/paks/portredir/uninstall.sh       |  28 ++
> > > > src/paks/portredir/update.sh          |  26 ++
> > > > 15 files changed, 993 insertions(+), 1 deletion(-)
> > > > create mode 100644 config/portredir/EX-portredir.menu
> > > > create mode 100644 config/portredir/lang/portredir.de.pl
> > > > create mode 100644 config/portredir/lang/portredir.en.pl
> > > > create mode 100644 config/portredir/portredir-backup
> > > > create mode 100644 config/portredir/portredir.cgi
> > > > create mode 100644 config/rootfiles/packages/portredir
> > > > create mode 100644 lfs/portredir
> > > > create mode 100644 src/initscripts/packages/portredir
> > > > create mode 100644 src/misc-progs/portredirctrl.c
> > > > create mode 100644 src/paks/portredir/install.sh
> > > > create mode 100644 src/paks/portredir/uninstall.sh
> > > > create mode 100644 src/paks/portredir/update.sh
> > > > 
> > > > diff --git a/config/portredir/EX-portredir.menu
> > > > b/config/portredir/EX-portredir.menu
> > > > new file mode 100644
> > > > index 000000000..8376e8053
> > > > --- /dev/null
> > > > +++ b/config/portredir/EX-portredir.menu
> > > > @@ -0,0 +1,6 @@
> > > > +    $subfirewall->{'95.portredir'} = {
> > > > +                               'caption' =>
> > > > $Lang::tr{'portredir port redirections'},
> > > > +                               'uri' => '/cgi-
> > > > bin/portredir.cgi',
> > > > +                               'title' =>
> > > > "$Lang::tr{'portredir port redirections'}",
> > > > +                               'enabled' => 1
> > > > +                               };
> > > > diff --git a/config/portredir/lang/portredir.de.pl
> > > > b/config/portredir/lang/portredir.de.pl
> > > > new file mode 100644
> > > > index 000000000..b932d4a85
> > > > --- /dev/null
> > > > +++ b/config/portredir/lang/portredir.de.pl
> > > > @@ -0,0 +1,19 @@
> > > > +%tr = (
> > > > +%tr,
> > > > +'portredir enable addon' => 'Addon aktivieren',
> > > > +'portredir common settings' => 'Allgemeine Einstellungen',
> > > > +'portredir port redirections' => 'Portumleitungen',
> > > > +'portredir fw for interface' => 'Firewalloptionen für das
> > > > Interface',
> > > > +'portredir enable user redirections' => 'Aktiviere
> > > > benutzerdefinierte Portumleitungen',
> > > > +'portredir force local dns' => 'Erzwinge lokale DNS-Server',
> > > > +'portredir force local ntp' => 'Erzwinge lokale NTP-Server',
> > > > +'portredir custom redirections' => 'Benutzerdefinierte
> > > > Portumleitungen',
> > > > +'portredir remove rule' => 'Entferne Regel',
> > > > +'portredir add rule' => 'Hinzufügen',
> > > > +'portredir no entries' => 'Keine Einträge vorhanden.',
> > > > +'portredir invalid address' => 'Ungültige Host-Addresse.',
> > > > +'portredir empty input' => 'Fehlende Angabe: Bitte geben Sie
> > > > einen gültigen Host an.',
> > > > +'portredir save to activate' => 'Speichern, um Änderungen zu
> > > > aktivieren',
> > > > +);
> > > > +
> > > > +#EOF
> > > > diff --git a/config/portredir/lang/portredir.en.pl
> > > > b/config/portredir/lang/portredir.en.pl
> > > > new file mode 100644
> > > > index 000000000..f442f3eaa
> > > > --- /dev/null
> > > > +++ b/config/portredir/lang/portredir.en.pl
> > > > @@ -0,0 +1,19 @@
> > > > +%tr = (
> > > > +%tr,
> > > > +'portredir enable addon' => 'Enable addon',
> > > > +'portredir common settings' => 'Common settings',
> > > > +'portredir port redirections' => 'Port redirections',
> > > > +'portredir fw for interface' => 'Firewall options for
> > > > interface',
> > > > +'portredir enable user redirections' => 'Enable user port
> > > > redirections',
> > > > +'portredir force local dns' => 'Enforce local DNS servers',
> > > > +'portredir force local ntp' => 'Enforce local NTP servers',
> > > > +'portredir custom redirections' => 'Custom port redirections',
> > > > +'portredir remove rule' => 'Remove rule',
> > > > +'portredir add rule' => 'Add new',
> > > > +'portredir no entries' => 'No entries at the moment.',
> > > > +'portredir invalid address' => 'Invalid host address.',
> > > > +'portredir empty input' => 'Empty input: Please enter a valid
> > > > host.',
> > > > +'portredir save to activate' => 'Save to activate changes',
> > > > +);
> > > > +
> > > > +#EOF
> > > > diff --git a/config/portredir/portredir-backup
> > > > b/config/portredir/portredir-backup
> > > > new file mode 100644
> > > > index 000000000..bd2ada742
> > > > --- /dev/null
> > > > +++ b/config/portredir/portredir-backup
> > > > @@ -0,0 +1 @@
> > > > +/var/ipfire/portredir
> > > > diff --git a/config/portredir/portredir.cgi
> > > > b/config/portredir/portredir.cgi
> > > > new file mode 100644
> > > > index 000000000..4913dda3f
> > > > --- /dev/null
> > > > +++ b/config/portredir/portredir.cgi
> > > > @@ -0,0 +1,525 @@
> > > > +#!/usr/bin/perl
> > > > +##############################################################
> > > > #################
> > > > +#                                                             
> > > >                 #
> > > > +# IPFire.org - A linux based
> > > > firewall                                         #
> > > > +# Copyright (C) 2021  IPFire Team 
> > > > <info@ipfire.org>                          #
> > > > +#                                                             
> > > >                 #
> > > > +# This program is free software: you can redistribute it
> > > > and/or modify        #
> > > > +# it under the terms of the GNU General Public License as
> > > > published by        #
> > > > +# the Free Software Foundation, either version 3 of the
> > > > License, or           #
> > > > +# (at your option) any later
> > > > version.                                         #
> > > > +#                                                             
> > > >                 #
> > > > +# This program is distributed in the hope that it will be
> > > > useful,             #
> > > > +# but WITHOUT ANY WARRANTY; without even the implied warranty
> > > > of              #
> > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
> > > > the               #
> > > > +# GNU General Public License for more
> > > > details.                                #
> > > > +#                                                             
> > > >                 #
> > > > +# You should have received a copy of the GNU General Public
> > > > License           #
> > > > +# along with this program.  If not, see <
> > > > http://www.gnu.org/licenses/>.       #
> > > > +#                                                             
> > > >                 #
> > > > +##############################################################
> > > > #################
> > > > +
> > > > +use strict;
> > > > +
> > > > +# enable only the following on debugging purpose
> > > > +use warnings;
> > > > +use CGI::Carp 'fatalsToBrowser';
> > > > +
> > > > +require '/var/ipfire/general-functions.pl';
> > > > +require "${General::swroot}/lang.pl";
> > > > +require "${General::swroot}/header.pl";
> > > > +
> > > > +# File declarations
> > > > +my $settingsfile = "${General::swroot}/portredir/settings";
> > > > +my $redirectsfile = "${General::swroot}/portredir/redirects";
> > > > +
> > > > +# Create empty settingsfiles if they does not exist yet
> > > > +unless (-e "$settingsfile") { system ("touch $settingsfile");
> > > > }
> > > > +unless (-e "$redirectsfile") { system ("touch
> > > > $redirectsfile"); }
> > > > +
> > > > +# load ipfire settings
> > > > +our %netsettings = ();
> > > > +our %color = ();
> > > > +&General::readhash("${General::swroot}/ethernet/settings",
> > > > \%netsettings);
> > > > +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include
> > > > /colors.txt", \%color);
> > > > +
> > > > +my %settings=();
> > > > +my %portredirs=();
> > > > +my %checked=(); # Checkbox manipulations
> > > > +my $errormessage='';
> > > > +my %selected=();
> > > > +our %redirects=();
> > > > +
> > > > +$settings{'ACTION'} = '';
> > > > +$settings{'REDIR_ENABLE_ADDON'}="off";
> > > > +$settings{'REDIR_CUSTOM_GREEN'}="off";
> > > > +$settings{'REDIR_CUSTOM_BLUE'}="off";
> > > > +$settings{'REDIR_CUSTOM_ORANGE'}="off";
> > > > +$settings{'REDIR_DNS_GREEN'}="off";
> > > > +$settings{'REDIR_NTP_GREEN'}="off";
> > > > +$settings{'REDIR_DNS_BLUE'}="off";
> > > > +$settings{'REDIR_NTP_BLUE'}="off";
> > > > +$settings{'REDIR_DNS_ORANGE'}="off";
> > > > +$settings{'REDIR_NTP_ORANGE'}="off";
> > > > +
> > > > +&Header::showhttpheaders();
> > > > +
> > > > +# Get GUI values
> > > > +&Header::getcgihash(\%settings);
> > > > +
> > > > +# Save action
> > > > +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
> > > > +
> > > > +       # If custom rules enabled, deactivate default rules on
> > > > interface
> > > > +       if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
> > > > +               $settings{'REDIR_DNS_GREEN'}="off";
> > > > +               $settings{'REDIR_NTP_GREEN'}="off";
> > > > +       }
> > > > +
> > > > +       if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
> > > > +               $settings{'REDIR_DNS_BLUE'}="off";
> > > > +               $settings{'REDIR_NTP_BLUE'}="off";
> > > > +       }
> > > > +
> > > > +       if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
> > > > +               $settings{'REDIR_DNS_ORANGE'}="off";
> > > > +               $settings{'REDIR_NTP_ORANGE'}="off";
> > > > +       }
> > > > +
> > > > +       &General::writehash($settingsfile, \%settings);
> > > > +
> > > > +       if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
> > > > +               system ('/usr/local/bin/portredirctrl restart
> > > > >/dev/null 2>&1');
> > > > +               system ('/usr/local/bin/portredirctrl enable
> > > > >/dev/null 2>&1');
> > > > +               &General::log('portredir addon: port
> > > > redirections enabled');
> > > > +       }
> > > > +       if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
> > > > +               system ('/usr/local/bin/portredirctrl disable
> > > > >/dev/null 2>&1');
> > > > +               system ('/usr/local/bin/portredirctrl stop
> > > > >/dev/null 2>&1');
> > > > +               &General::log('portredir addon: port
> > > > redirections disabled');
> > > > +       }
> > > > +
> > > > +# Add/edit an entry to the redirectsfile.
> > > > +
> > > > +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) ||
> > > > ($settings{'ACTION'} eq $Lang::tr{'update'})) {
> > > > +
> > > > +       # Check if any input has been performed.
> > > > +       if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
> > > > +
> > > > +               # Check if the given input is no valid IP-
> > > > address, display an error message.
> > > > +               if
> > > > (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
> > > > +                       $errormessage = "$Lang::tr{'portredir
> > > > invalid address'}";
> > > > +               }
> > > > +       } else {
> > > > +               $errormessage = "$Lang::tr{'portredir empty
> > > > input'}";
> > > > +       }
> > > > +
> > > > +       # Go further if there was no error.
> > > > +       if ($errormessage eq '') {
> > > > +               my %redirects = ();
> > > > +               my $id;
> > > > +               my $status;
> > > > +
> > > > +               # Assign hash values.
> > > > +               my $new_entry_interface =
> > > > $settings{'REDIR_ENTRY_INTERFACE'};
> > > > +               my $new_entry_protocol =
> > > > $settings{'REDIR_ENTRY_PROTOCOL'};
> > > > +               my $new_entry_port =
> > > > $settings{'REDIR_ENTRY_PORT'};
> > > > +               my $new_entry_address =
> > > > $settings{'REDIR_ENTRY_ADDRESS'};
> > > > +               my $new_entry_remark =
> > > > $settings{'REDIR_ENTRY_REMARK'};
> > > > +
> > > > +               # Read-in redirectsfile.
> > > > +               &General::readhasharray($redirectsfile,
> > > > \%redirects);
> > > > +
> > > > +               # Check if we should edit an existing entry and
> > > > got an ID.
> > > > +               if (($settings{'ACTION'} eq
> > > > $Lang::tr{'update'}) && ($settings{'ID'})) {
> > > > +                       # Assin the provided id.
> > > > +                       $id = $settings{'ID'};
> > > > +
> > > > +                       # Undef the given ID.
> > > > +                       undef($settings{'ID'});
> > > > +
> > > > +                       # Grab the configured status of the
> > > > corresponding entry.
> > > > +                       $status = $redirects{$id}[4];
> > > > +               } else {
> > > > +                       # Each newly added entry automatically
> > > > should be enabled.
> > > > +                       $status = "enabled";
> > > > +
> > > > +                       # Generate the ID for the new entry.
> > > > +                       #
> > > > +                       # Sort the keys by their ID and store
> > > > them in an array.
> > > > +                       my @keys = sort { $a <=> $b } keys
> > > > %redirects;
> > > > +
> > > > +                       # Reverse the key array.
> > > > +                       my @reversed = reverse(@keys);
> > > > +
> > > > +                       # Obtain the last used id.
> > > > +                       my $last_id = @reversed[0];
> > > > +
> > > > +                       # Increase the last id by one and use
> > > > it as id for the new entry.
> > > > +                       $id = ++$last_id;
> > > > +               }
> > > > +
> > > > +               # Add/Modify the entry to/in the redirects
> > > > hash.
> > > > +               $redirects{$id} = ["$new_entry_interface",
> > > > "$new_entry_protocol", "$new_entry_port",
> > > > "$new_entry_address","$status", "$new_entry_remark"];
> > > > +
> > > > +               # Write the changed redirects hash to the
> > > > redirects file.
> > > > +               &General::writehasharray($redirectsfile,
> > > > \%redirects);
> > > > +       }
> > > > +
> > > > +# Toggle Enabled/Disabled for an existing entry on the
> > > > redirects list.
> > > > +
> > > > +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable
> > > > disable'}) {
> > > > +       my %redirects = ();
> > > > +
> > > > +       # Only go further, if an ID has been passed.
> > > > +       if ($settings{'ID'}) {
> > > > +               # Assign the given ID.
> > > > +               my $id = $settings{'ID'};
> > > > +
> > > > +               # Undef the given ID.
> > > > +               undef($settings{'ID'});
> > > > +
> > > > +               # Read-in ignoredfile.
> > > > +               &General::readhasharray($redirectsfile,
> > > > \%redirects);
> > > > +
> > > > +               # Grab the configured status of the
> > > > corresponding entry.
> > > > +               my $status = $redirects{$id}[4];
> > > > +
> > > > +               # Switch the status.
> > > > +               if ($status eq "disabled") {
> > > > +                       $status = "enabled";
> > > > +               } else {
> > > > +                       $status = "disabled";
> > > > +               }
> > > > +
> > > > +               # Modify the status of the existing entry.
> > > > +               $redirects{$id} = ["$redirects{$id}[0]",
> > > > "$redirects{$id}[1]", "$redirects{$id}[2]",
> > > > "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
> > > > +
> > > > +               # Write the changed ignored hash to the
> > > > redirects file.
> > > > +               &General::writehasharray($redirectsfile,
> > > > \%redirects);
> > > > +       }
> > > > +
> > > > +# Remove entry from redirects list.
> > > > +
> > > > +} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
> > > > +       my %redirects = ();
> > > > +
> > > > +       # Read-in redirectsfile.
> > > > +       &General::readhasharray($redirectsfile, \%redirects);
> > > > +
> > > > +       # move data on key up
> > > > +       foreach my $key (sort keys %redirects) {
> > > > +               if ($key >= $settings{'ID'}) {
> > > > +                       my $next = $key + 1;
> > > > +                       if (exists $redirects{$next}) {
> > > > +                               foreach my $i (0 ..
> > > > $#{$redirects{$next}}) { $redirects{$key}[$i] =
> > > > $redirects{$next}[$i]; }
> > > > +                       }
> > > > +               }
> > > > +       }
> > > > +
> > > > +       my $last_key = (sort {$a <=> $b} keys %redirects)[-1];
> > > > +       delete $redirects{$last_key};
> > > > +
> > > > +       # Undef the given ID.
> > > > +       undef($settings{'ID'});
> > > > +
> > > > +       # Write the changed redirects hash to file.
> > > > +       &General::writehasharray($redirectsfile, \%redirects);
> > > > +}
> > > > +
> > > > +# Load settings from file
> > > > +&General::readhash($settingsfile, \%settings);
> > > > +&General::readhasharray($redirectsfile, \%redirects);
> > > > +
> > > > +# Call functions to generate whole page.
> > > > +&Header::openpage($Lang::tr{'portredir port redirections'}, 1,
> > > > '');
> > > > +&Header::openbigbox('100%', 'left', '', $errormessage);
> > > > +
> > > > +if ($errormessage) {
> > > > +        &Header::openbox('100%', 'left', $Lang::tr{'warning
> > > > messages'});
> > > > +        print "<font color='red'>$errormessage&nbsp;</font>";
> > > > +        &Header::closebox();
> > > > +}
> > > > +
> > > > +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
> > > > +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
> > > > +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_ADDON'}
> > > > } = "checked='checked'";
> > > > +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
> > > > +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
> > > > +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_GREEN'}
> > > > } = "checked='checked'";
> > > > +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
> > > > +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
> > > > +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BLUE'}}
> > > > = "checked='checked'";
> > > > +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
> > > > +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
> > > > +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_ORANGE
> > > > '}} = "checked='checked'";
> > > > +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
> > > > +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
> > > > +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}} =
> > > > "checked='checked'";
> > > > +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
> > > > +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
> > > > +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}} =
> > > > "checked='checked'";
> > > > +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
> > > > +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
> > > > +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}} =
> > > > "checked='checked'";
> > > > +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
> > > > +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
> > > > +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}} =
> > > > "checked='checked'";
> > > > +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
> > > > +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
> > > > +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE'}} =
> > > > "checked='checked'";
> > > > +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
> > > > +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
> > > > +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE'}} =
> > > > "checked='checked'";
> > > > +
> > > > +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTRY_INTE
> > > > RFACE'}} = 'selected';
> > > > +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY_PROTO
> > > > COL'}} = 'selected';
> > > > +
> > > > +&showMainBox();
> > > > +&showRedirectsBox();
> > > > +
> > > > +&Header::closebigbox();
> > > > +&Header::closepage();
> > > > +
> > > > +# Function to show main settings and options.
> > > > +sub showMainBox() {
> > > > +
> > > > +       &Header::openbox('100%', 'center',
> > > > "$Lang::tr{'settings'}");
> > > > +       print "<form method='post'
> > > > action='$ENV{'SCRIPT_NAME'}'>";
> > > > +
> > > > +print <<END;
> > > > +<table width='80%' cellspacing='0' border='0'>
> > > > +       <tr><td colspan='2' class='base'
> > > > bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir common
> > > > settings'}</b></td></tr
> > > > +       <tr>
> > > > +               <td width='25%'
> > > > class='base'>$Lang::tr{'portredir enable addon'}:</td>
> > > > +               <td><input type='checkbox'
> > > > name='REDIR_ENABLE_ADDON' $checked{'REDIR_ENABLE_ADDON'}{'on'}
> > > > /></td>
> > > > +       </tr>
> > > > +       <tr><td colspan='2'></td></tr>
> > > > +       <tr><td colspan='2'>&nbsp;</td></tr>
> > > > +END
> > > > +
> > > > +       # create html table with header line 1
> > > > +       print "<table width='80%' cellspacing='0'
> > > > border='0'><tr>";
> > > > +       print "<th class='base' width='40%'
> > > > align='left'><b>$Lang::tr{'portredir fw for interface'}</th>";
> > > > +       if ($netsettings{'GREEN_DEV'})  {print "<th
> > > > class='base' width='10%'><b><font
> > > > color=green>$Lang::tr{'green'}</font></th>";
> > > > +               } else {                 print "<th
> > > > class='base' width='10%'></font></th>"; }
> > > > +       if ($netsettings{'BLUE_DEV'})   {print "<th
> > > > class='base' width='10%'><b><font
> > > > color=blue>$Lang::tr{'blue'}</font></th>";
> > > > +               } else {                 print "<th
> > > > class='base' width='10%'></font></th>"; }
> > > > +       if ($netsettings{'ORANGE_DEV'}) {print "<th
> > > > class='base' width='10%'><b><font
> > > > color=orange>$Lang::tr{'orange'}</font></th>";
> > > > +               } else {                 print "<th
> > > > class='base' width='10%'></font></th>"; }
> > > > +
> > > > +       # the empty right row
> > > > +       print "<th class='base'
> > > > width='30%'><td></td></th></tr>";
> > > > +
> > > > +       # line 2
> > > > +       print "<tr><td>$Lang::tr{'portredir force local
> > > > dns'}</td>";
> > > > +       if ($netsettings{'GREEN_DEV'})  {print "<td
> > > > class='base' align='center'><input type='checkbox'
> > > > name='REDIR_DNS_GREEN'
> > > > $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print
> > > > "<td></td>";}
> > > > +       if ($netsettings{'BLUE_DEV'})   {print "<td
> > > > class='base' align='center'><input type='checkbox'
> > > > name='REDIR_DNS_BLUE' $checked{'REDIR_DNS_BLUE'}{'on'}></td>";}
> > > > else { print "<td></td>";}
> > > > +       if ($netsettings{'ORANGE_DEV'}) {print "<td
> > > > class='base' align='center'><input type='checkbox'
> > > > name='REDIR_DNS_ORANGE'
> > > > $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print
> > > > "<td></td>";}
> > > > +
> > > > +       # line 3
> > > > +       print "</tr><tr><td>$Lang::tr{'portredir force local
> > > > ntp'}</td>";
> > > > +       if ($netsettings{'GREEN_DEV'})  {print "<td
> > > > class='base' align='center'><input type='checkbox'
> > > > name='REDIR_NTP_GREEN'
> > > > $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print
> > > > "<td></td>";}
> > > > +       if ($netsettings{'BLUE_DEV'})   {print "<td
> > > > class='base' align='center'><input type='checkbox'
> > > > name='REDIR_NTP_BLUE' $checked{'REDIR_NTP_BLUE'}{'on'}></td>";}
> > > > else { print "<td></td>";}
> > > > +       if ($netsettings{'ORANGE_DEV'}) {print "<td
> > > > class='base' align='center'><input type='checkbox'
> > > > name='REDIR_NTP_ORANGE'
> > > > $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print
> > > > "<td></td>";}
> > > > +
> > > > +       # line 4
> > > > +       print "</tr><tr><td>$Lang::tr{'portredir enable user
> > > > redirections'}</td>";
> > > > +       if ($netsettings{'GREEN_DEV'})  {print "<td
> > > > class='base' align='center'><input type='checkbox'
> > > > name='REDIR_CUSTOM_GREEN'
> > > > $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else { print
> > > > "<td></td>";}
> > > > +       if ($netsettings{'BLUE_DEV'})   {print "<td
> > > > class='base' align='center'><input type='checkbox'
> > > > name='REDIR_CUSTOM_BLUE'
> > > > $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print
> > > > "<td></td>";}
> > > > +       if ($netsettings{'ORANGE_DEV'}) {print "<td
> > > > class='base' align='center'><input type='checkbox'
> > > > name='REDIR_CUSTOM_ORANGE'
> > > > $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else { print
> > > > "<td></td>";}
> > > > +
> > > > +       print <<END;
> > > > +       </tr></table>
> > > > +       <table width='80%' cellspacing='0' border='0'>
> > > > +               <tr><td colspan='2'>&nbsp;</td></tr>
> > > > +               <tr><td align='left'><b>$Lang::tr{'portredir
> > > > save to activate'}</b></td><td width='5%' align='center'><input
> > > > type='submit' name='ACTION' value='  $Lang::tr{'save'} 
> > > > '></td></tr>
> > > > +       </table></form>
> > > > +END
> > > > +
> > > > +&Header::closebox();
> > > > +}
> > > > +
> > > > +# Function to show elements of the redirects file and allow to
> > > > add or remove single members of it.
> > > > +sub showRedirectsBox() {
> > > > +        &Header::openbox('100%', 'center',
> > > > "$Lang::tr{'portredir custom redirections'}");
> > > > +
> > > > +       print <<END;
> > > > +               <table width='80%' cellspacing='1' border='0'>
> > > > +                       <tr>
> > > > +                               <td class='base'
> > > > bgcolor='$color{'color20'}'
> > > > align='center'><b>$Lang::tr{'interface'}</b></td>
> > > > +                               <td class='base'
> > > > bgcolor='$color{'color20'}'
> > > > align='center'><b>$Lang::tr{'protocol'}</b></td>
> > > > +                               <td class='base'
> > > > bgcolor='$color{'color20'}'
> > > > align='center'><b>$Lang::tr{'port'}</b></td>
> > > > +                               <td class='base'
> > > > bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'ip
> > > > address'}</b></td>
> > > > +                               <td class='base'
> > > > bgcolor='$color{'color20'}'
> > > > align='center'><b>$Lang::tr{'remark'}</b></td>
> > > > +                               <td class='base' colspan='3'
> > > > bgcolor='$color{'color20'}'></td>
> > > > +                       </tr>
> > > > +END
> > > > +                       # Check if some rules have been added
> > > > to be redirects.
> > > > +                       if (keys (%redirects)) {
> > > > +                               my $col = "";
> > > > +
> > > > +                               # List all entries of the hash.
> > > > +                               foreach my $key (sort keys
> > > > %redirects){
> > > > +
> > > > +                                       # Assign data array
> > > > positions to some nice variable names.
> > > > +                                       my $interface =
> > > > $redirects{$key}[0];
> > > > +                                       my $protocol =
> > > > $redirects{$key}[1];
> > > > +                                       my $port  =
> > > > $redirects{$key}[2];
> > > > +                                       my $address =
> > > > $redirects{$key}[3];
> > > > +                                       my $status  =
> > > > $redirects{$key}[4];
> > > > +                                       my $remark  =
> > > > $redirects{$key}[5];
> > > > +
> > > > +                                       # Check if the key (id)
> > > > number is even or not.
> > > > +                                       if ($settings{'ID'} eq
> > > > $key) {
> > > > +                                               $col="bgcolor='
> > > > ${Header::colouryellow}'";
> > > > +                                       } elsif ($key % 2) {
> > > > +                                               $col="bgcolor='
> > > > $color{'color22'}'";
> > > > +                                       } else {
> > > > +                                               $col="bgcolor='
> > > > $color{'color20'}'";
> > > > +                                       }
> > > > +
> > > > +                                       # Choose icon for the
> > > > checkbox.
> > > > +                                       my $gif;
> > > > +                                       my $gdesc;
> > > > +
> > > > +                                       # Check if the status
> > > > is enabled and select the correct image and description.
> > > > +                                       if ($status eq
> > > > 'enabled' ) {
> > > > +                                               $gif =
> > > > 'on.gif';
> > > > +                                               $gdesc =
> > > > $Lang::tr{'click to disable'};
> > > > +                                       } else {
> > > > +                                               $gif =
> > > > 'off.gif';
> > > > +                                               $gdesc =
> > > > $Lang::tr{'click to enable'};
> > > > +                                       }
> > > > +
> > > > +                                       print <<END;
> > > > +                                       <tr>
> > > > +                                               <td width='15%'
> > > > class='base' align='center' $col><b><font
> > > > color=$interface>$Lang::tr{$interface}</font></b></td>
> > > > +                                               <td width='10%'
> > > > class='base' align='center' $col>$protocol</td>
> > > > +                                               <td width='10%'
> > > > class='base' align='center' $col>$port</td>
> > > > +                                               <td width='15%'
> > > > class='base' align='center' $col>&nbsp;$address</td>
> > > > +                                               <td width='40%'
> > > > class='base' align='center' $col>&nbsp;$remark</td>
> > > > +                                               <td
> > > > align='center' $col>
> > > > +                                                       <form
> > > > method='post' action='$ENV{'SCRIPT_NAME'}'>
> > > > +                                                              
> > > >  <input type='hidden' name='ACTION' value='$Lang::tr{'toggle
> > > > enable disable'}' />
> > > > +                                                              
> > > >  <input type='image' name='$Lang::tr{'toggle enable disable'}'
> > > > src='/images/$gif' alt='$gdesc' title='$gdesc' />
> > > > +                                                              
> > > >  <input type='hidden' name='ID' value='$key' />
> > > > +                                                       </form>
> > > > +                                               </td>
> > > > +                                               <td
> > > > align='center' $col>
> > > > +                                                       <form
> > > > method='post' action='$ENV{'SCRIPT_NAME'}'>
> > > > +                                                              
> > > >  <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}'
> > > > />
> > > > +                                                              
> > > >  <input type='image' name='$Lang::tr{'edit'}'
> > > > src='/images/edit.gif' alt='$Lang::tr{'edit'}'
> > > > title='$Lang::tr{'edit'}' />
> > > > +                                                              
> > > >  <input type='hidden' name='ID' value='$key' />
> > > > +                                                       </form>
> > > > +                                               </td>
> > > > +                                               <td
> > > > align='center' $col>
> > > > +                                                       <form
> > > > method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
> > > > +                                                              
> > > >  <input type='image' name='$Lang::tr{'remove'}'
> > > > src='/images/delete.gif' title='$Lang::tr{'remove'}'
> > > > alt='$Lang::tr{'remove'}'>
> > > > +                                                              
> > > >  <input type='hidden' name='ID' value='$key'>
> > > > +                                                              
> > > >  <input type='hidden' name='ACTION'
> > > > value='$Lang::tr{'remove'}'>
> > > > +                                                       </form>
> > > > +                                               </td>
> > > > +                                       </tr>
> > > > +END
> > > > +                               }
> > > > +                       } else {
> > > > +                               # Print notice that currently
> > > > no ports are redirected.
> > > > +                               print "<tr>\n";
> > > > +                               print "<td class='base'
> > > > colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
> > > > +                               print "</tr>\n";
> > > > +                       }
> > > > +
> > > > +               print "</table>\n";
> > > > +
> > > > +       # Section to add new elements or edit existing ones.
> > > > +       print <<END;
> > > > +       <br>
> > > > +       <hr>
> > > > +       <br>
> > > > +       <div align='center'>
> > > > +               <table width='100%' cellspacing='0' border='0'>
> > > > +END
> > > > +
> > > > +       # Assign correct headline and button text.
> > > > +       my $buttontext;
> > > > +       my $entry_interface;
> > > > +       my $entry_protocol;
> > > > +       my $entry_port;
> > > > +       my $entry_address;
> > > > +       my $entry_remark;
> > > > +
> > > > +       # Check if an ID (key) has been given, in this case an
> > > > existing entry should be edited.
> > > > +       if ($settings{'ID'} ne '') {
> > > > +               $buttontext = $Lang::tr{'update'};
> > > > +               print "<tr><td class='boldbase'
> > > > colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
> > > > +
> > > > +               # Grab address and remark for the given key.
> > > > +               $entry_interface =
> > > > $redirects{$settings{'ID'}}[0];
> > > > +               $entry_protocol =
> > > > $redirects{$settings{'ID'}}[1];
> > > > +               $entry_port = $redirects{$settings{'ID'}}[2];
> > > > +               $entry_address =
> > > > $redirects{$settings{'ID'}}[3];
> > > > +               $entry_remark = $redirects{$settings{'ID'}}[5];
> > > > +
> > > > +       } else {
> > > > +               $buttontext = $Lang::tr{'add'};
> > > > +               print "<tr><td class='boldbase'
> > > > colspan='11'><b>$Lang::tr{'dnsforward add a new
> > > > entry'}</b></td></tr>\n";
> > > > +               print "<tr><td>&nbsp</td></tr>\n";
> > > > +       }
> > > > +
> > > > +       print <<END;
> > > > +                       <tr>
> > > > +                               <td class='base' width='1%' 
> > > > bgcolor='$color{'color22'}'></td>
> > > > +                               <td class='base' width='15%'
> > > > bgcolor='$color{'color22'}'
> > > > align='left'><b>$Lang::tr{'interface'}</b></td>
> > > > +                               <td class='base' width='10%'
> > > > bgcolor='$color{'color22'}'
> > > > align='left'><b>$Lang::tr{'protocol'}</b></td>
> > > > +                               <td class='base' width='10%'
> > > > bgcolor='$color{'color22'}'
> > > > align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
> > > > +                               <td class='base' width='13%'
> > > > bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'ip
> > > > address'}</b></td>
> > > > +                               <td class='base' width='30%'
> > > > bgcolor='$color{'color22'}'
> > > > align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
> > > > +                               <td class='base' width='15%'
> > > > bgcolor='$color{'color22'}'></td>
> > > > +                               <td class='base' width='1%' 
> > > > bgcolor='$color{'color22'}'></td>
> > > > +                       </tr>
> > > > +
> > > > +                       <form method='post'
> > > > action='$ENV{'SCRIPT_NAME'}'>
> > > > +                       <input type='hidden' name='ID'
> > > > value='$settings{'ID'}'>
> > > > +                       <tr>
> > > > +                               <td class='base'></td>
> > > > +                               <td><select style='width:90px;'
> > > > id='interface' name='REDIR_ENTRY_INTERFACE'>
> > > > +END
> > > > +                               if ($netsettings{'GREEN_DEV'})
> > > > {
> > > > +                                       if ($entry_interface eq
> > > > "green") {
> > > > +                                               print "<option
> > > > value='green' selected='selected'
> > > > {'green'}>$Lang::tr{'green'}</option>";
> > > > +                                       } else { print "<option
> > > > value='green' {'green'}>$Lang::tr{'green'}</option>";}
> > > > +                               }
> > > > +                               if ($netsettings{'BLUE_DEV'}) {
> > > > +                                       if ($entry_interface eq
> > > > "blue") { 
> > > > +                                               print "<option
> > > > value='blue' selected='selected'
> > > > {'blue'}>$Lang::tr{'blue'}</option>";
> > > > +                                       } else { print "<option
> > > > value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
> > > > +                               }
> > > > +                               if ($netsettings{'ORANGE_DEV'})
> > > > {
> > > > +                                       if ($entry_interface eq
> > > > "orange") { 
> > > > +                                               print "<option
> > > > value='orange' selected='selected'
> > > > {'orange'}>$Lang::tr{'orange'}</option>";
> > > > +                                       } else { print "<option
> > > > value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
> > > > +                               }
> > > > +
> > > > +                       print "</select><td><select
> > > > style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
> > > > +                       if ((!$entry_protocol) ||
> > > > ($entry_protocol eq "tcp")) {
> > > > +                               print "<option
> > > > selected='selected' id='protocol' value='tcp'
> > > > {'tcp'}>tcp</option>";
> > > > +                               print "<option value='udp'
> > > > {'udp'}>udp</option>";
> > > > +                       } elsif ($entry_protocol eq "udp") {
> > > > +                               print "<option value='tcp'
> > > > {'tcp'}>tcp</option>";
> > > > +                               print "<option
> > > > selected='selected' value='udp' {'udp'}>udp</option>";
> > > > +                       }
> > > > +       print <<END;
> > > > +                               </select></td>
> > > > +                               <td><input type='text'
> > > > name='REDIR_ENTRY_PORT'    value='$entry_port'   
> > > > size='4'></td>
> > > > +                               <td><input type='text'
> > > > name='REDIR_ENTRY_ADDRESS' value='$entry_address'
> > > > size='14'></td>
> > > > +                               <td><input type='text'
> > > > name='REDIR_ENTRY_REMARK'  value='$entry_remark' 
> > > > size='35'></td>
> > > > +                               <td width='10%'
> > > > align='center'><input type='submit' name='ACTION' value=' 
> > > > $buttontext  '></td>
> > > > +                               <td class='base'></td>
> > > > +                       </tr>
> > > > +                       </form>
> > > > +               </table>
> > > > +       </div>
> > > > +END
> > > > +       &Header::closebox();
> > > > +}
> > > > diff --git a/config/rootfiles/common/misc-progs
> > > > b/config/rootfiles/common/misc-progs
> > > > index d6594b3f8..fbad2af8b 100644
> > > > --- a/config/rootfiles/common/misc-progs
> > > > +++ b/config/rootfiles/common/misc-progs
> > > > @@ -17,6 +17,7 @@ usr/local/bin/logwatch
> > > > #usr/local/bin/mpfirectrl
> > > > usr/local/bin/openvpnctrl
> > > > usr/local/bin/pakfire
> > > > +#usr/local/bin/portredirctrl
> > > > usr/local/bin/qosctrl
> > > > usr/local/bin/rebuildhosts
> > > > usr/local/bin/rebuildroutes
> > > > diff --git a/config/rootfiles/packages/portredir
> > > > b/config/rootfiles/packages/portredir
> > > > new file mode 100644
> > > > index 000000000..4b4ba8366
> > > > --- /dev/null
> > > > +++ b/config/rootfiles/packages/portredir
> > > > @@ -0,0 +1,11 @@
> > > > +etc/rc.d/init.d/portredir
> > > > +etc/rc.d/rc0.d/K77portredir
> > > > +etc/rc.d/rc3.d/S23portredir
> > > > +etc/rc.d/rc6.d/K77portredir
> > > > +srv/web/ipfire/cgi-bin/portredir.cgi
> > > > +usr/local/bin/portredirctrl
> > > > +var/ipfire/addon-lang/portredir.de.pl
> > > > +var/ipfire/addon-lang/portredir.en.pl
> > > > +var/ipfire/backup/addons/includes/portredir
> > > > +var/ipfire/menu.d/EX-portredir.menu
> > > > +var/ipfire/portredir
> > > > diff --git a/lfs/portredir b/lfs/portredir
> > > > new file mode 100644
> > > > index 000000000..a4911f71f
> > > > --- /dev/null
> > > > +++ b/lfs/portredir
> > > > @@ -0,0 +1,85 @@
> > > > +##############################################################
> > > > #################
> > > > +#                                                             
> > > >                 #
> > > > +# IPFire.org - A linux based
> > > > firewall                                         #
> > > > +# Copyright (C) 2007-2021  IPFire Team 
> > > > <info@ipfire.org>                     #
> > > > +#                                                             
> > > >                 #
> > > > +# This program is free software: you can redistribute it
> > > > and/or modify        #
> > > > +# it under the terms of the GNU General Public License as
> > > > published by        #
> > > > +# the Free Software Foundation, either version 3 of the
> > > > License, or           #
> > > > +# (at your option) any later
> > > > version.                                         #
> > > > +#                                                             
> > > >                 #
> > > > +# This program is distributed in the hope that it will be
> > > > useful,             #
> > > > +# but WITHOUT ANY WARRANTY; without even the implied warranty
> > > > of              #
> > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
> > > > the               #
> > > > +# GNU General Public License for more
> > > > details.                                #
> > > > +#                                                             
> > > >                 #
> > > > +# You should have received a copy of the GNU General Public
> > > > License           #
> > > > +# along with this program.  If not, see <
> > > > http://www.gnu.org/licenses/>.       #
> > > > +#                                                             
> > > >                 #
> > > > +##############################################################
> > > > #################
> > > > +
> > > > +##############################################################
> > > > #################
> > > > +# Definitions
> > > > +##############################################################
> > > > #################
> > > > +
> > > > +include Config
> > > > +
> > > > +VER        = 1.0
> > > > +
> > > > +THISAPP    = portredir-$(VER)
> > > > +DIR_APP    = $(DIR_SRC)/$(THISAPP)
> > > > +TARGET     = $(DIR_INFO)/$(THISAPP)
> > > > +PROG       = portredir
> > > > +PAK_VER    = 1
> > > > +
> > > > +##############################################################
> > > > #################
> > > > +# Top-level Rules
> > > > +##############################################################
> > > > #################
> > > > +
> > > > +install : $(TARGET)
> > > > +
> > > > +check :
> > > > +
> > > > +download :
> > > > +
> > > > +md5 :
> > > > +
> > > > +dist: 
> > > > +       @$(PAK)
> > > > +
> > > > +##############################################################
> > > > #################
> > > > +# Installation Details
> > > > +##############################################################
> > > > #################
> > > > +
> > > > +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
> > > > +       @$(PREBUILD)
> > > > +       @rm -rf $(DIR_APP) && cd $(DIR_SRC)
> > > > +
> > > > +       #install cgi 
> > > > +       install -v -m 755 $(DIR_CONF)/portredir/portredir.cgi
> > > > /srv/web/ipfire/cgi-bin/
> > > > +
> > > > +       #create configuration dir 
> > > > +       -mkdir -pv /var/ipfire/portredir/
> > > > +       chown -R nobody:nobody /var/ipfire/portredir/
> > > > +
> > > > +       # Install include file for backup
> > > > +       install -v -m 644 $(DIR_CONF)/portredir/portredir-
> > > > backup /var/ipfire/backup/addons/includes/portredir
> > > > +
> > > > +       # Install menu file
> > > > +       install -v -m 644 $(DIR_CONF)/portredir/EX-
> > > > portredir.menu /var/ipfire/menu.d/
> > > > +       chown nobody:nobody /var/ipfire/menu.d/EX-
> > > > portredir.menu
> > > > +
> > > > +       # Install addon-specific language-files
> > > > +       install -v -m 644
> > > > $(DIR_CONF)/portredir/lang/portredir.*.pl /var/ipfire/addon-
> > > > lang/
> > > > +
> > > > +       #install initscripts
> > > > +       $(call INSTALL_INITSCRIPT,portredir)
> > > > +
> > > > +       # Create symlinks for runlevel interaction.
> > > > +       ln -svf /etc/rc.d/init.d/portredir
> > > > /etc/rc.d/rc3.d/S23portredir
> > > > +       ln -svf /etc/rc.d/init.d/portredir
> > > > /etc/rc.d/rc0.d/K77portredir
> > > > +       ln -svf /etc/rc.d/init.d/portredir
> > > > /etc/rc.d/rc6.d/K77portredir
> > > > +
> > > > +       @rm -rf $(DIR_APP)
> > > > +       @$(POSTBUILD)
> > > > +
> > > > diff --git a/make.sh b/make.sh
> > > > index fc03ebcd5..ab9fe881a 100755
> > > > --- a/make.sh
> > > > +++ b/make.sh
> > > > @@ -1623,6 +1623,7 @@ buildipfire() {
> > > >  lfsmake2 socat
> > > >  lfsmake2 libcdada
> > > >  lfsmake2 pmacct
> > > > +  lfsmake2 portredir
> > > > }
> > > > 
> > > > buildinstaller() {
> > > > diff --git a/src/initscripts/packages/portredir
> > > > b/src/initscripts/packages/portredir
> > > > new file mode 100644
> > > > index 000000000..cc57fb9cc
> > > > --- /dev/null
> > > > +++ b/src/initscripts/packages/portredir
> > > > @@ -0,0 +1,191 @@
> > > > +#!/bin/sh
> > > > +##############################################################
> > > > ##########
> > > > +# Begin $rc_base/init.d/portredir
> > > > +#
> > > > +# Description : portredir init script for DNS/NTP and custom 
> > > > +#              port redirection rules
> > > > +#
> > > > +##############################################################
> > > > ##########
> > > > +
> > > > +. /etc/sysconfig/rc
> > > > +. ${rc_functions}
> > > > +
> > > > +IPT="/sbin/iptables";
> > > > +parent_chain="PREROUTING";
> > > > +chain="PORT_REDIRECT";
> > > > +
> > > > +confdir="/var/ipfire/portredir";
> > > > +settingsfile="${confdir}/settings";
> > > > +redirectsfile="${confdir}/redirects";
> > > > +SYSLOG="NO";
> > > > +VERBOSE="NO";
> > > > +
> > > > +eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings);
> > > > +eval $(/usr/local/bin/readhash ${settingsfile});
> > > > +
> > > > +logtext() {
> > > > +       if [ "${SYSLOG}" = "YES" ]; then logger -t "portredir"
> > > > ${1}; fi;
> > > > +       if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
> > > > +
> > > > +create_chain() {
> > > > +
> > > > +       local line=$(${IPT} -t nat -L ${parent_chain} --line-
> > > > numbers |grep "SQUID" |awk '{printf($1)}');
> > > > +
> > > > +       if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z
> > > > "${REDIR_ENABLE_ADDON}" ]]; then
> > > > +               logtext "addon not enabled in web
> > > > interface...";
> > > > +               echo "Portredir addon not enabled in web
> > > > interface...";
> > > > +               exit 0;
> > > > +       fi;
> > > > +
> > > > +       if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep
> > > > ${chain})" ]; then
> > > > +               ${IPT} -t nat -N ${chain};
> > > > +
> > > > +               if [ ! -z "${line}" ]; then
> > > > +                       logtext "create chain ${chain} and link
> > > > in ${parent_chain} at position ${line}...";
> > > > +                       ${IPT} -t nat -I ${parent_chain}
> > > > ${line} -j ${chain};
> > > > +               else
> > > > +                       logtext "create chain ${chain} and link
> > > > in ${parent_chain} at last position...";
> > > > +                       ${IPT} -t nat -A ${parent_chain} -j
> > > > ${chain};
> > > > +               fi
> > > > +       else
> > > > +               return 1;
> > > > +       fi;
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +remove_chain() {
> > > > +       if [ ! -z "$(${IPT} -t nat -L ${parent_chain} |grep
> > > > ${chain})" ]; then
> > > > +               logtext "remove chain ${chain} and link in
> > > > ${parent_chain} from system...";
> > > > +               ${IPT} -t nat -D "${parent_chain}" -j ${chain};
> > > > +               ${IPT} -t nat -F ${chain};
> > > > +               ${IPT} -t nat -X ${chain};
> > > > +       else
> > > > +               return 1;
> > > > +       fi;
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +activate_custom_redirections() {
> > > > +       
> > > > +       local array=();
> > > > +       local redirects=();
> > > > +       local i;
> > > > +       index=();
> > > > +       iface=();
> > > > +       protocol=();
> > > > +       port=();
> > > > +       targetip=();
> > > > +       enabled=();
> > > > +
> > > > +       IFS=$'\n' read -d '' -ra redirects < ${redirectsfile};
> > > > +
> > > > +       for i in "${!redirects[@]}"
> > > > +       do
> > > > +               IFS=$',' read -ra array <<< ${redirects[i]};
> > > > +               index[i]=${array[0]};
> > > > +               iface[i]=${array[1]};
> > > > +               protocol[i]=${array[2]};
> > > > +               port[i]=${array[3]};
> > > > +               targetip[i]=${array[4]};
> > > > +               enabled[i]=${array[5]};
> > > > +       done
> > > > +
> > > > +       for i in "${!index[@]}"
> > > > +       do
> > > > +               if [[ ! -z "${GREEN_DEV}" &&  "${iface[i]}" =
> > > > "green" && "${enabled[i]}" = "enabled" ]]; then
> > > > +
> > > > +                       logtext "add redirect in ${chain} on
> > > > ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
> > > > ${port[i]} ";
> > > > +                       ${IPT} -t nat -A ${chain} -i
> > > > ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
> > > > ${protocol[i]} --dport ${port[i]} -j RETURN;
> > > > +                       ${IPT} -t nat -A ${chain} -i
> > > > ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
> > > > ${port[i]} -j REDIRECT;
> > > > +               fi
> > > > +               if [[ ! -z "${BLUE_DEV}" &&  "${iface[i]}" =
> > > > "blue" && "${enabled[i]}" = "enabled" ]]; then
> > > > +                       logtext "add redirect in ${chain} on
> > > > ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
> > > > ${port[i]} ";
> > > > +                       ${IPT} -t nat -A ${chain} -i
> > > > ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
> > > > ${protocol[i]} --dport ${port[i]} -j RETURN;
> > > > +                       ${IPT} -t nat -A ${chain} -i
> > > > ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
> > > > ${port[i]} -j REDIRECT;
> > > > +               fi
> > > > +               if [[ ! -z "${ORANGE_DEV}" &&  "${iface[i]}" =
> > > > "orange" && "${enabled[i]}" = "enabled" ]]; then
> > > > +                       logtext "add redirect in ${chain} on
> > > > ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
> > > > ${port[i]} ";
> > > > +                       ${IPT} -t nat -A ${chain} -i
> > > > ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
> > > > ${protocol[i]} --dport ${port[i]} -j RETURN;
> > > > +                       ${IPT} -t nat -A ${chain} -i
> > > > ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
> > > > ${port[i]} -j REDIRECT;
> > > > +               fi
> > > > +       done
> > > > +       unset array redirects i index iface protocol port
> > > > targetip enabled;
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +activate_redirections() {
> > > > +
> > > > +       if ! create_chain; then return 1; fi;
> > > > +       
> > > > +       # Force DNS REDIRECTs on GREEN (udp, tcp, 53)
> > > > +       if [[ "${REDIR_DNS_GREEN}" == "on" && 
> > > > "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
> > > > +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
> > > > ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
> > > > +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
> > > > udp -m udp --dport domain -j REDIRECT;
> > > > +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
> > > > ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
> > > > +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
> > > > tcp -m tcp --dport domain -j REDIRECT;
> > > > +       fi
> > > > +
> > > > +       # Force DNS REDIRECTs on BLUE (udp, tcp, 53)
> > > > +       if [[ "${REDIR_DNS_BLUE}" == "on" && 
> > > > "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
> > > > +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
> > > > ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
> > > > +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
> > > > -m udp --dport domain -j REDIRECT
> > > > +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
> > > > ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
> > > > +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p tcp
> > > > -m tcp --dport domain -j REDIRECT
> > > > +       fi
> > > > +
> > > > +       # Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
> > > > +       if [[ "${REDIR_DNS_ORANGE}" == "on" && 
> > > > "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
> > > > +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
> > > > ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
> > > > +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
> > > > udp -m udp --dport domain -j REDIRECT
> > > > +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
> > > > ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
> > > > +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
> > > > tcp -m tcp --dport domain -j REDIRECT
> > > > +       fi
> > > > +
> > > > +       # Force NTP REDIRECTs on GREEN (udp, 123)
> > > > +       if [[ "${REDIR_NTP_GREEN}" == "on" && 
> > > > "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
> > > > +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
> > > > ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
> > > > +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
> > > > udp -m udp --dport ntp -j REDIRECT
> > > > +       fi
> > > > +
> > > > +       # Force NTP REDIRECTs on BLUE (udp, 123)
> > > > +       if [[ "${REDIR_NTP_BLUE}" == "on" && 
> > > > "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
> > > > +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
> > > > ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
> > > > +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
> > > > -m udp --dport ntp -j REDIRECT
> > > > +       fi
> > > > +
> > > > +       # Force NTP REDIRECTs on ORANGE (udp, 123)
> > > > +       if [[ "${REDIR_NTP_ORANGE}" == "on" && 
> > > > "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
> > > > +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
> > > > ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
> > > > +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
> > > > udp -m udp --dport ntp -j REDIRECT
> > > > +       fi
> > > > +
> > > > +       if ! activate_custom_redirections; then return 1; fi;
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +case "${1}" in
> > > > +       start)
> > > > +               boot_mesg "Loading port redirections..."
> > > > +               activate_redirections;
> > > > +               evaluate_retval;
> > > > +               ;;
> > > > +
> > > > +       stop)   
> > > > +               boot_mesg "Removing port redirections..."
> > > > +               remove_chain;
> > > > +               evaluate_retval;
> > > > +               ;;
> > > > +
> > > > +       restart)
> > > > +               ${0} stop
> > > > +               ${0} start
> > > > +               ;;
> > > > +
> > > > +       *)
> > > > +               echo "Usage: ${0} {start|stop|restart}"
> > > > +               exit 1
> > > > +               ;;
> > > > +esac
> > > > +
> > > > +# End $rc_base/init.d/portredir
> > > > diff --git a/src/misc-progs/Makefile b/src/misc-progs/Makefile
> > > > index 7c3ef7529..850f8fdcc 100644
> > > > --- a/src/misc-progs/Makefile
> > > > +++ b/src/misc-progs/Makefile
> > > > @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
> > > >         wirelessctrl getipstat qosctrl \
> > > >         redctrl syslogdctrl extrahdctrl sambactrl \
> > > >         smartctrl clamavctrl addonctrl pakfire mpfirectrl
> > > > wlanapctrl \
> > > > -       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
> > > > rebuildroutes \
> > > > +       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
> > > > rebuildroutes portredirctrl \
> > > >         getconntracktable wirelessclient torctrl ddnsctrl
> > > > unboundctrl \
> > > >         captivectrl
> > > > 
> > > > diff --git a/src/misc-progs/portredirctrl.c b/src/misc-
> > > > progs/portredirctrl.c
> > > > new file mode 100644
> > > > index 000000000..7897d711c
> > > > --- /dev/null
> > > > +++ b/src/misc-progs/portredirctrl.c
> > > > @@ -0,0 +1,47 @@
> > > > +/* This file is part of the IPFire Firewall.
> > > > + *
> > > > + * This program is distributed under the terms of the GNU
> > > > General Public
> > > > + * Licence.  See the file COPYING for details.
> > > > + *
> > > > + */
> > > > +
> > > > +#include <stdlib.h>
> > > > +#include <stdio.h>
> > > > +#include <string.h>
> > > > +#include <unistd.h>
> > > > +#include <sys/types.h>
> > > > +#include <fcntl.h>
> > > > +#include "setuid.h"
> > > > +
> > > > +int main(int argc, char *argv[]) {
> > > > +       if (!(initsetuid()))
> > > > +               exit(1);
> > > > +
> > > > +       // Check what command is asked
> > > > +        if (argc < 2) {
> > > > +                fprintf(stderr, "\nNo argument
> > > > given.\n\nportredirctrl
> > > > (start|stop|restart|enable|disable)\n\n");
> > > > +                exit(1);
> > > > +        }
> > > > +
> > > > +        if (strcmp(argv[1], "start") == 0) {
> > > > +                safe_system("/etc/rc.d/init.d/portredir
> > > > start");
> > > > +        } else if (strcmp(argv[1], "stop") == 0) {
> > > > +                safe_system("/etc/rc.d/init.d/portredir
> > > > stop");
> > > > +        } else if (strcmp(argv[1], "restart") == 0) {
> > > > +                safe_system("/etc/rc.d/init.d/portredir
> > > > restart");
> > > > +       } else if (strcmp(argv[1], "enable") == 0) {
> > > > +               safe_system("touch
> > > > /var/ipfire/portredir/enable");
> > > > +               safe_system("ln -snf /etc/rc.d/init.d/portredir
> > > > /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
> > > > +               safe_system("ln -snf /etc/rc.d/init.d/portredir
> > > > /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
> > > > +               safe_system("ln -snf /etc/rc.d/init.d/portredir
> > > > /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
> > > > +       } else if (strcmp(argv[1], "disable") == 0) {
> > > > +               safe_system("/etc/rc.d/init.d/portredir stop");
> > > > +               safe_system("unlink
> > > > /var/ipfire/portredir/enable");
> > > > +               safe_system("rm -rf /etc/rc.d/rc*.d/*portredir
> > > > >/dev/null 2>&1");
> > > > +        } else {
> > > > +                fprintf(stderr, "\nBad argument
> > > > given.\n\nportredirctrl
> > > > (start|stop|restart|enable|disable)\n\n");
> > > > +                exit(1);
> > > > +        }
> > > > +
> > > > +       return 0;
> > > > +}
> > > > diff --git a/src/paks/portredir/install.sh
> > > > b/src/paks/portredir/install.sh
> > > > new file mode 100644
> > > > index 000000000..9f69aeae2
> > > > --- /dev/null
> > > > +++ b/src/paks/portredir/install.sh
> > > > @@ -0,0 +1,32 @@
> > > > +#!/bin/bash
> > > > +##############################################################
> > > > ##############
> > > > +#                                                             
> > > >              #
> > > > +# This file is part of the IPFire
> > > > Firewall.                                #
> > > > +#                                                             
> > > >              #
> > > > +# IPFire is free software; you can redistribute it and/or
> > > > modify           #
> > > > +# it under the terms of the GNU General Public License as
> > > > published by     #
> > > > +# the Free Software Foundation; either version 2 of the
> > > > License, or        #
> > > > +# (at your option) any later
> > > > version.                                      #
> > > > +#                                                             
> > > >              #
> > > > +# IPFire is distributed in the hope that it will be
> > > > useful,                #
> > > > +# but WITHOUT ANY WARRANTY; without even the implied warranty
> > > > of           #
> > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
> > > > the            #
> > > > +# GNU General Public License for more
> > > > details.                             #
> > > > +#                                                             
> > > >              #
> > > > +# You should have received a copy of the GNU General Public
> > > > License        #
> > > > +# along with IPFire; if not, write to the Free
> > > > Software                    #
> > > > +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
> > > > 02111-1307 USA #
> > > > +#                                                             
> > > >              #
> > > > +# Copyright (C) 2021 IPFire-Team
> > > > <info@ipfire.org>.                        #
> > > > +#                                                             
> > > >              #
> > > > +##############################################################
> > > > ##############
> > > > +#
> > > > +. /opt/pakfire/lib/functions.sh
> > > > +extract_files
> > > > +restore_backup ${NAME}
> > > > +
> > > > +/usr/local/bin/update-lang-cache
> > > > +
> > > > +chown root:nobody /usr/local/bin/portredirctrl
> > > > +chmod 4750 /usr/local/bin/portredirctrl
> > > > +chmod u+s /usr/local/bin/portredirctrl
> > > > diff --git a/src/paks/portredir/uninstall.sh
> > > > b/src/paks/portredir/uninstall.sh
> > > > new file mode 100644
> > > > index 000000000..df9270125
> > > > --- /dev/null
> > > > +++ b/src/paks/portredir/uninstall.sh
> > > > @@ -0,0 +1,28 @@
> > > > +#!/bin/bash
> > > > +##############################################################
> > > > ##############
> > > > +#                                                             
> > > >              #
> > > > +# This file is part of the IPFire
> > > > Firewall.                                #
> > > > +#                                                             
> > > >              #
> > > > +# IPFire is free software; you can redistribute it and/or
> > > > modify           #
> > > > +# it under the terms of the GNU General Public License as
> > > > published by     #
> > > > +# the Free Software Foundation; either version 2 of the
> > > > License, or        #
> > > > +# (at your option) any later
> > > > version.                                      #
> > > > +#                                                             
> > > >              #
> > > > +# IPFire is distributed in the hope that it will be
> > > > useful,                #
> > > > +# but WITHOUT ANY WARRANTY; without even the implied warranty
> > > > of           #
> > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
> > > > the            #
> > > > +# GNU General Public License for more
> > > > details.                             #
> > > > +#                                                             
> > > >              #
> > > > +# You should have received a copy of the GNU General Public
> > > > License        #
> > > > +# along with IPFire; if not, write to the Free
> > > > Software                    #
> > > > +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
> > > > 02111-1307 USA #
> > > > +#                                                             
> > > >              #
> > > > +# Copyright (C) 2007 IPFire-Team
> > > > <info@ipfire.org>.                        #
> > > > +#                                                             
> > > >              #
> > > > +##############################################################
> > > > ##############
> > > > +#
> > > > +. /opt/pakfire/lib/functions.sh
> > > > +make_backup ${NAME}
> > > > +remove_files
> > > > +
> > > > +/usr/local/bin/update-lang-cache
> > > > diff --git a/src/paks/portredir/update.sh
> > > > b/src/paks/portredir/update.sh
> > > > new file mode 100644
> > > > index 000000000..89c40d0d7
> > > > --- /dev/null
> > > > +++ b/src/paks/portredir/update.sh
> > > > @@ -0,0 +1,26 @@
> > > > +#!/bin/bash
> > > > +##############################################################
> > > > ##############
> > > > +#                                                             
> > > >              #
> > > > +# This file is part of the IPFire
> > > > Firewall.                                #
> > > > +#                                                             
> > > >              #
> > > > +# IPFire is free software; you can redistribute it and/or
> > > > modify           #
> > > > +# it under the terms of the GNU General Public License as
> > > > published by     #
> > > > +# the Free Software Foundation; either version 2 of the
> > > > License, or        #
> > > > +# (at your option) any later
> > > > version.                                      #
> > > > +#                                                             
> > > >              #
> > > > +# IPFire is distributed in the hope that it will be
> > > > useful,                #
> > > > +# but WITHOUT ANY WARRANTY; without even the implied warranty
> > > > of           #
> > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
> > > > the            #
> > > > +# GNU General Public License for more
> > > > details.                             #
> > > > +#                                                             
> > > >              #
> > > > +# You should have received a copy of the GNU General Public
> > > > License        #
> > > > +# along with IPFire; if not, write to the Free
> > > > Software                    #
> > > > +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
> > > > 02111-1307 USA #
> > > > +#                                                             
> > > >              #
> > > > +# Copyright (C) 2007 IPFire-Team
> > > > <info@ipfire.org>.                        #
> > > > +#                                                             
> > > >              #
> > > > +##############################################################
> > > > ##############
> > > > +#
> > > > +. /opt/pakfire/lib/functions.sh
> > > > +./uninstall.sh
> > > > +./install.sh
> > > > -- 
> > > > 2.18.0
> > > > 
> > > 
> > 
>
  
Jon Murphy July 1, 2021, 3:08 a.m. UTC | #5
Hi Stefan,

Thank you for taking this on!

I applied the patchwork.ipfire patch.

I think I entered something wrong since I cannot get things to work.  I tried both with Destination Firewall GREEN & Firewall RED.


Does the Firewall Rule seem right?

Best regards,
Jon


Here is the rule I set up:







And this is what I see with conntrack:

conntrack -E -e NEW,UPDATE | grep -e "=53 "

    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=51169 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=51169
    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=54168 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=54168
    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=56094 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=56094
    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=52964 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=52964
    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=53279 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=53279
    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=61657 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=61657
    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=57723 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=57723




> On Jun 30, 2021, at 2:14 PM, Stefan Schantl <stefan.schantl@ipfire.org> wrote:
> 
> Hello Matthias, Hello Michael, Hello Jon, Hello *,
> 
> I've followed the conversation on this list since the first mail and
> thoughts about forcing DNS traffic to use the local resolver.
> 
> It was a very long journey and lot of time and work has been spent to
> get to the present point.
> 
> As Michael requested here, I've digged through the lines of the perl
> script which is responsible for creating the firewall rules and
> surprisingly found that everyting which is needed to create generic
> REDIRECT rules already was written in the past - it just did not work
> as designed/expected.
> 
> Finaly I was able to adjust these lines of code and to repair that
> feature.
> 
> A redirect rule can be created by picking a single host or group of
> hosts or entire network(s) as source, selecting NAT (DNAT) and choosing
> the Firewall itself as target.
> 
> The protocol or service or service group which should be redirected has
> to be selected afterwards. If you want to redirect a given port to
> another one it can be specified as "Target port".
> 
> All created redirect rules are displayed as "input rules".
> 
> 
> The patch directly can be accessed here:
> 
> https://patchwork.ipfire.org/project/ipfire/patch/20210630184031.7726-1-stefan.schantl@ipfire.org/
> 
> Best regards,
> 
> -Stefan
> 
>> Hello,
>> 
>>> On 28 Jun 2021, at 18:53, Jon Murphy <jcmurphy26@gmail.com> wrote:
>>> 
>>> Hi Michael!  Happy Monday!
>>> 
>>> 
>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>> of code instead of adding many hundreds of lines?
>>>> 
>>>> Please can someone elaborate on this more?
>>> 
>>> Doing a DNS redirect, via the WegBUI, has been an issue since
>>> 2015.  I found this quote in the old forum:
>>> 
>>> "Having investigated a bit more I have concluded that it's not
>>> currently possible to create such rules through the WUI.
>>> 
>>> There are a number of obstacles:
>>> 1. It is not allowed to create a rule where source IP and
>>> destination nat IP is on the same subnetwork (e.g. GREEN), WUI
>>> error message: "Source and destination IP addresses are from the
>>> same subnet."
>>> 
>>> 2. WUI will not allow you to create a rule without a destination
>>> (the filtered packet must adhere to a destination, not only a port)
>>> and the destination MUST be an IP address of one of the IPFire
>>> interfaces, which limits whats possible a great deal." 
>> 
>> And these cannot be changed?
>> 
>>> And I found this from 2016:
>>> https://bugzilla.ipfire.org/show_bug.cgi?id=11168
>>> 
>>> So I am guessing that no one has been able to determine a way to
>>> extend the WebGUI.  
>> 
>> Has anyone tried? I do not see any obvious reasons why this should
>> not be possible.
>> 
>>> I am curious - Who created the 
>>> https://ipfire:444/cgi-bin/firewall.cgi page?  And could they help?
>> 
>> -Michael
>> 
>>> Jon
>>> 
>>> 
>>>> On Jun 28, 2021, at 11:04 AM, Michael Tremer <
>>>> michael.tremer@ipfire.org> wrote:
>>>> 
>>>> Hello Matthias,
>>>> 
>>>>> On 27 Jun 2021, at 14:48, Matthias Fischer <
>>>>> matthias.fischer@ipfire.org> wrote:
>>>>> 
>>>>> From: Marcel Lorenz <marcel.lorenz@ipfire.org>
>>>> 
>>>> Thank you for sending this patch on Marcel’s behalf, but I would
>>>> much more prefer if he would submit his patches on his own. I do
>>>> not see why that isn’t possible.
>>>> 
>>>>> Please note:
>>>>> This is a new addon written by Marcel Lorenz <
>>>>> marcel.lorenz@ipfire.org>.
>>>>> 
>>>>> It adds a new GUI to IPFire for DNS/NTP *and* user specific
>>>>> port redirections.
>>>>> 
>>>>> How its working:
>>>>> It has exactly the same functionalities as "Forcing
>>>>> DNS/NTP..."  - and some more.
>>>>> 
>>>>> By setting switches, DNS/NTP requests are automatically
>>>>> redirected to the local IPFire DNS/NTP servers.
>>>>> 
>>>>> Additionally, the user can specify custom redirections.
>>>>> 
>>>>> These rules are added to a new chain in PREROUTING =>
>>>>> PORT_REDIRECT.
>>>>> 
>>>>> To avoid problems with (e.g.) transparent 'squid'
>>>>> configurations,
>>>>> redirection rules are added automatically before existing
>>>>> 'squid' rules.
>>>> 
>>>> This message does unfortunately not say why this add-on would be
>>>> useful. I am emphasising this again and again that it is not very
>>>> important how something is done specially. That should be
>>>> commented in the code and other implementation details should
>>>> also be documented there.
>>>> 
>>>> As I have stated on this functionality many times before, I do
>>>> not see why this is necessary at all.
>>>> 
>>>> Why is this an add-on?
>>>> 
>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>> of code instead of adding many hundreds of lines?
>>>> 
>>>> Please can someone elaborate on this more?
>>>> 
>>>> -Michael
>>>> 
>>>>> Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
>>>>> ---
>>>>> config/portredir/EX-portredir.menu    |   6 +
>>>>> config/portredir/lang/portredir.de.pl |  19 +
>>>>> config/portredir/lang/portredir.en.pl |  19 +
>>>>> config/portredir/portredir-backup     |   1 +
>>>>> config/portredir/portredir.cgi        | 525
>>>>> ++++++++++++++++++++++++++
>>>>> config/rootfiles/common/misc-progs    |   1 +
>>>>> config/rootfiles/packages/portredir   |  11 +
>>>>> lfs/portredir                         |  85 +++++
>>>>> make.sh                               |   1 +
>>>>> src/initscripts/packages/portredir    | 191 ++++++++++
>>>>> src/misc-progs/Makefile               |   2 +-
>>>>> src/misc-progs/portredirctrl.c        |  47 +++
>>>>> src/paks/portredir/install.sh         |  32 ++
>>>>> src/paks/portredir/uninstall.sh       |  28 ++
>>>>> src/paks/portredir/update.sh          |  26 ++
>>>>> 15 files changed, 993 insertions(+), 1 deletion(-)
>>>>> create mode 100644 config/portredir/EX-portredir.menu
>>>>> create mode 100644 config/portredir/lang/portredir.de.pl
>>>>> create mode 100644 config/portredir/lang/portredir.en.pl
>>>>> create mode 100644 config/portredir/portredir-backup
>>>>> create mode 100644 config/portredir/portredir.cgi
>>>>> create mode 100644 config/rootfiles/packages/portredir
>>>>> create mode 100644 lfs/portredir
>>>>> create mode 100644 src/initscripts/packages/portredir
>>>>> create mode 100644 src/misc-progs/portredirctrl.c
>>>>> create mode 100644 src/paks/portredir/install.sh
>>>>> create mode 100644 src/paks/portredir/uninstall.sh
>>>>> create mode 100644 src/paks/portredir/update.sh
>>>>> 
>>>>> diff --git a/config/portredir/EX-portredir.menu
>>>>> b/config/portredir/EX-portredir.menu
>>>>> new file mode 100644
>>>>> index 000000000..8376e8053
>>>>> --- /dev/null
>>>>> +++ b/config/portredir/EX-portredir.menu
>>>>> @@ -0,0 +1,6 @@
>>>>> +    $subfirewall->{'95.portredir'} = {
>>>>> +                               'caption' =>
>>>>> $Lang::tr{'portredir port redirections'},
>>>>> +                               'uri' => '/cgi-
>>>>> bin/portredir.cgi',
>>>>> +                               'title' =>
>>>>> "$Lang::tr{'portredir port redirections'}",
>>>>> +                               'enabled' => 1
>>>>> +                               };
>>>>> diff --git a/config/portredir/lang/portredir.de.pl
>>>>> b/config/portredir/lang/portredir.de.pl
>>>>> new file mode 100644
>>>>> index 000000000..b932d4a85
>>>>> --- /dev/null
>>>>> +++ b/config/portredir/lang/portredir.de.pl
>>>>> @@ -0,0 +1,19 @@
>>>>> +%tr = (
>>>>> +%tr,
>>>>> +'portredir enable addon' => 'Addon aktivieren',
>>>>> +'portredir common settings' => 'Allgemeine Einstellungen',
>>>>> +'portredir port redirections' => 'Portumleitungen',
>>>>> +'portredir fw for interface' => 'Firewalloptionen für das
>>>>> Interface',
>>>>> +'portredir enable user redirections' => 'Aktiviere
>>>>> benutzerdefinierte Portumleitungen',
>>>>> +'portredir force local dns' => 'Erzwinge lokale DNS-Server',
>>>>> +'portredir force local ntp' => 'Erzwinge lokale NTP-Server',
>>>>> +'portredir custom redirections' => 'Benutzerdefinierte
>>>>> Portumleitungen',
>>>>> +'portredir remove rule' => 'Entferne Regel',
>>>>> +'portredir add rule' => 'Hinzufügen',
>>>>> +'portredir no entries' => 'Keine Einträge vorhanden.',
>>>>> +'portredir invalid address' => 'Ungültige Host-Addresse.',
>>>>> +'portredir empty input' => 'Fehlende Angabe: Bitte geben Sie
>>>>> einen gültigen Host an.',
>>>>> +'portredir save to activate' => 'Speichern, um Änderungen zu
>>>>> aktivieren',
>>>>> +);
>>>>> +
>>>>> +#EOF
>>>>> diff --git a/config/portredir/lang/portredir.en.pl
>>>>> b/config/portredir/lang/portredir.en.pl
>>>>> new file mode 100644
>>>>> index 000000000..f442f3eaa
>>>>> --- /dev/null
>>>>> +++ b/config/portredir/lang/portredir.en.pl
>>>>> @@ -0,0 +1,19 @@
>>>>> +%tr = (
>>>>> +%tr,
>>>>> +'portredir enable addon' => 'Enable addon',
>>>>> +'portredir common settings' => 'Common settings',
>>>>> +'portredir port redirections' => 'Port redirections',
>>>>> +'portredir fw for interface' => 'Firewall options for
>>>>> interface',
>>>>> +'portredir enable user redirections' => 'Enable user port
>>>>> redirections',
>>>>> +'portredir force local dns' => 'Enforce local DNS servers',
>>>>> +'portredir force local ntp' => 'Enforce local NTP servers',
>>>>> +'portredir custom redirections' => 'Custom port redirections',
>>>>> +'portredir remove rule' => 'Remove rule',
>>>>> +'portredir add rule' => 'Add new',
>>>>> +'portredir no entries' => 'No entries at the moment.',
>>>>> +'portredir invalid address' => 'Invalid host address.',
>>>>> +'portredir empty input' => 'Empty input: Please enter a valid
>>>>> host.',
>>>>> +'portredir save to activate' => 'Save to activate changes',
>>>>> +);
>>>>> +
>>>>> +#EOF
>>>>> diff --git a/config/portredir/portredir-backup
>>>>> b/config/portredir/portredir-backup
>>>>> new file mode 100644
>>>>> index 000000000..bd2ada742
>>>>> --- /dev/null
>>>>> +++ b/config/portredir/portredir-backup
>>>>> @@ -0,0 +1 @@
>>>>> +/var/ipfire/portredir
>>>>> diff --git a/config/portredir/portredir.cgi
>>>>> b/config/portredir/portredir.cgi
>>>>> new file mode 100644
>>>>> index 000000000..4913dda3f
>>>>> --- /dev/null
>>>>> +++ b/config/portredir/portredir.cgi
>>>>> @@ -0,0 +1,525 @@
>>>>> +#!/usr/bin/perl
>>>>> +##############################################################
>>>>> #################
>>>>> +#                                                             
>>>>>                 #
>>>>> +# IPFire.org - A linux based
>>>>> firewall                                         #
>>>>> +# Copyright (C) 2021  IPFire Team 
>>>>> <info@ipfire.org>                          #
>>>>> +#                                                             
>>>>>                 #
>>>>> +# This program is free software: you can redistribute it
>>>>> and/or modify        #
>>>>> +# it under the terms of the GNU General Public License as
>>>>> published by        #
>>>>> +# the Free Software Foundation, either version 3 of the
>>>>> License, or           #
>>>>> +# (at your option) any later
>>>>> version.                                         #
>>>>> +#                                                             
>>>>>                 #
>>>>> +# This program is distributed in the hope that it will be
>>>>> useful,             #
>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>> of              #
>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>> the               #
>>>>> +# GNU General Public License for more
>>>>> details.                                #
>>>>> +#                                                             
>>>>>                 #
>>>>> +# You should have received a copy of the GNU General Public
>>>>> License           #
>>>>> +# along with this program.  If not, see <
>>>>> http://www.gnu.org/licenses/>.       #
>>>>> +#                                                             
>>>>>                 #
>>>>> +##############################################################
>>>>> #################
>>>>> +
>>>>> +use strict;
>>>>> +
>>>>> +# enable only the following on debugging purpose
>>>>> +use warnings;
>>>>> +use CGI::Carp 'fatalsToBrowser';
>>>>> +
>>>>> +require '/var/ipfire/general-functions.pl';
>>>>> +require "${General::swroot}/lang.pl";
>>>>> +require "${General::swroot}/header.pl";
>>>>> +
>>>>> +# File declarations
>>>>> +my $settingsfile = "${General::swroot}/portredir/settings";
>>>>> +my $redirectsfile = "${General::swroot}/portredir/redirects";
>>>>> +
>>>>> +# Create empty settingsfiles if they does not exist yet
>>>>> +unless (-e "$settingsfile") { system ("touch $settingsfile");
>>>>> }
>>>>> +unless (-e "$redirectsfile") { system ("touch
>>>>> $redirectsfile"); }
>>>>> +
>>>>> +# load ipfire settings
>>>>> +our %netsettings = ();
>>>>> +our %color = ();
>>>>> +&General::readhash("${General::swroot}/ethernet/settings",
>>>>> \%netsettings);
>>>>> +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include
>>>>> /colors.txt", \%color);
>>>>> +
>>>>> +my %settings=();
>>>>> +my %portredirs=();
>>>>> +my %checked=(); # Checkbox manipulations
>>>>> +my $errormessage='';
>>>>> +my %selected=();
>>>>> +our %redirects=();
>>>>> +
>>>>> +$settings{'ACTION'} = '';
>>>>> +$settings{'REDIR_ENABLE_ADDON'}="off";
>>>>> +$settings{'REDIR_CUSTOM_GREEN'}="off";
>>>>> +$settings{'REDIR_CUSTOM_BLUE'}="off";
>>>>> +$settings{'REDIR_CUSTOM_ORANGE'}="off";
>>>>> +$settings{'REDIR_DNS_GREEN'}="off";
>>>>> +$settings{'REDIR_NTP_GREEN'}="off";
>>>>> +$settings{'REDIR_DNS_BLUE'}="off";
>>>>> +$settings{'REDIR_NTP_BLUE'}="off";
>>>>> +$settings{'REDIR_DNS_ORANGE'}="off";
>>>>> +$settings{'REDIR_NTP_ORANGE'}="off";
>>>>> +
>>>>> +&Header::showhttpheaders();
>>>>> +
>>>>> +# Get GUI values
>>>>> +&Header::getcgihash(\%settings);
>>>>> +
>>>>> +# Save action
>>>>> +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
>>>>> +
>>>>> +       # If custom rules enabled, deactivate default rules on
>>>>> interface
>>>>> +       if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
>>>>> +               $settings{'REDIR_DNS_GREEN'}="off";
>>>>> +               $settings{'REDIR_NTP_GREEN'}="off";
>>>>> +       }
>>>>> +
>>>>> +       if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
>>>>> +               $settings{'REDIR_DNS_BLUE'}="off";
>>>>> +               $settings{'REDIR_NTP_BLUE'}="off";
>>>>> +       }
>>>>> +
>>>>> +       if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
>>>>> +               $settings{'REDIR_DNS_ORANGE'}="off";
>>>>> +               $settings{'REDIR_NTP_ORANGE'}="off";
>>>>> +       }
>>>>> +
>>>>> +       &General::writehash($settingsfile, \%settings);
>>>>> +
>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
>>>>> +               system ('/usr/local/bin/portredirctrl restart
>>>>>> /dev/null 2>&1');
>>>>> +               system ('/usr/local/bin/portredirctrl enable
>>>>>> /dev/null 2>&1');
>>>>> +               &General::log('portredir addon: port
>>>>> redirections enabled');
>>>>> +       }
>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
>>>>> +               system ('/usr/local/bin/portredirctrl disable
>>>>>> /dev/null 2>&1');
>>>>> +               system ('/usr/local/bin/portredirctrl stop
>>>>>> /dev/null 2>&1');
>>>>> +               &General::log('portredir addon: port
>>>>> redirections disabled');
>>>>> +       }
>>>>> +
>>>>> +# Add/edit an entry to the redirectsfile.
>>>>> +
>>>>> +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) ||
>>>>> ($settings{'ACTION'} eq $Lang::tr{'update'})) {
>>>>> +
>>>>> +       # Check if any input has been performed.
>>>>> +       if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
>>>>> +
>>>>> +               # Check if the given input is no valid IP-
>>>>> address, display an error message.
>>>>> +               if
>>>>> (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
>>>>> +                       $errormessage = "$Lang::tr{'portredir
>>>>> invalid address'}";
>>>>> +               }
>>>>> +       } else {
>>>>> +               $errormessage = "$Lang::tr{'portredir empty
>>>>> input'}";
>>>>> +       }
>>>>> +
>>>>> +       # Go further if there was no error.
>>>>> +       if ($errormessage eq '') {
>>>>> +               my %redirects = ();
>>>>> +               my $id;
>>>>> +               my $status;
>>>>> +
>>>>> +               # Assign hash values.
>>>>> +               my $new_entry_interface =
>>>>> $settings{'REDIR_ENTRY_INTERFACE'};
>>>>> +               my $new_entry_protocol =
>>>>> $settings{'REDIR_ENTRY_PROTOCOL'};
>>>>> +               my $new_entry_port =
>>>>> $settings{'REDIR_ENTRY_PORT'};
>>>>> +               my $new_entry_address =
>>>>> $settings{'REDIR_ENTRY_ADDRESS'};
>>>>> +               my $new_entry_remark =
>>>>> $settings{'REDIR_ENTRY_REMARK'};
>>>>> +
>>>>> +               # Read-in redirectsfile.
>>>>> +               &General::readhasharray($redirectsfile,
>>>>> \%redirects);
>>>>> +
>>>>> +               # Check if we should edit an existing entry and
>>>>> got an ID.
>>>>> +               if (($settings{'ACTION'} eq
>>>>> $Lang::tr{'update'}) && ($settings{'ID'})) {
>>>>> +                       # Assin the provided id.
>>>>> +                       $id = $settings{'ID'};
>>>>> +
>>>>> +                       # Undef the given ID.
>>>>> +                       undef($settings{'ID'});
>>>>> +
>>>>> +                       # Grab the configured status of the
>>>>> corresponding entry.
>>>>> +                       $status = $redirects{$id}[4];
>>>>> +               } else {
>>>>> +                       # Each newly added entry automatically
>>>>> should be enabled.
>>>>> +                       $status = "enabled";
>>>>> +
>>>>> +                       # Generate the ID for the new entry.
>>>>> +                       #
>>>>> +                       # Sort the keys by their ID and store
>>>>> them in an array.
>>>>> +                       my @keys = sort { $a <=> $b } keys
>>>>> %redirects;
>>>>> +
>>>>> +                       # Reverse the key array.
>>>>> +                       my @reversed = reverse(@keys);
>>>>> +
>>>>> +                       # Obtain the last used id.
>>>>> +                       my $last_id = @reversed[0];
>>>>> +
>>>>> +                       # Increase the last id by one and use
>>>>> it as id for the new entry.
>>>>> +                       $id = ++$last_id;
>>>>> +               }
>>>>> +
>>>>> +               # Add/Modify the entry to/in the redirects
>>>>> hash.
>>>>> +               $redirects{$id} = ["$new_entry_interface",
>>>>> "$new_entry_protocol", "$new_entry_port",
>>>>> "$new_entry_address","$status", "$new_entry_remark"];
>>>>> +
>>>>> +               # Write the changed redirects hash to the
>>>>> redirects file.
>>>>> +               &General::writehasharray($redirectsfile,
>>>>> \%redirects);
>>>>> +       }
>>>>> +
>>>>> +# Toggle Enabled/Disabled for an existing entry on the
>>>>> redirects list.
>>>>> +
>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable
>>>>> disable'}) {
>>>>> +       my %redirects = ();
>>>>> +
>>>>> +       # Only go further, if an ID has been passed.
>>>>> +       if ($settings{'ID'}) {
>>>>> +               # Assign the given ID.
>>>>> +               my $id = $settings{'ID'};
>>>>> +
>>>>> +               # Undef the given ID.
>>>>> +               undef($settings{'ID'});
>>>>> +
>>>>> +               # Read-in ignoredfile.
>>>>> +               &General::readhasharray($redirectsfile,
>>>>> \%redirects);
>>>>> +
>>>>> +               # Grab the configured status of the
>>>>> corresponding entry.
>>>>> +               my $status = $redirects{$id}[4];
>>>>> +
>>>>> +               # Switch the status.
>>>>> +               if ($status eq "disabled") {
>>>>> +                       $status = "enabled";
>>>>> +               } else {
>>>>> +                       $status = "disabled";
>>>>> +               }
>>>>> +
>>>>> +               # Modify the status of the existing entry.
>>>>> +               $redirects{$id} = ["$redirects{$id}[0]",
>>>>> "$redirects{$id}[1]", "$redirects{$id}[2]",
>>>>> "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
>>>>> +
>>>>> +               # Write the changed ignored hash to the
>>>>> redirects file.
>>>>> +               &General::writehasharray($redirectsfile,
>>>>> \%redirects);
>>>>> +       }
>>>>> +
>>>>> +# Remove entry from redirects list.
>>>>> +
>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
>>>>> +       my %redirects = ();
>>>>> +
>>>>> +       # Read-in redirectsfile.
>>>>> +       &General::readhasharray($redirectsfile, \%redirects);
>>>>> +
>>>>> +       # move data on key up
>>>>> +       foreach my $key (sort keys %redirects) {
>>>>> +               if ($key >= $settings{'ID'}) {
>>>>> +                       my $next = $key + 1;
>>>>> +                       if (exists $redirects{$next}) {
>>>>> +                               foreach my $i (0 ..
>>>>> $#{$redirects{$next}}) { $redirects{$key}[$i] =
>>>>> $redirects{$next}[$i]; }
>>>>> +                       }
>>>>> +               }
>>>>> +       }
>>>>> +
>>>>> +       my $last_key = (sort {$a <=> $b} keys %redirects)[-1];
>>>>> +       delete $redirects{$last_key};
>>>>> +
>>>>> +       # Undef the given ID.
>>>>> +       undef($settings{'ID'});
>>>>> +
>>>>> +       # Write the changed redirects hash to file.
>>>>> +       &General::writehasharray($redirectsfile, \%redirects);
>>>>> +}
>>>>> +
>>>>> +# Load settings from file
>>>>> +&General::readhash($settingsfile, \%settings);
>>>>> +&General::readhasharray($redirectsfile, \%redirects);
>>>>> +
>>>>> +# Call functions to generate whole page.
>>>>> +&Header::openpage($Lang::tr{'portredir port redirections'}, 1,
>>>>> '');
>>>>> +&Header::openbigbox('100%', 'left', '', $errormessage);
>>>>> +
>>>>> +if ($errormessage) {
>>>>> +        &Header::openbox('100%', 'left', $Lang::tr{'warning
>>>>> messages'});
>>>>> +        print "<font color='red'>$errormessage&nbsp;</font>";
>>>>> +        &Header::closebox();
>>>>> +}
>>>>> +
>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
>>>>> +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_ADDON'}
>>>>> } = "checked='checked'";
>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_GREEN'}
>>>>> } = "checked='checked'";
>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BLUE'}}
>>>>> = "checked='checked'";
>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_ORANGE
>>>>> '}} = "checked='checked'";
>>>>> +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
>>>>> +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
>>>>> +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}} =
>>>>> "checked='checked'";
>>>>> +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
>>>>> +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
>>>>> +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}} =
>>>>> "checked='checked'";
>>>>> +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
>>>>> +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
>>>>> +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}} =
>>>>> "checked='checked'";
>>>>> +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
>>>>> +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
>>>>> +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}} =
>>>>> "checked='checked'";
>>>>> +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
>>>>> +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
>>>>> +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE'}} =
>>>>> "checked='checked'";
>>>>> +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
>>>>> +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
>>>>> +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE'}} =
>>>>> "checked='checked'";
>>>>> +
>>>>> +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTRY_INTE
>>>>> RFACE'}} = 'selected';
>>>>> +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY_PROTO
>>>>> COL'}} = 'selected';
>>>>> +
>>>>> +&showMainBox();
>>>>> +&showRedirectsBox();
>>>>> +
>>>>> +&Header::closebigbox();
>>>>> +&Header::closepage();
>>>>> +
>>>>> +# Function to show main settings and options.
>>>>> +sub showMainBox() {
>>>>> +
>>>>> +       &Header::openbox('100%', 'center',
>>>>> "$Lang::tr{'settings'}");
>>>>> +       print "<form method='post'
>>>>> action='$ENV{'SCRIPT_NAME'}'>";
>>>>> +
>>>>> +print <<END;
>>>>> +<table width='80%' cellspacing='0' border='0'>
>>>>> +       <tr><td colspan='2' class='base'
>>>>> bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir common
>>>>> settings'}</b></td></tr
>>>>> +       <tr>
>>>>> +               <td width='25%'
>>>>> class='base'>$Lang::tr{'portredir enable addon'}:</td>
>>>>> +               <td><input type='checkbox'
>>>>> name='REDIR_ENABLE_ADDON' $checked{'REDIR_ENABLE_ADDON'}{'on'}
>>>>> /></td>
>>>>> +       </tr>
>>>>> +       <tr><td colspan='2'></td></tr>
>>>>> +       <tr><td colspan='2'>&nbsp;</td></tr>
>>>>> +END
>>>>> +
>>>>> +       # create html table with header line 1
>>>>> +       print "<table width='80%' cellspacing='0'
>>>>> border='0'><tr>";
>>>>> +       print "<th class='base' width='40%'
>>>>> align='left'><b>$Lang::tr{'portredir fw for interface'}</th>";
>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<th
>>>>> class='base' width='10%'><b><font
>>>>> color=green>$Lang::tr{'green'}</font></th>";
>>>>> +               } else {                 print "<th
>>>>> class='base' width='10%'></font></th>"; }
>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<th
>>>>> class='base' width='10%'><b><font
>>>>> color=blue>$Lang::tr{'blue'}</font></th>";
>>>>> +               } else {                 print "<th
>>>>> class='base' width='10%'></font></th>"; }
>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<th
>>>>> class='base' width='10%'><b><font
>>>>> color=orange>$Lang::tr{'orange'}</font></th>";
>>>>> +               } else {                 print "<th
>>>>> class='base' width='10%'></font></th>"; }
>>>>> +
>>>>> +       # the empty right row
>>>>> +       print "<th class='base'
>>>>> width='30%'><td></td></th></tr>";
>>>>> +
>>>>> +       # line 2
>>>>> +       print "<tr><td>$Lang::tr{'portredir force local
>>>>> dns'}</td>";
>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_DNS_GREEN'
>>>>> $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print
>>>>> "<td></td>";}
>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_DNS_BLUE' $checked{'REDIR_DNS_BLUE'}{'on'}></td>";}
>>>>> else { print "<td></td>";}
>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_DNS_ORANGE'
>>>>> $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print
>>>>> "<td></td>";}
>>>>> +
>>>>> +       # line 3
>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir force local
>>>>> ntp'}</td>";
>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_NTP_GREEN'
>>>>> $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print
>>>>> "<td></td>";}
>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_NTP_BLUE' $checked{'REDIR_NTP_BLUE'}{'on'}></td>";}
>>>>> else { print "<td></td>";}
>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_NTP_ORANGE'
>>>>> $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print
>>>>> "<td></td>";}
>>>>> +
>>>>> +       # line 4
>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir enable user
>>>>> redirections'}</td>";
>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_CUSTOM_GREEN'
>>>>> $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else { print
>>>>> "<td></td>";}
>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_CUSTOM_BLUE'
>>>>> $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print
>>>>> "<td></td>";}
>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_CUSTOM_ORANGE'
>>>>> $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else { print
>>>>> "<td></td>";}
>>>>> +
>>>>> +       print <<END;
>>>>> +       </tr></table>
>>>>> +       <table width='80%' cellspacing='0' border='0'>
>>>>> +               <tr><td colspan='2'>&nbsp;</td></tr>
>>>>> +               <tr><td align='left'><b>$Lang::tr{'portredir
>>>>> save to activate'}</b></td><td width='5%' align='center'><input
>>>>> type='submit' name='ACTION' value='  $Lang::tr{'save'} 
>>>>> '></td></tr>
>>>>> +       </table></form>
>>>>> +END
>>>>> +
>>>>> +&Header::closebox();
>>>>> +}
>>>>> +
>>>>> +# Function to show elements of the redirects file and allow to
>>>>> add or remove single members of it.
>>>>> +sub showRedirectsBox() {
>>>>> +        &Header::openbox('100%', 'center',
>>>>> "$Lang::tr{'portredir custom redirections'}");
>>>>> +
>>>>> +       print <<END;
>>>>> +               <table width='80%' cellspacing='1' border='0'>
>>>>> +                       <tr>
>>>>> +                               <td class='base'
>>>>> bgcolor='$color{'color20'}'
>>>>> align='center'><b>$Lang::tr{'interface'}</b></td>
>>>>> +                               <td class='base'
>>>>> bgcolor='$color{'color20'}'
>>>>> align='center'><b>$Lang::tr{'protocol'}</b></td>
>>>>> +                               <td class='base'
>>>>> bgcolor='$color{'color20'}'
>>>>> align='center'><b>$Lang::tr{'port'}</b></td>
>>>>> +                               <td class='base'
>>>>> bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'ip
>>>>> address'}</b></td>
>>>>> +                               <td class='base'
>>>>> bgcolor='$color{'color20'}'
>>>>> align='center'><b>$Lang::tr{'remark'}</b></td>
>>>>> +                               <td class='base' colspan='3'
>>>>> bgcolor='$color{'color20'}'></td>
>>>>> +                       </tr>
>>>>> +END
>>>>> +                       # Check if some rules have been added
>>>>> to be redirects.
>>>>> +                       if (keys (%redirects)) {
>>>>> +                               my $col = "";
>>>>> +
>>>>> +                               # List all entries of the hash.
>>>>> +                               foreach my $key (sort keys
>>>>> %redirects){
>>>>> +
>>>>> +                                       # Assign data array
>>>>> positions to some nice variable names.
>>>>> +                                       my $interface =
>>>>> $redirects{$key}[0];
>>>>> +                                       my $protocol =
>>>>> $redirects{$key}[1];
>>>>> +                                       my $port  =
>>>>> $redirects{$key}[2];
>>>>> +                                       my $address =
>>>>> $redirects{$key}[3];
>>>>> +                                       my $status  =
>>>>> $redirects{$key}[4];
>>>>> +                                       my $remark  =
>>>>> $redirects{$key}[5];
>>>>> +
>>>>> +                                       # Check if the key (id)
>>>>> number is even or not.
>>>>> +                                       if ($settings{'ID'} eq
>>>>> $key) {
>>>>> +                                               $col="bgcolor='
>>>>> ${Header::colouryellow}'";
>>>>> +                                       } elsif ($key % 2) {
>>>>> +                                               $col="bgcolor='
>>>>> $color{'color22'}'";
>>>>> +                                       } else {
>>>>> +                                               $col="bgcolor='
>>>>> $color{'color20'}'";
>>>>> +                                       }
>>>>> +
>>>>> +                                       # Choose icon for the
>>>>> checkbox.
>>>>> +                                       my $gif;
>>>>> +                                       my $gdesc;
>>>>> +
>>>>> +                                       # Check if the status
>>>>> is enabled and select the correct image and description.
>>>>> +                                       if ($status eq
>>>>> 'enabled' ) {
>>>>> +                                               $gif =
>>>>> 'on.gif';
>>>>> +                                               $gdesc =
>>>>> $Lang::tr{'click to disable'};
>>>>> +                                       } else {
>>>>> +                                               $gif =
>>>>> 'off.gif';
>>>>> +                                               $gdesc =
>>>>> $Lang::tr{'click to enable'};
>>>>> +                                       }
>>>>> +
>>>>> +                                       print <<END;
>>>>> +                                       <tr>
>>>>> +                                               <td width='15%'
>>>>> class='base' align='center' $col><b><font
>>>>> color=$interface>$Lang::tr{$interface}</font></b></td>
>>>>> +                                               <td width='10%'
>>>>> class='base' align='center' $col>$protocol</td>
>>>>> +                                               <td width='10%'
>>>>> class='base' align='center' $col>$port</td>
>>>>> +                                               <td width='15%'
>>>>> class='base' align='center' $col>&nbsp;$address</td>
>>>>> +                                               <td width='40%'
>>>>> class='base' align='center' $col>&nbsp;$remark</td>
>>>>> +                                               <td
>>>>> align='center' $col>
>>>>> +                                                       <form
>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>> +                                                              
>>>>>  <input type='hidden' name='ACTION' value='$Lang::tr{'toggle
>>>>> enable disable'}' />
>>>>> +                                                              
>>>>>  <input type='image' name='$Lang::tr{'toggle enable disable'}'
>>>>> src='/images/$gif' alt='$gdesc' title='$gdesc' />
>>>>> +                                                              
>>>>>  <input type='hidden' name='ID' value='$key' />
>>>>> +                                                       </form>
>>>>> +                                               </td>
>>>>> +                                               <td
>>>>> align='center' $col>
>>>>> +                                                       <form
>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>> +                                                              
>>>>>  <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}'
>>>>> />
>>>>> +                                                              
>>>>>  <input type='image' name='$Lang::tr{'edit'}'
>>>>> src='/images/edit.gif' alt='$Lang::tr{'edit'}'
>>>>> title='$Lang::tr{'edit'}' />
>>>>> +                                                              
>>>>>  <input type='hidden' name='ID' value='$key' />
>>>>> +                                                       </form>
>>>>> +                                               </td>
>>>>> +                                               <td
>>>>> align='center' $col>
>>>>> +                                                       <form
>>>>> method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
>>>>> +                                                              
>>>>>  <input type='image' name='$Lang::tr{'remove'}'
>>>>> src='/images/delete.gif' title='$Lang::tr{'remove'}'
>>>>> alt='$Lang::tr{'remove'}'>
>>>>> +                                                              
>>>>>  <input type='hidden' name='ID' value='$key'>
>>>>> +                                                              
>>>>>  <input type='hidden' name='ACTION'
>>>>> value='$Lang::tr{'remove'}'>
>>>>> +                                                       </form>
>>>>> +                                               </td>
>>>>> +                                       </tr>
>>>>> +END
>>>>> +                               }
>>>>> +                       } else {
>>>>> +                               # Print notice that currently
>>>>> no ports are redirected.
>>>>> +                               print "<tr>\n";
>>>>> +                               print "<td class='base'
>>>>> colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
>>>>> +                               print "</tr>\n";
>>>>> +                       }
>>>>> +
>>>>> +               print "</table>\n";
>>>>> +
>>>>> +       # Section to add new elements or edit existing ones.
>>>>> +       print <<END;
>>>>> +       <br>
>>>>> +       <hr>
>>>>> +       <br>
>>>>> +       <div align='center'>
>>>>> +               <table width='100%' cellspacing='0' border='0'>
>>>>> +END
>>>>> +
>>>>> +       # Assign correct headline and button text.
>>>>> +       my $buttontext;
>>>>> +       my $entry_interface;
>>>>> +       my $entry_protocol;
>>>>> +       my $entry_port;
>>>>> +       my $entry_address;
>>>>> +       my $entry_remark;
>>>>> +
>>>>> +       # Check if an ID (key) has been given, in this case an
>>>>> existing entry should be edited.
>>>>> +       if ($settings{'ID'} ne '') {
>>>>> +               $buttontext = $Lang::tr{'update'};
>>>>> +               print "<tr><td class='boldbase'
>>>>> colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
>>>>> +
>>>>> +               # Grab address and remark for the given key.
>>>>> +               $entry_interface =
>>>>> $redirects{$settings{'ID'}}[0];
>>>>> +               $entry_protocol =
>>>>> $redirects{$settings{'ID'}}[1];
>>>>> +               $entry_port = $redirects{$settings{'ID'}}[2];
>>>>> +               $entry_address =
>>>>> $redirects{$settings{'ID'}}[3];
>>>>> +               $entry_remark = $redirects{$settings{'ID'}}[5];
>>>>> +
>>>>> +       } else {
>>>>> +               $buttontext = $Lang::tr{'add'};
>>>>> +               print "<tr><td class='boldbase'
>>>>> colspan='11'><b>$Lang::tr{'dnsforward add a new
>>>>> entry'}</b></td></tr>\n";
>>>>> +               print "<tr><td>&nbsp</td></tr>\n";
>>>>> +       }
>>>>> +
>>>>> +       print <<END;
>>>>> +                       <tr>
>>>>> +                               <td class='base' width='1%' 
>>>>> bgcolor='$color{'color22'}'></td>
>>>>> +                               <td class='base' width='15%'
>>>>> bgcolor='$color{'color22'}'
>>>>> align='left'><b>$Lang::tr{'interface'}</b></td>
>>>>> +                               <td class='base' width='10%'
>>>>> bgcolor='$color{'color22'}'
>>>>> align='left'><b>$Lang::tr{'protocol'}</b></td>
>>>>> +                               <td class='base' width='10%'
>>>>> bgcolor='$color{'color22'}'
>>>>> align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
>>>>> +                               <td class='base' width='13%'
>>>>> bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'ip
>>>>> address'}</b></td>
>>>>> +                               <td class='base' width='30%'
>>>>> bgcolor='$color{'color22'}'
>>>>> align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
>>>>> +                               <td class='base' width='15%'
>>>>> bgcolor='$color{'color22'}'></td>
>>>>> +                               <td class='base' width='1%' 
>>>>> bgcolor='$color{'color22'}'></td>
>>>>> +                       </tr>
>>>>> +
>>>>> +                       <form method='post'
>>>>> action='$ENV{'SCRIPT_NAME'}'>
>>>>> +                       <input type='hidden' name='ID'
>>>>> value='$settings{'ID'}'>
>>>>> +                       <tr>
>>>>> +                               <td class='base'></td>
>>>>> +                               <td><select style='width:90px;'
>>>>> id='interface' name='REDIR_ENTRY_INTERFACE'>
>>>>> +END
>>>>> +                               if ($netsettings{'GREEN_DEV'})
>>>>> {
>>>>> +                                       if ($entry_interface eq
>>>>> "green") {
>>>>> +                                               print "<option
>>>>> value='green' selected='selected'
>>>>> {'green'}>$Lang::tr{'green'}</option>";
>>>>> +                                       } else { print "<option
>>>>> value='green' {'green'}>$Lang::tr{'green'}</option>";}
>>>>> +                               }
>>>>> +                               if ($netsettings{'BLUE_DEV'}) {
>>>>> +                                       if ($entry_interface eq
>>>>> "blue") { 
>>>>> +                                               print "<option
>>>>> value='blue' selected='selected'
>>>>> {'blue'}>$Lang::tr{'blue'}</option>";
>>>>> +                                       } else { print "<option
>>>>> value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
>>>>> +                               }
>>>>> +                               if ($netsettings{'ORANGE_DEV'})
>>>>> {
>>>>> +                                       if ($entry_interface eq
>>>>> "orange") { 
>>>>> +                                               print "<option
>>>>> value='orange' selected='selected'
>>>>> {'orange'}>$Lang::tr{'orange'}</option>";
>>>>> +                                       } else { print "<option
>>>>> value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
>>>>> +                               }
>>>>> +
>>>>> +                       print "</select><td><select
>>>>> style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
>>>>> +                       if ((!$entry_protocol) ||
>>>>> ($entry_protocol eq "tcp")) {
>>>>> +                               print "<option
>>>>> selected='selected' id='protocol' value='tcp'
>>>>> {'tcp'}>tcp</option>";
>>>>> +                               print "<option value='udp'
>>>>> {'udp'}>udp</option>";
>>>>> +                       } elsif ($entry_protocol eq "udp") {
>>>>> +                               print "<option value='tcp'
>>>>> {'tcp'}>tcp</option>";
>>>>> +                               print "<option
>>>>> selected='selected' value='udp' {'udp'}>udp</option>";
>>>>> +                       }
>>>>> +       print <<END;
>>>>> +                               </select></td>
>>>>> +                               <td><input type='text'
>>>>> name='REDIR_ENTRY_PORT'    value='$entry_port'   
>>>>> size='4'></td>
>>>>> +                               <td><input type='text'
>>>>> name='REDIR_ENTRY_ADDRESS' value='$entry_address'
>>>>> size='14'></td>
>>>>> +                               <td><input type='text'
>>>>> name='REDIR_ENTRY_REMARK'  value='$entry_remark' 
>>>>> size='35'></td>
>>>>> +                               <td width='10%'
>>>>> align='center'><input type='submit' name='ACTION' value=' 
>>>>> $buttontext  '></td>
>>>>> +                               <td class='base'></td>
>>>>> +                       </tr>
>>>>> +                       </form>
>>>>> +               </table>
>>>>> +       </div>
>>>>> +END
>>>>> +       &Header::closebox();
>>>>> +}
>>>>> diff --git a/config/rootfiles/common/misc-progs
>>>>> b/config/rootfiles/common/misc-progs
>>>>> index d6594b3f8..fbad2af8b 100644
>>>>> --- a/config/rootfiles/common/misc-progs
>>>>> +++ b/config/rootfiles/common/misc-progs
>>>>> @@ -17,6 +17,7 @@ usr/local/bin/logwatch
>>>>> #usr/local/bin/mpfirectrl
>>>>> usr/local/bin/openvpnctrl
>>>>> usr/local/bin/pakfire
>>>>> +#usr/local/bin/portredirctrl
>>>>> usr/local/bin/qosctrl
>>>>> usr/local/bin/rebuildhosts
>>>>> usr/local/bin/rebuildroutes
>>>>> diff --git a/config/rootfiles/packages/portredir
>>>>> b/config/rootfiles/packages/portredir
>>>>> new file mode 100644
>>>>> index 000000000..4b4ba8366
>>>>> --- /dev/null
>>>>> +++ b/config/rootfiles/packages/portredir
>>>>> @@ -0,0 +1,11 @@
>>>>> +etc/rc.d/init.d/portredir
>>>>> +etc/rc.d/rc0.d/K77portredir
>>>>> +etc/rc.d/rc3.d/S23portredir
>>>>> +etc/rc.d/rc6.d/K77portredir
>>>>> +srv/web/ipfire/cgi-bin/portredir.cgi
>>>>> +usr/local/bin/portredirctrl
>>>>> +var/ipfire/addon-lang/portredir.de.pl
>>>>> +var/ipfire/addon-lang/portredir.en.pl
>>>>> +var/ipfire/backup/addons/includes/portredir
>>>>> +var/ipfire/menu.d/EX-portredir.menu
>>>>> +var/ipfire/portredir
>>>>> diff --git a/lfs/portredir b/lfs/portredir
>>>>> new file mode 100644
>>>>> index 000000000..a4911f71f
>>>>> --- /dev/null
>>>>> +++ b/lfs/portredir
>>>>> @@ -0,0 +1,85 @@
>>>>> +##############################################################
>>>>> #################
>>>>> +#                                                             
>>>>>                 #
>>>>> +# IPFire.org - A linux based
>>>>> firewall                                         #
>>>>> +# Copyright (C) 2007-2021  IPFire Team 
>>>>> <info@ipfire.org>                     #
>>>>> +#                                                             
>>>>>                 #
>>>>> +# This program is free software: you can redistribute it
>>>>> and/or modify        #
>>>>> +# it under the terms of the GNU General Public License as
>>>>> published by        #
>>>>> +# the Free Software Foundation, either version 3 of the
>>>>> License, or           #
>>>>> +# (at your option) any later
>>>>> version.                                         #
>>>>> +#                                                             
>>>>>                 #
>>>>> +# This program is distributed in the hope that it will be
>>>>> useful,             #
>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>> of              #
>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>> the               #
>>>>> +# GNU General Public License for more
>>>>> details.                                #
>>>>> +#                                                             
>>>>>                 #
>>>>> +# You should have received a copy of the GNU General Public
>>>>> License           #
>>>>> +# along with this program.  If not, see <
>>>>> http://www.gnu.org/licenses/>.       #
>>>>> +#                                                             
>>>>>                 #
>>>>> +##############################################################
>>>>> #################
>>>>> +
>>>>> +##############################################################
>>>>> #################
>>>>> +# Definitions
>>>>> +##############################################################
>>>>> #################
>>>>> +
>>>>> +include Config
>>>>> +
>>>>> +VER        = 1.0
>>>>> +
>>>>> +THISAPP    = portredir-$(VER)
>>>>> +DIR_APP    = $(DIR_SRC)/$(THISAPP)
>>>>> +TARGET     = $(DIR_INFO)/$(THISAPP)
>>>>> +PROG       = portredir
>>>>> +PAK_VER    = 1
>>>>> +
>>>>> +##############################################################
>>>>> #################
>>>>> +# Top-level Rules
>>>>> +##############################################################
>>>>> #################
>>>>> +
>>>>> +install : $(TARGET)
>>>>> +
>>>>> +check :
>>>>> +
>>>>> +download :
>>>>> +
>>>>> +md5 :
>>>>> +
>>>>> +dist: 
>>>>> +       @$(PAK)
>>>>> +
>>>>> +##############################################################
>>>>> #################
>>>>> +# Installation Details
>>>>> +##############################################################
>>>>> #################
>>>>> +
>>>>> +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
>>>>> +       @$(PREBUILD)
>>>>> +       @rm -rf $(DIR_APP) && cd $(DIR_SRC)
>>>>> +
>>>>> +       #install cgi 
>>>>> +       install -v -m 755 $(DIR_CONF)/portredir/portredir.cgi
>>>>> /srv/web/ipfire/cgi-bin/
>>>>> +
>>>>> +       #create configuration dir 
>>>>> +       -mkdir -pv /var/ipfire/portredir/
>>>>> +       chown -R nobody:nobody /var/ipfire/portredir/
>>>>> +
>>>>> +       # Install include file for backup
>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/portredir-
>>>>> backup /var/ipfire/backup/addons/includes/portredir
>>>>> +
>>>>> +       # Install menu file
>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/EX-
>>>>> portredir.menu /var/ipfire/menu.d/
>>>>> +       chown nobody:nobody /var/ipfire/menu.d/EX-
>>>>> portredir.menu
>>>>> +
>>>>> +       # Install addon-specific language-files
>>>>> +       install -v -m 644
>>>>> $(DIR_CONF)/portredir/lang/portredir.*.pl /var/ipfire/addon-
>>>>> lang/
>>>>> +
>>>>> +       #install initscripts
>>>>> +       $(call INSTALL_INITSCRIPT,portredir)
>>>>> +
>>>>> +       # Create symlinks for runlevel interaction.
>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>> /etc/rc.d/rc3.d/S23portredir
>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>> /etc/rc.d/rc0.d/K77portredir
>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>> /etc/rc.d/rc6.d/K77portredir
>>>>> +
>>>>> +       @rm -rf $(DIR_APP)
>>>>> +       @$(POSTBUILD)
>>>>> +
>>>>> diff --git a/make.sh b/make.sh
>>>>> index fc03ebcd5..ab9fe881a 100755
>>>>> --- a/make.sh
>>>>> +++ b/make.sh
>>>>> @@ -1623,6 +1623,7 @@ buildipfire() {
>>>>>  lfsmake2 socat
>>>>>  lfsmake2 libcdada
>>>>>  lfsmake2 pmacct
>>>>> +  lfsmake2 portredir
>>>>> }
>>>>> 
>>>>> buildinstaller() {
>>>>> diff --git a/src/initscripts/packages/portredir
>>>>> b/src/initscripts/packages/portredir
>>>>> new file mode 100644
>>>>> index 000000000..cc57fb9cc
>>>>> --- /dev/null
>>>>> +++ b/src/initscripts/packages/portredir
>>>>> @@ -0,0 +1,191 @@
>>>>> +#!/bin/sh
>>>>> +##############################################################
>>>>> ##########
>>>>> +# Begin $rc_base/init.d/portredir
>>>>> +#
>>>>> +# Description : portredir init script for DNS/NTP and custom 
>>>>> +#              port redirection rules
>>>>> +#
>>>>> +##############################################################
>>>>> ##########
>>>>> +
>>>>> +. /etc/sysconfig/rc
>>>>> +. ${rc_functions}
>>>>> +
>>>>> +IPT="/sbin/iptables";
>>>>> +parent_chain="PREROUTING";
>>>>> +chain="PORT_REDIRECT";
>>>>> +
>>>>> +confdir="/var/ipfire/portredir";
>>>>> +settingsfile="${confdir}/settings";
>>>>> +redirectsfile="${confdir}/redirects";
>>>>> +SYSLOG="NO";
>>>>> +VERBOSE="NO";
>>>>> +
>>>>> +eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings);
>>>>> +eval $(/usr/local/bin/readhash ${settingsfile});
>>>>> +
>>>>> +logtext() {
>>>>> +       if [ "${SYSLOG}" = "YES" ]; then logger -t "portredir"
>>>>> ${1}; fi;
>>>>> +       if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
>>>>> +
>>>>> +create_chain() {
>>>>> +
>>>>> +       local line=$(${IPT} -t nat -L ${parent_chain} --line-
>>>>> numbers |grep "SQUID" |awk '{printf($1)}');
>>>>> +
>>>>> +       if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z
>>>>> "${REDIR_ENABLE_ADDON}" ]]; then
>>>>> +               logtext "addon not enabled in web
>>>>> interface...";
>>>>> +               echo "Portredir addon not enabled in web
>>>>> interface...";
>>>>> +               exit 0;
>>>>> +       fi;
>>>>> +
>>>>> +       if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>> ${chain})" ]; then
>>>>> +               ${IPT} -t nat -N ${chain};
>>>>> +
>>>>> +               if [ ! -z "${line}" ]; then
>>>>> +                       logtext "create chain ${chain} and link
>>>>> in ${parent_chain} at position ${line}...";
>>>>> +                       ${IPT} -t nat -I ${parent_chain}
>>>>> ${line} -j ${chain};
>>>>> +               else
>>>>> +                       logtext "create chain ${chain} and link
>>>>> in ${parent_chain} at last position...";
>>>>> +                       ${IPT} -t nat -A ${parent_chain} -j
>>>>> ${chain};
>>>>> +               fi
>>>>> +       else
>>>>> +               return 1;
>>>>> +       fi;
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +remove_chain() {
>>>>> +       if [ ! -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>> ${chain})" ]; then
>>>>> +               logtext "remove chain ${chain} and link in
>>>>> ${parent_chain} from system...";
>>>>> +               ${IPT} -t nat -D "${parent_chain}" -j ${chain};
>>>>> +               ${IPT} -t nat -F ${chain};
>>>>> +               ${IPT} -t nat -X ${chain};
>>>>> +       else
>>>>> +               return 1;
>>>>> +       fi;
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +activate_custom_redirections() {
>>>>> +       
>>>>> +       local array=();
>>>>> +       local redirects=();
>>>>> +       local i;
>>>>> +       index=();
>>>>> +       iface=();
>>>>> +       protocol=();
>>>>> +       port=();
>>>>> +       targetip=();
>>>>> +       enabled=();
>>>>> +
>>>>> +       IFS=$'\n' read -d '' -ra redirects < ${redirectsfile};
>>>>> +
>>>>> +       for i in "${!redirects[@]}"
>>>>> +       do
>>>>> +               IFS=$',' read -ra array <<< ${redirects[i]};
>>>>> +               index[i]=${array[0]};
>>>>> +               iface[i]=${array[1]};
>>>>> +               protocol[i]=${array[2]};
>>>>> +               port[i]=${array[3]};
>>>>> +               targetip[i]=${array[4]};
>>>>> +               enabled[i]=${array[5]};
>>>>> +       done
>>>>> +
>>>>> +       for i in "${!index[@]}"
>>>>> +       do
>>>>> +               if [[ ! -z "${GREEN_DEV}" &&  "${iface[i]}" =
>>>>> "green" && "${enabled[i]}" = "enabled" ]]; then
>>>>> +
>>>>> +                       logtext "add redirect in ${chain} on
>>>>> ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>> ${port[i]} ";
>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>> ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>> ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>> ${port[i]} -j REDIRECT;
>>>>> +               fi
>>>>> +               if [[ ! -z "${BLUE_DEV}" &&  "${iface[i]}" =
>>>>> "blue" && "${enabled[i]}" = "enabled" ]]; then
>>>>> +                       logtext "add redirect in ${chain} on
>>>>> ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>> ${port[i]} ";
>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>> ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>> ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>> ${port[i]} -j REDIRECT;
>>>>> +               fi
>>>>> +               if [[ ! -z "${ORANGE_DEV}" &&  "${iface[i]}" =
>>>>> "orange" && "${enabled[i]}" = "enabled" ]]; then
>>>>> +                       logtext "add redirect in ${chain} on
>>>>> ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>> ${port[i]} ";
>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>> ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>> ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>> ${port[i]} -j REDIRECT;
>>>>> +               fi
>>>>> +       done
>>>>> +       unset array redirects i index iface protocol port
>>>>> targetip enabled;
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +activate_redirections() {
>>>>> +
>>>>> +       if ! create_chain; then return 1; fi;
>>>>> +       
>>>>> +       # Force DNS REDIRECTs on GREEN (udp, tcp, 53)
>>>>> +       if [[ "${REDIR_DNS_GREEN}" == "on" && 
>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>> udp -m udp --dport domain -j REDIRECT;
>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>> ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>> tcp -m tcp --dport domain -j REDIRECT;
>>>>> +       fi
>>>>> +
>>>>> +       # Force DNS REDIRECTs on BLUE (udp, tcp, 53)
>>>>> +       if [[ "${REDIR_DNS_BLUE}" == "on" && 
>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>> -m udp --dport domain -j REDIRECT
>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>> ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p tcp
>>>>> -m tcp --dport domain -j REDIRECT
>>>>> +       fi
>>>>> +
>>>>> +       # Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
>>>>> +       if [[ "${REDIR_DNS_ORANGE}" == "on" && 
>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>> udp -m udp --dport domain -j REDIRECT
>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>> ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>> tcp -m tcp --dport domain -j REDIRECT
>>>>> +       fi
>>>>> +
>>>>> +       # Force NTP REDIRECTs on GREEN (udp, 123)
>>>>> +       if [[ "${REDIR_NTP_GREEN}" == "on" && 
>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>> +       fi
>>>>> +
>>>>> +       # Force NTP REDIRECTs on BLUE (udp, 123)
>>>>> +       if [[ "${REDIR_NTP_BLUE}" == "on" && 
>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>> -m udp --dport ntp -j REDIRECT
>>>>> +       fi
>>>>> +
>>>>> +       # Force NTP REDIRECTs on ORANGE (udp, 123)
>>>>> +       if [[ "${REDIR_NTP_ORANGE}" == "on" && 
>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>> +       fi
>>>>> +
>>>>> +       if ! activate_custom_redirections; then return 1; fi;
>>>>> +
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +case "${1}" in
>>>>> +       start)
>>>>> +               boot_mesg "Loading port redirections..."
>>>>> +               activate_redirections;
>>>>> +               evaluate_retval;
>>>>> +               ;;
>>>>> +
>>>>> +       stop)   
>>>>> +               boot_mesg "Removing port redirections..."
>>>>> +               remove_chain;
>>>>> +               evaluate_retval;
>>>>> +               ;;
>>>>> +
>>>>> +       restart)
>>>>> +               ${0} stop
>>>>> +               ${0} start
>>>>> +               ;;
>>>>> +
>>>>> +       *)
>>>>> +               echo "Usage: ${0} {start|stop|restart}"
>>>>> +               exit 1
>>>>> +               ;;
>>>>> +esac
>>>>> +
>>>>> +# End $rc_base/init.d/portredir
>>>>> diff --git a/src/misc-progs/Makefile b/src/misc-progs/Makefile
>>>>> index 7c3ef7529..850f8fdcc 100644
>>>>> --- a/src/misc-progs/Makefile
>>>>> +++ b/src/misc-progs/Makefile
>>>>> @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
>>>>>         wirelessctrl getipstat qosctrl \
>>>>>         redctrl syslogdctrl extrahdctrl sambactrl \
>>>>>         smartctrl clamavctrl addonctrl pakfire mpfirectrl
>>>>> wlanapctrl \
>>>>> -       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>> rebuildroutes \
>>>>> +       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>> rebuildroutes portredirctrl \
>>>>>         getconntracktable wirelessclient torctrl ddnsctrl
>>>>> unboundctrl \
>>>>>         captivectrl
>>>>> 
>>>>> diff --git a/src/misc-progs/portredirctrl.c b/src/misc-
>>>>> progs/portredirctrl.c
>>>>> new file mode 100644
>>>>> index 000000000..7897d711c
>>>>> --- /dev/null
>>>>> +++ b/src/misc-progs/portredirctrl.c
>>>>> @@ -0,0 +1,47 @@
>>>>> +/* This file is part of the IPFire Firewall.
>>>>> + *
>>>>> + * This program is distributed under the terms of the GNU
>>>>> General Public
>>>>> + * Licence.  See the file COPYING for details.
>>>>> + *
>>>>> + */
>>>>> +
>>>>> +#include <stdlib.h>
>>>>> +#include <stdio.h>
>>>>> +#include <string.h>
>>>>> +#include <unistd.h>
>>>>> +#include <sys/types.h>
>>>>> +#include <fcntl.h>
>>>>> +#include "setuid.h"
>>>>> +
>>>>> +int main(int argc, char *argv[]) {
>>>>> +       if (!(initsetuid()))
>>>>> +               exit(1);
>>>>> +
>>>>> +       // Check what command is asked
>>>>> +        if (argc < 2) {
>>>>> +                fprintf(stderr, "\nNo argument
>>>>> given.\n\nportredirctrl
>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>> +                exit(1);
>>>>> +        }
>>>>> +
>>>>> +        if (strcmp(argv[1], "start") == 0) {
>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>> start");
>>>>> +        } else if (strcmp(argv[1], "stop") == 0) {
>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>> stop");
>>>>> +        } else if (strcmp(argv[1], "restart") == 0) {
>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>> restart");
>>>>> +       } else if (strcmp(argv[1], "enable") == 0) {
>>>>> +               safe_system("touch
>>>>> /var/ipfire/portredir/enable");
>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>> /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>> /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>> /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
>>>>> +       } else if (strcmp(argv[1], "disable") == 0) {
>>>>> +               safe_system("/etc/rc.d/init.d/portredir stop");
>>>>> +               safe_system("unlink
>>>>> /var/ipfire/portredir/enable");
>>>>> +               safe_system("rm -rf /etc/rc.d/rc*.d/*portredir
>>>>>> /dev/null 2>&1");
>>>>> +        } else {
>>>>> +                fprintf(stderr, "\nBad argument
>>>>> given.\n\nportredirctrl
>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>> +                exit(1);
>>>>> +        }
>>>>> +
>>>>> +       return 0;
>>>>> +}
>>>>> diff --git a/src/paks/portredir/install.sh
>>>>> b/src/paks/portredir/install.sh
>>>>> new file mode 100644
>>>>> index 000000000..9f69aeae2
>>>>> --- /dev/null
>>>>> +++ b/src/paks/portredir/install.sh
>>>>> @@ -0,0 +1,32 @@
>>>>> +#!/bin/bash
>>>>> +##############################################################
>>>>> ##############
>>>>> +#                                                             
>>>>>              #
>>>>> +# This file is part of the IPFire
>>>>> Firewall.                                #
>>>>> +#                                                             
>>>>>              #
>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>> modify           #
>>>>> +# it under the terms of the GNU General Public License as
>>>>> published by     #
>>>>> +# the Free Software Foundation; either version 2 of the
>>>>> License, or        #
>>>>> +# (at your option) any later
>>>>> version.                                      #
>>>>> +#                                                             
>>>>>              #
>>>>> +# IPFire is distributed in the hope that it will be
>>>>> useful,                #
>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>> of           #
>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>> the            #
>>>>> +# GNU General Public License for more
>>>>> details.                             #
>>>>> +#                                                             
>>>>>              #
>>>>> +# You should have received a copy of the GNU General Public
>>>>> License        #
>>>>> +# along with IPFire; if not, write to the Free
>>>>> Software                    #
>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>> 02111-1307 USA #
>>>>> +#                                                             
>>>>>              #
>>>>> +# Copyright (C) 2021 IPFire-Team
>>>>> <info@ipfire.org>.                        #
>>>>> +#                                                             
>>>>>              #
>>>>> +##############################################################
>>>>> ##############
>>>>> +#
>>>>> +. /opt/pakfire/lib/functions.sh
>>>>> +extract_files
>>>>> +restore_backup ${NAME}
>>>>> +
>>>>> +/usr/local/bin/update-lang-cache
>>>>> +
>>>>> +chown root:nobody /usr/local/bin/portredirctrl
>>>>> +chmod 4750 /usr/local/bin/portredirctrl
>>>>> +chmod u+s /usr/local/bin/portredirctrl
>>>>> diff --git a/src/paks/portredir/uninstall.sh
>>>>> b/src/paks/portredir/uninstall.sh
>>>>> new file mode 100644
>>>>> index 000000000..df9270125
>>>>> --- /dev/null
>>>>> +++ b/src/paks/portredir/uninstall.sh
>>>>> @@ -0,0 +1,28 @@
>>>>> +#!/bin/bash
>>>>> +##############################################################
>>>>> ##############
>>>>> +#                                                             
>>>>>              #
>>>>> +# This file is part of the IPFire
>>>>> Firewall.                                #
>>>>> +#                                                             
>>>>>              #
>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>> modify           #
>>>>> +# it under the terms of the GNU General Public License as
>>>>> published by     #
>>>>> +# the Free Software Foundation; either version 2 of the
>>>>> License, or        #
>>>>> +# (at your option) any later
>>>>> version.                                      #
>>>>> +#                                                             
>>>>>              #
>>>>> +# IPFire is distributed in the hope that it will be
>>>>> useful,                #
>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>> of           #
>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>> the            #
>>>>> +# GNU General Public License for more
>>>>> details.                             #
>>>>> +#                                                             
>>>>>              #
>>>>> +# You should have received a copy of the GNU General Public
>>>>> License        #
>>>>> +# along with IPFire; if not, write to the Free
>>>>> Software                    #
>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>> 02111-1307 USA #
>>>>> +#                                                             
>>>>>              #
>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>> <info@ipfire.org>.                        #
>>>>> +#                                                             
>>>>>              #
>>>>> +##############################################################
>>>>> ##############
>>>>> +#
>>>>> +. /opt/pakfire/lib/functions.sh
>>>>> +make_backup ${NAME}
>>>>> +remove_files
>>>>> +
>>>>> +/usr/local/bin/update-lang-cache
>>>>> diff --git a/src/paks/portredir/update.sh
>>>>> b/src/paks/portredir/update.sh
>>>>> new file mode 100644
>>>>> index 000000000..89c40d0d7
>>>>> --- /dev/null
>>>>> +++ b/src/paks/portredir/update.sh
>>>>> @@ -0,0 +1,26 @@
>>>>> +#!/bin/bash
>>>>> +##############################################################
>>>>> ##############
>>>>> +#                                                             
>>>>>              #
>>>>> +# This file is part of the IPFire
>>>>> Firewall.                                #
>>>>> +#                                                             
>>>>>              #
>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>> modify           #
>>>>> +# it under the terms of the GNU General Public License as
>>>>> published by     #
>>>>> +# the Free Software Foundation; either version 2 of the
>>>>> License, or        #
>>>>> +# (at your option) any later
>>>>> version.                                      #
>>>>> +#                                                             
>>>>>              #
>>>>> +# IPFire is distributed in the hope that it will be
>>>>> useful,                #
>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>> of           #
>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>> the            #
>>>>> +# GNU General Public License for more
>>>>> details.                             #
>>>>> +#                                                             
>>>>>              #
>>>>> +# You should have received a copy of the GNU General Public
>>>>> License        #
>>>>> +# along with IPFire; if not, write to the Free
>>>>> Software                    #
>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>> 02111-1307 USA #
>>>>> +#                                                             
>>>>>              #
>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>> <info@ipfire.org>.                        #
>>>>> +#                                                             
>>>>>              #
>>>>> +##############################################################
>>>>> ##############
>>>>> +#
>>>>> +. /opt/pakfire/lib/functions.sh
>>>>> +./uninstall.sh
>>>>> +./install.sh
>>>>> -- 
>>>>> 2.18.0
>>>>> 
>>>> 
>>> 
>> 
>
  
Michael Tremer July 1, 2021, 8:08 a.m. UTC | #6
Hey Jon,

You probably want “any” as destination.

-Michael

> On 1 Jul 2021, at 04:08, Jon Murphy <jcmurphy26@gmail.com> wrote:
> 
> Hi Stefan,
> 
> Thank you for taking this on!
> 
> I applied the patchwork.ipfire patch.
> 
> I think I entered something wrong since I cannot get things to work.  I tried both with Destination Firewall GREEN & Firewall RED.
> 
> 
> Does the Firewall Rule seem right?
> 
> Best regards,
> Jon
> 
> 
> Here is the rule I set up:
> 
> 
> <Screen Shot 2021-06-30 at 9.53.52 PM.png>
> 
> <Screen Shot 2021-06-30 at 9.53.10 PM.png>
> 
> 
> And this is what I see with conntrack:
> 
> conntrack -E -e NEW,UPDATE | grep -e "=53 "
> 
>     [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=51169 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=51169
>     [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=54168 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=54168
>     [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=56094 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=56094
>     [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=52964 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=52964
>     [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=53279 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=53279
>     [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=61657 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=61657
>     [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=57723 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=57723
> 
> 
> 
> 
>> On Jun 30, 2021, at 2:14 PM, Stefan Schantl <stefan.schantl@ipfire.org> wrote:
>> 
>> Hello Matthias, Hello Michael, Hello Jon, Hello *,
>> 
>> I've followed the conversation on this list since the first mail and
>> thoughts about forcing DNS traffic to use the local resolver.
>> 
>> It was a very long journey and lot of time and work has been spent to
>> get to the present point.
>> 
>> As Michael requested here, I've digged through the lines of the perl
>> script which is responsible for creating the firewall rules and
>> surprisingly found that everyting which is needed to create generic
>> REDIRECT rules already was written in the past - it just did not work
>> as designed/expected.
>> 
>> Finaly I was able to adjust these lines of code and to repair that
>> feature.
>> 
>> A redirect rule can be created by picking a single host or group of
>> hosts or entire network(s) as source, selecting NAT (DNAT) and choosing
>> the Firewall itself as target.
>> 
>> The protocol or service or service group which should be redirected has
>> to be selected afterwards. If you want to redirect a given port to
>> another one it can be specified as "Target port".
>> 
>> All created redirect rules are displayed as "input rules".
>> 
>> 
>> The patch directly can be accessed here:
>> 
>> https://patchwork.ipfire.org/project/ipfire/patch/20210630184031.7726-1-stefan.schantl@ipfire.org/
>> 
>> Best regards,
>> 
>> -Stefan
>> 
>>> Hello,
>>> 
>>>> On 28 Jun 2021, at 18:53, Jon Murphy <jcmurphy26@gmail.com> wrote:
>>>> 
>>>> Hi Michael!  Happy Monday!
>>>> 
>>>> 
>>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>>> of code instead of adding many hundreds of lines?
>>>>> 
>>>>> Please can someone elaborate on this more?
>>>> 
>>>> Doing a DNS redirect, via the WegBUI, has been an issue since
>>>> 2015.  I found this quote in the old forum:
>>>> 
>>>> "Having investigated a bit more I have concluded that it's not
>>>> currently possible to create such rules through the WUI.
>>>> 
>>>> There are a number of obstacles:
>>>> 1. It is not allowed to create a rule where source IP and
>>>> destination nat IP is on the same subnetwork (e.g. GREEN), WUI
>>>> error message: "Source and destination IP addresses are from the
>>>> same subnet."
>>>> 
>>>> 2. WUI will not allow you to create a rule without a destination
>>>> (the filtered packet must adhere to a destination, not only a port)
>>>> and the destination MUST be an IP address of one of the IPFire
>>>> interfaces, which limits whats possible a great deal." 
>>> 
>>> And these cannot be changed?
>>> 
>>>> And I found this from 2016:
>>>> https://bugzilla.ipfire.org/show_bug.cgi?id=11168
>>>> 
>>>> So I am guessing that no one has been able to determine a way to
>>>> extend the WebGUI.  
>>> 
>>> Has anyone tried? I do not see any obvious reasons why this should
>>> not be possible.
>>> 
>>>> I am curious - Who created the 
>>>> https://ipfire:444/cgi-bin/firewall.cgi page?  And could they help?
>>> 
>>> -Michael
>>> 
>>>> Jon
>>>> 
>>>> 
>>>>> On Jun 28, 2021, at 11:04 AM, Michael Tremer <
>>>>> michael.tremer@ipfire.org> wrote:
>>>>> 
>>>>> Hello Matthias,
>>>>> 
>>>>>> On 27 Jun 2021, at 14:48, Matthias Fischer <
>>>>>> matthias.fischer@ipfire.org> wrote:
>>>>>> 
>>>>>> From: Marcel Lorenz <marcel.lorenz@ipfire.org>
>>>>> 
>>>>> Thank you for sending this patch on Marcel’s behalf, but I would
>>>>> much more prefer if he would submit his patches on his own. I do
>>>>> not see why that isn’t possible.
>>>>> 
>>>>>> Please note:
>>>>>> This is a new addon written by Marcel Lorenz <
>>>>>> marcel.lorenz@ipfire.org>.
>>>>>> 
>>>>>> It adds a new GUI to IPFire for DNS/NTP *and* user specific
>>>>>> port redirections.
>>>>>> 
>>>>>> How its working:
>>>>>> It has exactly the same functionalities as "Forcing
>>>>>> DNS/NTP..."  - and some more.
>>>>>> 
>>>>>> By setting switches, DNS/NTP requests are automatically
>>>>>> redirected to the local IPFire DNS/NTP servers.
>>>>>> 
>>>>>> Additionally, the user can specify custom redirections.
>>>>>> 
>>>>>> These rules are added to a new chain in PREROUTING =>
>>>>>> PORT_REDIRECT.
>>>>>> 
>>>>>> To avoid problems with (e.g.) transparent 'squid'
>>>>>> configurations,
>>>>>> redirection rules are added automatically before existing
>>>>>> 'squid' rules.
>>>>> 
>>>>> This message does unfortunately not say why this add-on would be
>>>>> useful. I am emphasising this again and again that it is not very
>>>>> important how something is done specially. That should be
>>>>> commented in the code and other implementation details should
>>>>> also be documented there.
>>>>> 
>>>>> As I have stated on this functionality many times before, I do
>>>>> not see why this is necessary at all.
>>>>> 
>>>>> Why is this an add-on?
>>>>> 
>>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>>> of code instead of adding many hundreds of lines?
>>>>> 
>>>>> Please can someone elaborate on this more?
>>>>> 
>>>>> -Michael
>>>>> 
>>>>>> Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
>>>>>> ---
>>>>>> config/portredir/EX-portredir.menu    |   6 +
>>>>>> config/portredir/lang/portredir.de.pl |  19 +
>>>>>> config/portredir/lang/portredir.en.pl |  19 +
>>>>>> config/portredir/portredir-backup     |   1 +
>>>>>> config/portredir/portredir.cgi        | 525
>>>>>> ++++++++++++++++++++++++++
>>>>>> config/rootfiles/common/misc-progs    |   1 +
>>>>>> config/rootfiles/packages/portredir   |  11 +
>>>>>> lfs/portredir                         |  85 +++++
>>>>>> make.sh                               |   1 +
>>>>>> src/initscripts/packages/portredir    | 191 ++++++++++
>>>>>> src/misc-progs/Makefile               |   2 +-
>>>>>> src/misc-progs/portredirctrl.c        |  47 +++
>>>>>> src/paks/portredir/install.sh         |  32 ++
>>>>>> src/paks/portredir/uninstall.sh       |  28 ++
>>>>>> src/paks/portredir/update.sh          |  26 ++
>>>>>> 15 files changed, 993 insertions(+), 1 deletion(-)
>>>>>> create mode 100644 config/portredir/EX-portredir.menu
>>>>>> create mode 100644 config/portredir/lang/portredir.de.pl
>>>>>> create mode 100644 config/portredir/lang/portredir.en.pl
>>>>>> create mode 100644 config/portredir/portredir-backup
>>>>>> create mode 100644 config/portredir/portredir.cgi
>>>>>> create mode 100644 config/rootfiles/packages/portredir
>>>>>> create mode 100644 lfs/portredir
>>>>>> create mode 100644 src/initscripts/packages/portredir
>>>>>> create mode 100644 src/misc-progs/portredirctrl.c
>>>>>> create mode 100644 src/paks/portredir/install.sh
>>>>>> create mode 100644 src/paks/portredir/uninstall.sh
>>>>>> create mode 100644 src/paks/portredir/update.sh
>>>>>> 
>>>>>> diff --git a/config/portredir/EX-portredir.menu
>>>>>> b/config/portredir/EX-portredir.menu
>>>>>> new file mode 100644
>>>>>> index 000000000..8376e8053
>>>>>> --- /dev/null
>>>>>> +++ b/config/portredir/EX-portredir.menu
>>>>>> @@ -0,0 +1,6 @@
>>>>>> +    $subfirewall->{'95.portredir'} = {
>>>>>> +                               'caption' =>
>>>>>> $Lang::tr{'portredir port redirections'},
>>>>>> +                               'uri' => '/cgi-
>>>>>> bin/portredir.cgi',
>>>>>> +                               'title' =>
>>>>>> "$Lang::tr{'portredir port redirections'}",
>>>>>> +                               'enabled' => 1
>>>>>> +                               };
>>>>>> diff --git a/config/portredir/lang/portredir.de.pl
>>>>>> b/config/portredir/lang/portredir.de.pl
>>>>>> new file mode 100644
>>>>>> index 000000000..b932d4a85
>>>>>> --- /dev/null
>>>>>> +++ b/config/portredir/lang/portredir.de.pl
>>>>>> @@ -0,0 +1,19 @@
>>>>>> +%tr = (
>>>>>> +%tr,
>>>>>> +'portredir enable addon' => 'Addon aktivieren',
>>>>>> +'portredir common settings' => 'Allgemeine Einstellungen',
>>>>>> +'portredir port redirections' => 'Portumleitungen',
>>>>>> +'portredir fw for interface' => 'Firewalloptionen für das
>>>>>> Interface',
>>>>>> +'portredir enable user redirections' => 'Aktiviere
>>>>>> benutzerdefinierte Portumleitungen',
>>>>>> +'portredir force local dns' => 'Erzwinge lokale DNS-Server',
>>>>>> +'portredir force local ntp' => 'Erzwinge lokale NTP-Server',
>>>>>> +'portredir custom redirections' => 'Benutzerdefinierte
>>>>>> Portumleitungen',
>>>>>> +'portredir remove rule' => 'Entferne Regel',
>>>>>> +'portredir add rule' => 'Hinzufügen',
>>>>>> +'portredir no entries' => 'Keine Einträge vorhanden.',
>>>>>> +'portredir invalid address' => 'Ungültige Host-Addresse.',
>>>>>> +'portredir empty input' => 'Fehlende Angabe: Bitte geben Sie
>>>>>> einen gültigen Host an.',
>>>>>> +'portredir save to activate' => 'Speichern, um Änderungen zu
>>>>>> aktivieren',
>>>>>> +);
>>>>>> +
>>>>>> +#EOF
>>>>>> diff --git a/config/portredir/lang/portredir.en.pl
>>>>>> b/config/portredir/lang/portredir.en.pl
>>>>>> new file mode 100644
>>>>>> index 000000000..f442f3eaa
>>>>>> --- /dev/null
>>>>>> +++ b/config/portredir/lang/portredir.en.pl
>>>>>> @@ -0,0 +1,19 @@
>>>>>> +%tr = (
>>>>>> +%tr,
>>>>>> +'portredir enable addon' => 'Enable addon',
>>>>>> +'portredir common settings' => 'Common settings',
>>>>>> +'portredir port redirections' => 'Port redirections',
>>>>>> +'portredir fw for interface' => 'Firewall options for
>>>>>> interface',
>>>>>> +'portredir enable user redirections' => 'Enable user port
>>>>>> redirections',
>>>>>> +'portredir force local dns' => 'Enforce local DNS servers',
>>>>>> +'portredir force local ntp' => 'Enforce local NTP servers',
>>>>>> +'portredir custom redirections' => 'Custom port redirections',
>>>>>> +'portredir remove rule' => 'Remove rule',
>>>>>> +'portredir add rule' => 'Add new',
>>>>>> +'portredir no entries' => 'No entries at the moment.',
>>>>>> +'portredir invalid address' => 'Invalid host address.',
>>>>>> +'portredir empty input' => 'Empty input: Please enter a valid
>>>>>> host.',
>>>>>> +'portredir save to activate' => 'Save to activate changes',
>>>>>> +);
>>>>>> +
>>>>>> +#EOF
>>>>>> diff --git a/config/portredir/portredir-backup
>>>>>> b/config/portredir/portredir-backup
>>>>>> new file mode 100644
>>>>>> index 000000000..bd2ada742
>>>>>> --- /dev/null
>>>>>> +++ b/config/portredir/portredir-backup
>>>>>> @@ -0,0 +1 @@
>>>>>> +/var/ipfire/portredir
>>>>>> diff --git a/config/portredir/portredir.cgi
>>>>>> b/config/portredir/portredir.cgi
>>>>>> new file mode 100644
>>>>>> index 000000000..4913dda3f
>>>>>> --- /dev/null
>>>>>> +++ b/config/portredir/portredir.cgi
>>>>>> @@ -0,0 +1,525 @@
>>>>>> +#!/usr/bin/perl
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# IPFire.org - A linux based
>>>>>> firewall                                         #
>>>>>> +# Copyright (C) 2021  IPFire Team 
>>>>>> <info@ipfire.org>                          #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# This program is free software: you can redistribute it
>>>>>> and/or modify        #
>>>>>> +# it under the terms of the GNU General Public License as
>>>>>> published by        #
>>>>>> +# the Free Software Foundation, either version 3 of the
>>>>>> License, or           #
>>>>>> +# (at your option) any later
>>>>>> version.                                         #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# This program is distributed in the hope that it will be
>>>>>> useful,             #
>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>> of              #
>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>> the               #
>>>>>> +# GNU General Public License for more
>>>>>> details.                                #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# You should have received a copy of the GNU General Public
>>>>>> License           #
>>>>>> +# along with this program.  If not, see <
>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +
>>>>>> +use strict;
>>>>>> +
>>>>>> +# enable only the following on debugging purpose
>>>>>> +use warnings;
>>>>>> +use CGI::Carp 'fatalsToBrowser';
>>>>>> +
>>>>>> +require '/var/ipfire/general-functions.pl';
>>>>>> +require "${General::swroot}/lang.pl";
>>>>>> +require "${General::swroot}/header.pl";
>>>>>> +
>>>>>> +# File declarations
>>>>>> +my $settingsfile = "${General::swroot}/portredir/settings";
>>>>>> +my $redirectsfile = "${General::swroot}/portredir/redirects";
>>>>>> +
>>>>>> +# Create empty settingsfiles if they does not exist yet
>>>>>> +unless (-e "$settingsfile") { system ("touch $settingsfile");
>>>>>> }
>>>>>> +unless (-e "$redirectsfile") { system ("touch
>>>>>> $redirectsfile"); }
>>>>>> +
>>>>>> +# load ipfire settings
>>>>>> +our %netsettings = ();
>>>>>> +our %color = ();
>>>>>> +&General::readhash("${General::swroot}/ethernet/settings",
>>>>>> \%netsettings);
>>>>>> +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include
>>>>>> /colors.txt", \%color);
>>>>>> +
>>>>>> +my %settings=();
>>>>>> +my %portredirs=();
>>>>>> +my %checked=(); # Checkbox manipulations
>>>>>> +my $errormessage='';
>>>>>> +my %selected=();
>>>>>> +our %redirects=();
>>>>>> +
>>>>>> +$settings{'ACTION'} = '';
>>>>>> +$settings{'REDIR_ENABLE_ADDON'}="off";
>>>>>> +$settings{'REDIR_CUSTOM_GREEN'}="off";
>>>>>> +$settings{'REDIR_CUSTOM_BLUE'}="off";
>>>>>> +$settings{'REDIR_CUSTOM_ORANGE'}="off";
>>>>>> +$settings{'REDIR_DNS_GREEN'}="off";
>>>>>> +$settings{'REDIR_NTP_GREEN'}="off";
>>>>>> +$settings{'REDIR_DNS_BLUE'}="off";
>>>>>> +$settings{'REDIR_NTP_BLUE'}="off";
>>>>>> +$settings{'REDIR_DNS_ORANGE'}="off";
>>>>>> +$settings{'REDIR_NTP_ORANGE'}="off";
>>>>>> +
>>>>>> +&Header::showhttpheaders();
>>>>>> +
>>>>>> +# Get GUI values
>>>>>> +&Header::getcgihash(\%settings);
>>>>>> +
>>>>>> +# Save action
>>>>>> +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
>>>>>> +
>>>>>> +       # If custom rules enabled, deactivate default rules on
>>>>>> interface
>>>>>> +       if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
>>>>>> +               $settings{'REDIR_DNS_GREEN'}="off";
>>>>>> +               $settings{'REDIR_NTP_GREEN'}="off";
>>>>>> +       }
>>>>>> +
>>>>>> +       if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
>>>>>> +               $settings{'REDIR_DNS_BLUE'}="off";
>>>>>> +               $settings{'REDIR_NTP_BLUE'}="off";
>>>>>> +       }
>>>>>> +
>>>>>> +       if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
>>>>>> +               $settings{'REDIR_DNS_ORANGE'}="off";
>>>>>> +               $settings{'REDIR_NTP_ORANGE'}="off";
>>>>>> +       }
>>>>>> +
>>>>>> +       &General::writehash($settingsfile, \%settings);
>>>>>> +
>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
>>>>>> +               system ('/usr/local/bin/portredirctrl restart
>>>>>>> /dev/null 2>&1');
>>>>>> +               system ('/usr/local/bin/portredirctrl enable
>>>>>>> /dev/null 2>&1');
>>>>>> +               &General::log('portredir addon: port
>>>>>> redirections enabled');
>>>>>> +       }
>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
>>>>>> +               system ('/usr/local/bin/portredirctrl disable
>>>>>>> /dev/null 2>&1');
>>>>>> +               system ('/usr/local/bin/portredirctrl stop
>>>>>>> /dev/null 2>&1');
>>>>>> +               &General::log('portredir addon: port
>>>>>> redirections disabled');
>>>>>> +       }
>>>>>> +
>>>>>> +# Add/edit an entry to the redirectsfile.
>>>>>> +
>>>>>> +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) ||
>>>>>> ($settings{'ACTION'} eq $Lang::tr{'update'})) {
>>>>>> +
>>>>>> +       # Check if any input has been performed.
>>>>>> +       if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
>>>>>> +
>>>>>> +               # Check if the given input is no valid IP-
>>>>>> address, display an error message.
>>>>>> +               if
>>>>>> (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
>>>>>> +                       $errormessage = "$Lang::tr{'portredir
>>>>>> invalid address'}";
>>>>>> +               }
>>>>>> +       } else {
>>>>>> +               $errormessage = "$Lang::tr{'portredir empty
>>>>>> input'}";
>>>>>> +       }
>>>>>> +
>>>>>> +       # Go further if there was no error.
>>>>>> +       if ($errormessage eq '') {
>>>>>> +               my %redirects = ();
>>>>>> +               my $id;
>>>>>> +               my $status;
>>>>>> +
>>>>>> +               # Assign hash values.
>>>>>> +               my $new_entry_interface =
>>>>>> $settings{'REDIR_ENTRY_INTERFACE'};
>>>>>> +               my $new_entry_protocol =
>>>>>> $settings{'REDIR_ENTRY_PROTOCOL'};
>>>>>> +               my $new_entry_port =
>>>>>> $settings{'REDIR_ENTRY_PORT'};
>>>>>> +               my $new_entry_address =
>>>>>> $settings{'REDIR_ENTRY_ADDRESS'};
>>>>>> +               my $new_entry_remark =
>>>>>> $settings{'REDIR_ENTRY_REMARK'};
>>>>>> +
>>>>>> +               # Read-in redirectsfile.
>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>> \%redirects);
>>>>>> +
>>>>>> +               # Check if we should edit an existing entry and
>>>>>> got an ID.
>>>>>> +               if (($settings{'ACTION'} eq
>>>>>> $Lang::tr{'update'}) && ($settings{'ID'})) {
>>>>>> +                       # Assin the provided id.
>>>>>> +                       $id = $settings{'ID'};
>>>>>> +
>>>>>> +                       # Undef the given ID.
>>>>>> +                       undef($settings{'ID'});
>>>>>> +
>>>>>> +                       # Grab the configured status of the
>>>>>> corresponding entry.
>>>>>> +                       $status = $redirects{$id}[4];
>>>>>> +               } else {
>>>>>> +                       # Each newly added entry automatically
>>>>>> should be enabled.
>>>>>> +                       $status = "enabled";
>>>>>> +
>>>>>> +                       # Generate the ID for the new entry.
>>>>>> +                       #
>>>>>> +                       # Sort the keys by their ID and store
>>>>>> them in an array.
>>>>>> +                       my @keys = sort { $a <=> $b } keys
>>>>>> %redirects;
>>>>>> +
>>>>>> +                       # Reverse the key array.
>>>>>> +                       my @reversed = reverse(@keys);
>>>>>> +
>>>>>> +                       # Obtain the last used id.
>>>>>> +                       my $last_id = @reversed[0];
>>>>>> +
>>>>>> +                       # Increase the last id by one and use
>>>>>> it as id for the new entry.
>>>>>> +                       $id = ++$last_id;
>>>>>> +               }
>>>>>> +
>>>>>> +               # Add/Modify the entry to/in the redirects
>>>>>> hash.
>>>>>> +               $redirects{$id} = ["$new_entry_interface",
>>>>>> "$new_entry_protocol", "$new_entry_port",
>>>>>> "$new_entry_address","$status", "$new_entry_remark"];
>>>>>> +
>>>>>> +               # Write the changed redirects hash to the
>>>>>> redirects file.
>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>> \%redirects);
>>>>>> +       }
>>>>>> +
>>>>>> +# Toggle Enabled/Disabled for an existing entry on the
>>>>>> redirects list.
>>>>>> +
>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable
>>>>>> disable'}) {
>>>>>> +       my %redirects = ();
>>>>>> +
>>>>>> +       # Only go further, if an ID has been passed.
>>>>>> +       if ($settings{'ID'}) {
>>>>>> +               # Assign the given ID.
>>>>>> +               my $id = $settings{'ID'};
>>>>>> +
>>>>>> +               # Undef the given ID.
>>>>>> +               undef($settings{'ID'});
>>>>>> +
>>>>>> +               # Read-in ignoredfile.
>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>> \%redirects);
>>>>>> +
>>>>>> +               # Grab the configured status of the
>>>>>> corresponding entry.
>>>>>> +               my $status = $redirects{$id}[4];
>>>>>> +
>>>>>> +               # Switch the status.
>>>>>> +               if ($status eq "disabled") {
>>>>>> +                       $status = "enabled";
>>>>>> +               } else {
>>>>>> +                       $status = "disabled";
>>>>>> +               }
>>>>>> +
>>>>>> +               # Modify the status of the existing entry.
>>>>>> +               $redirects{$id} = ["$redirects{$id}[0]",
>>>>>> "$redirects{$id}[1]", "$redirects{$id}[2]",
>>>>>> "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
>>>>>> +
>>>>>> +               # Write the changed ignored hash to the
>>>>>> redirects file.
>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>> \%redirects);
>>>>>> +       }
>>>>>> +
>>>>>> +# Remove entry from redirects list.
>>>>>> +
>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
>>>>>> +       my %redirects = ();
>>>>>> +
>>>>>> +       # Read-in redirectsfile.
>>>>>> +       &General::readhasharray($redirectsfile, \%redirects);
>>>>>> +
>>>>>> +       # move data on key up
>>>>>> +       foreach my $key (sort keys %redirects) {
>>>>>> +               if ($key >= $settings{'ID'}) {
>>>>>> +                       my $next = $key + 1;
>>>>>> +                       if (exists $redirects{$next}) {
>>>>>> +                               foreach my $i (0 ..
>>>>>> $#{$redirects{$next}}) { $redirects{$key}[$i] =
>>>>>> $redirects{$next}[$i]; }
>>>>>> +                       }
>>>>>> +               }
>>>>>> +       }
>>>>>> +
>>>>>> +       my $last_key = (sort {$a <=> $b} keys %redirects)[-1];
>>>>>> +       delete $redirects{$last_key};
>>>>>> +
>>>>>> +       # Undef the given ID.
>>>>>> +       undef($settings{'ID'});
>>>>>> +
>>>>>> +       # Write the changed redirects hash to file.
>>>>>> +       &General::writehasharray($redirectsfile, \%redirects);
>>>>>> +}
>>>>>> +
>>>>>> +# Load settings from file
>>>>>> +&General::readhash($settingsfile, \%settings);
>>>>>> +&General::readhasharray($redirectsfile, \%redirects);
>>>>>> +
>>>>>> +# Call functions to generate whole page.
>>>>>> +&Header::openpage($Lang::tr{'portredir port redirections'}, 1,
>>>>>> '');
>>>>>> +&Header::openbigbox('100%', 'left', '', $errormessage);
>>>>>> +
>>>>>> +if ($errormessage) {
>>>>>> +        &Header::openbox('100%', 'left', $Lang::tr{'warning
>>>>>> messages'});
>>>>>> +        print "<font color='red'>$errormessage&nbsp;</font>";
>>>>>> +        &Header::closebox();
>>>>>> +}
>>>>>> +
>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_ADDON'}
>>>>>> } = "checked='checked'";
>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_GREEN'}
>>>>>> } = "checked='checked'";
>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BLUE'}}
>>>>>> = "checked='checked'";
>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_ORANGE
>>>>>> '}} = "checked='checked'";
>>>>>> +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
>>>>>> +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
>>>>>> +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}} =
>>>>>> "checked='checked'";
>>>>>> +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
>>>>>> +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
>>>>>> +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}} =
>>>>>> "checked='checked'";
>>>>>> +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
>>>>>> +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
>>>>>> +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}} =
>>>>>> "checked='checked'";
>>>>>> +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
>>>>>> +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
>>>>>> +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}} =
>>>>>> "checked='checked'";
>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
>>>>>> +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE'}} =
>>>>>> "checked='checked'";
>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
>>>>>> +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE'}} =
>>>>>> "checked='checked'";
>>>>>> +
>>>>>> +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTRY_INTE
>>>>>> RFACE'}} = 'selected';
>>>>>> +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY_PROTO
>>>>>> COL'}} = 'selected';
>>>>>> +
>>>>>> +&showMainBox();
>>>>>> +&showRedirectsBox();
>>>>>> +
>>>>>> +&Header::closebigbox();
>>>>>> +&Header::closepage();
>>>>>> +
>>>>>> +# Function to show main settings and options.
>>>>>> +sub showMainBox() {
>>>>>> +
>>>>>> +       &Header::openbox('100%', 'center',
>>>>>> "$Lang::tr{'settings'}");
>>>>>> +       print "<form method='post'
>>>>>> action='$ENV{'SCRIPT_NAME'}'>";
>>>>>> +
>>>>>> +print <<END;
>>>>>> +<table width='80%' cellspacing='0' border='0'>
>>>>>> +       <tr><td colspan='2' class='base'
>>>>>> bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir common
>>>>>> settings'}</b></td></tr
>>>>>> +       <tr>
>>>>>> +               <td width='25%'
>>>>>> class='base'>$Lang::tr{'portredir enable addon'}:</td>
>>>>>> +               <td><input type='checkbox'
>>>>>> name='REDIR_ENABLE_ADDON' $checked{'REDIR_ENABLE_ADDON'}{'on'}
>>>>>> /></td>
>>>>>> +       </tr>
>>>>>> +       <tr><td colspan='2'></td></tr>
>>>>>> +       <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>> +END
>>>>>> +
>>>>>> +       # create html table with header line 1
>>>>>> +       print "<table width='80%' cellspacing='0'
>>>>>> border='0'><tr>";
>>>>>> +       print "<th class='base' width='40%'
>>>>>> align='left'><b>$Lang::tr{'portredir fw for interface'}</th>";
>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<th
>>>>>> class='base' width='10%'><b><font
>>>>>> color=green>$Lang::tr{'green'}</font></th>";
>>>>>> +               } else {                 print "<th
>>>>>> class='base' width='10%'></font></th>"; }
>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<th
>>>>>> class='base' width='10%'><b><font
>>>>>> color=blue>$Lang::tr{'blue'}</font></th>";
>>>>>> +               } else {                 print "<th
>>>>>> class='base' width='10%'></font></th>"; }
>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<th
>>>>>> class='base' width='10%'><b><font
>>>>>> color=orange>$Lang::tr{'orange'}</font></th>";
>>>>>> +               } else {                 print "<th
>>>>>> class='base' width='10%'></font></th>"; }
>>>>>> +
>>>>>> +       # the empty right row
>>>>>> +       print "<th class='base'
>>>>>> width='30%'><td></td></th></tr>";
>>>>>> +
>>>>>> +       # line 2
>>>>>> +       print "<tr><td>$Lang::tr{'portredir force local
>>>>>> dns'}</td>";
>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_DNS_GREEN'
>>>>>> $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print
>>>>>> "<td></td>";}
>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_DNS_BLUE' $checked{'REDIR_DNS_BLUE'}{'on'}></td>";}
>>>>>> else { print "<td></td>";}
>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_DNS_ORANGE'
>>>>>> $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print
>>>>>> "<td></td>";}
>>>>>> +
>>>>>> +       # line 3
>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir force local
>>>>>> ntp'}</td>";
>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_NTP_GREEN'
>>>>>> $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print
>>>>>> "<td></td>";}
>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_NTP_BLUE' $checked{'REDIR_NTP_BLUE'}{'on'}></td>";}
>>>>>> else { print "<td></td>";}
>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_NTP_ORANGE'
>>>>>> $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print
>>>>>> "<td></td>";}
>>>>>> +
>>>>>> +       # line 4
>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir enable user
>>>>>> redirections'}</td>";
>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_CUSTOM_GREEN'
>>>>>> $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else { print
>>>>>> "<td></td>";}
>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_CUSTOM_BLUE'
>>>>>> $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print
>>>>>> "<td></td>";}
>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_CUSTOM_ORANGE'
>>>>>> $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else { print
>>>>>> "<td></td>";}
>>>>>> +
>>>>>> +       print <<END;
>>>>>> +       </tr></table>
>>>>>> +       <table width='80%' cellspacing='0' border='0'>
>>>>>> +               <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>> +               <tr><td align='left'><b>$Lang::tr{'portredir
>>>>>> save to activate'}</b></td><td width='5%' align='center'><input
>>>>>> type='submit' name='ACTION' value='  $Lang::tr{'save'} 
>>>>>> '></td></tr>
>>>>>> +       </table></form>
>>>>>> +END
>>>>>> +
>>>>>> +&Header::closebox();
>>>>>> +}
>>>>>> +
>>>>>> +# Function to show elements of the redirects file and allow to
>>>>>> add or remove single members of it.
>>>>>> +sub showRedirectsBox() {
>>>>>> +        &Header::openbox('100%', 'center',
>>>>>> "$Lang::tr{'portredir custom redirections'}");
>>>>>> +
>>>>>> +       print <<END;
>>>>>> +               <table width='80%' cellspacing='1' border='0'>
>>>>>> +                       <tr>
>>>>>> +                               <td class='base'
>>>>>> bgcolor='$color{'color20'}'
>>>>>> align='center'><b>$Lang::tr{'interface'}</b></td>
>>>>>> +                               <td class='base'
>>>>>> bgcolor='$color{'color20'}'
>>>>>> align='center'><b>$Lang::tr{'protocol'}</b></td>
>>>>>> +                               <td class='base'
>>>>>> bgcolor='$color{'color20'}'
>>>>>> align='center'><b>$Lang::tr{'port'}</b></td>
>>>>>> +                               <td class='base'
>>>>>> bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'ip
>>>>>> address'}</b></td>
>>>>>> +                               <td class='base'
>>>>>> bgcolor='$color{'color20'}'
>>>>>> align='center'><b>$Lang::tr{'remark'}</b></td>
>>>>>> +                               <td class='base' colspan='3'
>>>>>> bgcolor='$color{'color20'}'></td>
>>>>>> +                       </tr>
>>>>>> +END
>>>>>> +                       # Check if some rules have been added
>>>>>> to be redirects.
>>>>>> +                       if (keys (%redirects)) {
>>>>>> +                               my $col = "";
>>>>>> +
>>>>>> +                               # List all entries of the hash.
>>>>>> +                               foreach my $key (sort keys
>>>>>> %redirects){
>>>>>> +
>>>>>> +                                       # Assign data array
>>>>>> positions to some nice variable names.
>>>>>> +                                       my $interface =
>>>>>> $redirects{$key}[0];
>>>>>> +                                       my $protocol =
>>>>>> $redirects{$key}[1];
>>>>>> +                                       my $port  =
>>>>>> $redirects{$key}[2];
>>>>>> +                                       my $address =
>>>>>> $redirects{$key}[3];
>>>>>> +                                       my $status  =
>>>>>> $redirects{$key}[4];
>>>>>> +                                       my $remark  =
>>>>>> $redirects{$key}[5];
>>>>>> +
>>>>>> +                                       # Check if the key (id)
>>>>>> number is even or not.
>>>>>> +                                       if ($settings{'ID'} eq
>>>>>> $key) {
>>>>>> +                                               $col="bgcolor='
>>>>>> ${Header::colouryellow}'";
>>>>>> +                                       } elsif ($key % 2) {
>>>>>> +                                               $col="bgcolor='
>>>>>> $color{'color22'}'";
>>>>>> +                                       } else {
>>>>>> +                                               $col="bgcolor='
>>>>>> $color{'color20'}'";
>>>>>> +                                       }
>>>>>> +
>>>>>> +                                       # Choose icon for the
>>>>>> checkbox.
>>>>>> +                                       my $gif;
>>>>>> +                                       my $gdesc;
>>>>>> +
>>>>>> +                                       # Check if the status
>>>>>> is enabled and select the correct image and description.
>>>>>> +                                       if ($status eq
>>>>>> 'enabled' ) {
>>>>>> +                                               $gif =
>>>>>> 'on.gif';
>>>>>> +                                               $gdesc =
>>>>>> $Lang::tr{'click to disable'};
>>>>>> +                                       } else {
>>>>>> +                                               $gif =
>>>>>> 'off.gif';
>>>>>> +                                               $gdesc =
>>>>>> $Lang::tr{'click to enable'};
>>>>>> +                                       }
>>>>>> +
>>>>>> +                                       print <<END;
>>>>>> +                                       <tr>
>>>>>> +                                               <td width='15%'
>>>>>> class='base' align='center' $col><b><font
>>>>>> color=$interface>$Lang::tr{$interface}</font></b></td>
>>>>>> +                                               <td width='10%'
>>>>>> class='base' align='center' $col>$protocol</td>
>>>>>> +                                               <td width='10%'
>>>>>> class='base' align='center' $col>$port</td>
>>>>>> +                                               <td width='15%'
>>>>>> class='base' align='center' $col>&nbsp;$address</td>
>>>>>> +                                               <td width='40%'
>>>>>> class='base' align='center' $col>&nbsp;$remark</td>
>>>>>> +                                               <td
>>>>>> align='center' $col>
>>>>>> +                                                       <form
>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>> +                                                              
>>>>>>  <input type='hidden' name='ACTION' value='$Lang::tr{'toggle
>>>>>> enable disable'}' />
>>>>>> +                                                              
>>>>>>  <input type='image' name='$Lang::tr{'toggle enable disable'}'
>>>>>> src='/images/$gif' alt='$gdesc' title='$gdesc' />
>>>>>> +                                                              
>>>>>>  <input type='hidden' name='ID' value='$key' />
>>>>>> +                                                       </form>
>>>>>> +                                               </td>
>>>>>> +                                               <td
>>>>>> align='center' $col>
>>>>>> +                                                       <form
>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>> +                                                              
>>>>>>  <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}'
>>>>>> />
>>>>>> +                                                              
>>>>>>  <input type='image' name='$Lang::tr{'edit'}'
>>>>>> src='/images/edit.gif' alt='$Lang::tr{'edit'}'
>>>>>> title='$Lang::tr{'edit'}' />
>>>>>> +                                                              
>>>>>>  <input type='hidden' name='ID' value='$key' />
>>>>>> +                                                       </form>
>>>>>> +                                               </td>
>>>>>> +                                               <td
>>>>>> align='center' $col>
>>>>>> +                                                       <form
>>>>>> method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
>>>>>> +                                                              
>>>>>>  <input type='image' name='$Lang::tr{'remove'}'
>>>>>> src='/images/delete.gif' title='$Lang::tr{'remove'}'
>>>>>> alt='$Lang::tr{'remove'}'>
>>>>>> +                                                              
>>>>>>  <input type='hidden' name='ID' value='$key'>
>>>>>> +                                                              
>>>>>>  <input type='hidden' name='ACTION'
>>>>>> value='$Lang::tr{'remove'}'>
>>>>>> +                                                       </form>
>>>>>> +                                               </td>
>>>>>> +                                       </tr>
>>>>>> +END
>>>>>> +                               }
>>>>>> +                       } else {
>>>>>> +                               # Print notice that currently
>>>>>> no ports are redirected.
>>>>>> +                               print "<tr>\n";
>>>>>> +                               print "<td class='base'
>>>>>> colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
>>>>>> +                               print "</tr>\n";
>>>>>> +                       }
>>>>>> +
>>>>>> +               print "</table>\n";
>>>>>> +
>>>>>> +       # Section to add new elements or edit existing ones.
>>>>>> +       print <<END;
>>>>>> +       <br>
>>>>>> +       <hr>
>>>>>> +       <br>
>>>>>> +       <div align='center'>
>>>>>> +               <table width='100%' cellspacing='0' border='0'>
>>>>>> +END
>>>>>> +
>>>>>> +       # Assign correct headline and button text.
>>>>>> +       my $buttontext;
>>>>>> +       my $entry_interface;
>>>>>> +       my $entry_protocol;
>>>>>> +       my $entry_port;
>>>>>> +       my $entry_address;
>>>>>> +       my $entry_remark;
>>>>>> +
>>>>>> +       # Check if an ID (key) has been given, in this case an
>>>>>> existing entry should be edited.
>>>>>> +       if ($settings{'ID'} ne '') {
>>>>>> +               $buttontext = $Lang::tr{'update'};
>>>>>> +               print "<tr><td class='boldbase'
>>>>>> colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
>>>>>> +
>>>>>> +               # Grab address and remark for the given key.
>>>>>> +               $entry_interface =
>>>>>> $redirects{$settings{'ID'}}[0];
>>>>>> +               $entry_protocol =
>>>>>> $redirects{$settings{'ID'}}[1];
>>>>>> +               $entry_port = $redirects{$settings{'ID'}}[2];
>>>>>> +               $entry_address =
>>>>>> $redirects{$settings{'ID'}}[3];
>>>>>> +               $entry_remark = $redirects{$settings{'ID'}}[5];
>>>>>> +
>>>>>> +       } else {
>>>>>> +               $buttontext = $Lang::tr{'add'};
>>>>>> +               print "<tr><td class='boldbase'
>>>>>> colspan='11'><b>$Lang::tr{'dnsforward add a new
>>>>>> entry'}</b></td></tr>\n";
>>>>>> +               print "<tr><td>&nbsp</td></tr>\n";
>>>>>> +       }
>>>>>> +
>>>>>> +       print <<END;
>>>>>> +                       <tr>
>>>>>> +                               <td class='base' width='1%' 
>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>> +                               <td class='base' width='15%'
>>>>>> bgcolor='$color{'color22'}'
>>>>>> align='left'><b>$Lang::tr{'interface'}</b></td>
>>>>>> +                               <td class='base' width='10%'
>>>>>> bgcolor='$color{'color22'}'
>>>>>> align='left'><b>$Lang::tr{'protocol'}</b></td>
>>>>>> +                               <td class='base' width='10%'
>>>>>> bgcolor='$color{'color22'}'
>>>>>> align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
>>>>>> +                               <td class='base' width='13%'
>>>>>> bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'ip
>>>>>> address'}</b></td>
>>>>>> +                               <td class='base' width='30%'
>>>>>> bgcolor='$color{'color22'}'
>>>>>> align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
>>>>>> +                               <td class='base' width='15%'
>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>> +                               <td class='base' width='1%' 
>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>> +                       </tr>
>>>>>> +
>>>>>> +                       <form method='post'
>>>>>> action='$ENV{'SCRIPT_NAME'}'>
>>>>>> +                       <input type='hidden' name='ID'
>>>>>> value='$settings{'ID'}'>
>>>>>> +                       <tr>
>>>>>> +                               <td class='base'></td>
>>>>>> +                               <td><select style='width:90px;'
>>>>>> id='interface' name='REDIR_ENTRY_INTERFACE'>
>>>>>> +END
>>>>>> +                               if ($netsettings{'GREEN_DEV'})
>>>>>> {
>>>>>> +                                       if ($entry_interface eq
>>>>>> "green") {
>>>>>> +                                               print "<option
>>>>>> value='green' selected='selected'
>>>>>> {'green'}>$Lang::tr{'green'}</option>";
>>>>>> +                                       } else { print "<option
>>>>>> value='green' {'green'}>$Lang::tr{'green'}</option>";}
>>>>>> +                               }
>>>>>> +                               if ($netsettings{'BLUE_DEV'}) {
>>>>>> +                                       if ($entry_interface eq
>>>>>> "blue") { 
>>>>>> +                                               print "<option
>>>>>> value='blue' selected='selected'
>>>>>> {'blue'}>$Lang::tr{'blue'}</option>";
>>>>>> +                                       } else { print "<option
>>>>>> value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
>>>>>> +                               }
>>>>>> +                               if ($netsettings{'ORANGE_DEV'})
>>>>>> {
>>>>>> +                                       if ($entry_interface eq
>>>>>> "orange") { 
>>>>>> +                                               print "<option
>>>>>> value='orange' selected='selected'
>>>>>> {'orange'}>$Lang::tr{'orange'}</option>";
>>>>>> +                                       } else { print "<option
>>>>>> value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
>>>>>> +                               }
>>>>>> +
>>>>>> +                       print "</select><td><select
>>>>>> style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
>>>>>> +                       if ((!$entry_protocol) ||
>>>>>> ($entry_protocol eq "tcp")) {
>>>>>> +                               print "<option
>>>>>> selected='selected' id='protocol' value='tcp'
>>>>>> {'tcp'}>tcp</option>";
>>>>>> +                               print "<option value='udp'
>>>>>> {'udp'}>udp</option>";
>>>>>> +                       } elsif ($entry_protocol eq "udp") {
>>>>>> +                               print "<option value='tcp'
>>>>>> {'tcp'}>tcp</option>";
>>>>>> +                               print "<option
>>>>>> selected='selected' value='udp' {'udp'}>udp</option>";
>>>>>> +                       }
>>>>>> +       print <<END;
>>>>>> +                               </select></td>
>>>>>> +                               <td><input type='text'
>>>>>> name='REDIR_ENTRY_PORT'    value='$entry_port'   
>>>>>> size='4'></td>
>>>>>> +                               <td><input type='text'
>>>>>> name='REDIR_ENTRY_ADDRESS' value='$entry_address'
>>>>>> size='14'></td>
>>>>>> +                               <td><input type='text'
>>>>>> name='REDIR_ENTRY_REMARK'  value='$entry_remark' 
>>>>>> size='35'></td>
>>>>>> +                               <td width='10%'
>>>>>> align='center'><input type='submit' name='ACTION' value=' 
>>>>>> $buttontext  '></td>
>>>>>> +                               <td class='base'></td>
>>>>>> +                       </tr>
>>>>>> +                       </form>
>>>>>> +               </table>
>>>>>> +       </div>
>>>>>> +END
>>>>>> +       &Header::closebox();
>>>>>> +}
>>>>>> diff --git a/config/rootfiles/common/misc-progs
>>>>>> b/config/rootfiles/common/misc-progs
>>>>>> index d6594b3f8..fbad2af8b 100644
>>>>>> --- a/config/rootfiles/common/misc-progs
>>>>>> +++ b/config/rootfiles/common/misc-progs
>>>>>> @@ -17,6 +17,7 @@ usr/local/bin/logwatch
>>>>>> #usr/local/bin/mpfirectrl
>>>>>> usr/local/bin/openvpnctrl
>>>>>> usr/local/bin/pakfire
>>>>>> +#usr/local/bin/portredirctrl
>>>>>> usr/local/bin/qosctrl
>>>>>> usr/local/bin/rebuildhosts
>>>>>> usr/local/bin/rebuildroutes
>>>>>> diff --git a/config/rootfiles/packages/portredir
>>>>>> b/config/rootfiles/packages/portredir
>>>>>> new file mode 100644
>>>>>> index 000000000..4b4ba8366
>>>>>> --- /dev/null
>>>>>> +++ b/config/rootfiles/packages/portredir
>>>>>> @@ -0,0 +1,11 @@
>>>>>> +etc/rc.d/init.d/portredir
>>>>>> +etc/rc.d/rc0.d/K77portredir
>>>>>> +etc/rc.d/rc3.d/S23portredir
>>>>>> +etc/rc.d/rc6.d/K77portredir
>>>>>> +srv/web/ipfire/cgi-bin/portredir.cgi
>>>>>> +usr/local/bin/portredirctrl
>>>>>> +var/ipfire/addon-lang/portredir.de.pl
>>>>>> +var/ipfire/addon-lang/portredir.en.pl
>>>>>> +var/ipfire/backup/addons/includes/portredir
>>>>>> +var/ipfire/menu.d/EX-portredir.menu
>>>>>> +var/ipfire/portredir
>>>>>> diff --git a/lfs/portredir b/lfs/portredir
>>>>>> new file mode 100644
>>>>>> index 000000000..a4911f71f
>>>>>> --- /dev/null
>>>>>> +++ b/lfs/portredir
>>>>>> @@ -0,0 +1,85 @@
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# IPFire.org - A linux based
>>>>>> firewall                                         #
>>>>>> +# Copyright (C) 2007-2021  IPFire Team 
>>>>>> <info@ipfire.org>                     #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# This program is free software: you can redistribute it
>>>>>> and/or modify        #
>>>>>> +# it under the terms of the GNU General Public License as
>>>>>> published by        #
>>>>>> +# the Free Software Foundation, either version 3 of the
>>>>>> License, or           #
>>>>>> +# (at your option) any later
>>>>>> version.                                         #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# This program is distributed in the hope that it will be
>>>>>> useful,             #
>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>> of              #
>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>> the               #
>>>>>> +# GNU General Public License for more
>>>>>> details.                                #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# You should have received a copy of the GNU General Public
>>>>>> License           #
>>>>>> +# along with this program.  If not, see <
>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +# Definitions
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +
>>>>>> +include Config
>>>>>> +
>>>>>> +VER        = 1.0
>>>>>> +
>>>>>> +THISAPP    = portredir-$(VER)
>>>>>> +DIR_APP    = $(DIR_SRC)/$(THISAPP)
>>>>>> +TARGET     = $(DIR_INFO)/$(THISAPP)
>>>>>> +PROG       = portredir
>>>>>> +PAK_VER    = 1
>>>>>> +
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +# Top-level Rules
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +
>>>>>> +install : $(TARGET)
>>>>>> +
>>>>>> +check :
>>>>>> +
>>>>>> +download :
>>>>>> +
>>>>>> +md5 :
>>>>>> +
>>>>>> +dist: 
>>>>>> +       @$(PAK)
>>>>>> +
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +# Installation Details
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +
>>>>>> +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
>>>>>> +       @$(PREBUILD)
>>>>>> +       @rm -rf $(DIR_APP) && cd $(DIR_SRC)
>>>>>> +
>>>>>> +       #install cgi 
>>>>>> +       install -v -m 755 $(DIR_CONF)/portredir/portredir.cgi
>>>>>> /srv/web/ipfire/cgi-bin/
>>>>>> +
>>>>>> +       #create configuration dir 
>>>>>> +       -mkdir -pv /var/ipfire/portredir/
>>>>>> +       chown -R nobody:nobody /var/ipfire/portredir/
>>>>>> +
>>>>>> +       # Install include file for backup
>>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/portredir-
>>>>>> backup /var/ipfire/backup/addons/includes/portredir
>>>>>> +
>>>>>> +       # Install menu file
>>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/EX-
>>>>>> portredir.menu /var/ipfire/menu.d/
>>>>>> +       chown nobody:nobody /var/ipfire/menu.d/EX-
>>>>>> portredir.menu
>>>>>> +
>>>>>> +       # Install addon-specific language-files
>>>>>> +       install -v -m 644
>>>>>> $(DIR_CONF)/portredir/lang/portredir.*.pl /var/ipfire/addon-
>>>>>> lang/
>>>>>> +
>>>>>> +       #install initscripts
>>>>>> +       $(call INSTALL_INITSCRIPT,portredir)
>>>>>> +
>>>>>> +       # Create symlinks for runlevel interaction.
>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>> /etc/rc.d/rc3.d/S23portredir
>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>> /etc/rc.d/rc0.d/K77portredir
>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>> /etc/rc.d/rc6.d/K77portredir
>>>>>> +
>>>>>> +       @rm -rf $(DIR_APP)
>>>>>> +       @$(POSTBUILD)
>>>>>> +
>>>>>> diff --git a/make.sh b/make.sh
>>>>>> index fc03ebcd5..ab9fe881a 100755
>>>>>> --- a/make.sh
>>>>>> +++ b/make.sh
>>>>>> @@ -1623,6 +1623,7 @@ buildipfire() {
>>>>>>  lfsmake2 socat
>>>>>>  lfsmake2 libcdada
>>>>>>  lfsmake2 pmacct
>>>>>> +  lfsmake2 portredir
>>>>>> }
>>>>>> 
>>>>>> buildinstaller() {
>>>>>> diff --git a/src/initscripts/packages/portredir
>>>>>> b/src/initscripts/packages/portredir
>>>>>> new file mode 100644
>>>>>> index 000000000..cc57fb9cc
>>>>>> --- /dev/null
>>>>>> +++ b/src/initscripts/packages/portredir
>>>>>> @@ -0,0 +1,191 @@
>>>>>> +#!/bin/sh
>>>>>> +##############################################################
>>>>>> ##########
>>>>>> +# Begin $rc_base/init.d/portredir
>>>>>> +#
>>>>>> +# Description : portredir init script for DNS/NTP and custom 
>>>>>> +#              port redirection rules
>>>>>> +#
>>>>>> +##############################################################
>>>>>> ##########
>>>>>> +
>>>>>> +. /etc/sysconfig/rc
>>>>>> +. ${rc_functions}
>>>>>> +
>>>>>> +IPT="/sbin/iptables";
>>>>>> +parent_chain="PREROUTING";
>>>>>> +chain="PORT_REDIRECT";
>>>>>> +
>>>>>> +confdir="/var/ipfire/portredir";
>>>>>> +settingsfile="${confdir}/settings";
>>>>>> +redirectsfile="${confdir}/redirects";
>>>>>> +SYSLOG="NO";
>>>>>> +VERBOSE="NO";
>>>>>> +
>>>>>> +eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings);
>>>>>> +eval $(/usr/local/bin/readhash ${settingsfile});
>>>>>> +
>>>>>> +logtext() {
>>>>>> +       if [ "${SYSLOG}" = "YES" ]; then logger -t "portredir"
>>>>>> ${1}; fi;
>>>>>> +       if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
>>>>>> +
>>>>>> +create_chain() {
>>>>>> +
>>>>>> +       local line=$(${IPT} -t nat -L ${parent_chain} --line-
>>>>>> numbers |grep "SQUID" |awk '{printf($1)}');
>>>>>> +
>>>>>> +       if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z
>>>>>> "${REDIR_ENABLE_ADDON}" ]]; then
>>>>>> +               logtext "addon not enabled in web
>>>>>> interface...";
>>>>>> +               echo "Portredir addon not enabled in web
>>>>>> interface...";
>>>>>> +               exit 0;
>>>>>> +       fi;
>>>>>> +
>>>>>> +       if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>>> ${chain})" ]; then
>>>>>> +               ${IPT} -t nat -N ${chain};
>>>>>> +
>>>>>> +               if [ ! -z "${line}" ]; then
>>>>>> +                       logtext "create chain ${chain} and link
>>>>>> in ${parent_chain} at position ${line}...";
>>>>>> +                       ${IPT} -t nat -I ${parent_chain}
>>>>>> ${line} -j ${chain};
>>>>>> +               else
>>>>>> +                       logtext "create chain ${chain} and link
>>>>>> in ${parent_chain} at last position...";
>>>>>> +                       ${IPT} -t nat -A ${parent_chain} -j
>>>>>> ${chain};
>>>>>> +               fi
>>>>>> +       else
>>>>>> +               return 1;
>>>>>> +       fi;
>>>>>> +       return 0;
>>>>>> +}
>>>>>> +
>>>>>> +remove_chain() {
>>>>>> +       if [ ! -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>>> ${chain})" ]; then
>>>>>> +               logtext "remove chain ${chain} and link in
>>>>>> ${parent_chain} from system...";
>>>>>> +               ${IPT} -t nat -D "${parent_chain}" -j ${chain};
>>>>>> +               ${IPT} -t nat -F ${chain};
>>>>>> +               ${IPT} -t nat -X ${chain};
>>>>>> +       else
>>>>>> +               return 1;
>>>>>> +       fi;
>>>>>> +       return 0;
>>>>>> +}
>>>>>> +
>>>>>> +activate_custom_redirections() {
>>>>>> +       
>>>>>> +       local array=();
>>>>>> +       local redirects=();
>>>>>> +       local i;
>>>>>> +       index=();
>>>>>> +       iface=();
>>>>>> +       protocol=();
>>>>>> +       port=();
>>>>>> +       targetip=();
>>>>>> +       enabled=();
>>>>>> +
>>>>>> +       IFS=$'\n' read -d '' -ra redirects < ${redirectsfile};
>>>>>> +
>>>>>> +       for i in "${!redirects[@]}"
>>>>>> +       do
>>>>>> +               IFS=$',' read -ra array <<< ${redirects[i]};
>>>>>> +               index[i]=${array[0]};
>>>>>> +               iface[i]=${array[1]};
>>>>>> +               protocol[i]=${array[2]};
>>>>>> +               port[i]=${array[3]};
>>>>>> +               targetip[i]=${array[4]};
>>>>>> +               enabled[i]=${array[5]};
>>>>>> +       done
>>>>>> +
>>>>>> +       for i in "${!index[@]}"
>>>>>> +       do
>>>>>> +               if [[ ! -z "${GREEN_DEV}" &&  "${iface[i]}" =
>>>>>> "green" && "${enabled[i]}" = "enabled" ]]; then
>>>>>> +
>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>> ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>> ${port[i]} ";
>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>> ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>> ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>> ${port[i]} -j REDIRECT;
>>>>>> +               fi
>>>>>> +               if [[ ! -z "${BLUE_DEV}" &&  "${iface[i]}" =
>>>>>> "blue" && "${enabled[i]}" = "enabled" ]]; then
>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>> ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>> ${port[i]} ";
>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>> ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>> ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>> ${port[i]} -j REDIRECT;
>>>>>> +               fi
>>>>>> +               if [[ ! -z "${ORANGE_DEV}" &&  "${iface[i]}" =
>>>>>> "orange" && "${enabled[i]}" = "enabled" ]]; then
>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>> ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>> ${port[i]} ";
>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>> ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>> ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>> ${port[i]} -j REDIRECT;
>>>>>> +               fi
>>>>>> +       done
>>>>>> +       unset array redirects i index iface protocol port
>>>>>> targetip enabled;
>>>>>> +       return 0;
>>>>>> +}
>>>>>> +
>>>>>> +activate_redirections() {
>>>>>> +
>>>>>> +       if ! create_chain; then return 1; fi;
>>>>>> +       
>>>>>> +       # Force DNS REDIRECTs on GREEN (udp, tcp, 53)
>>>>>> +       if [[ "${REDIR_DNS_GREEN}" == "on" && 
>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>> udp -m udp --dport domain -j REDIRECT;
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>> ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>> tcp -m tcp --dport domain -j REDIRECT;
>>>>>> +       fi
>>>>>> +
>>>>>> +       # Force DNS REDIRECTs on BLUE (udp, tcp, 53)
>>>>>> +       if [[ "${REDIR_DNS_BLUE}" == "on" && 
>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>>> -m udp --dport domain -j REDIRECT
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>> ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p tcp
>>>>>> -m tcp --dport domain -j REDIRECT
>>>>>> +       fi
>>>>>> +
>>>>>> +       # Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
>>>>>> +       if [[ "${REDIR_DNS_ORANGE}" == "on" && 
>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>> udp -m udp --dport domain -j REDIRECT
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>> ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>> tcp -m tcp --dport domain -j REDIRECT
>>>>>> +       fi
>>>>>> +
>>>>>> +       # Force NTP REDIRECTs on GREEN (udp, 123)
>>>>>> +       if [[ "${REDIR_NTP_GREEN}" == "on" && 
>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>> +       fi
>>>>>> +
>>>>>> +       # Force NTP REDIRECTs on BLUE (udp, 123)
>>>>>> +       if [[ "${REDIR_NTP_BLUE}" == "on" && 
>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>>> -m udp --dport ntp -j REDIRECT
>>>>>> +       fi
>>>>>> +
>>>>>> +       # Force NTP REDIRECTs on ORANGE (udp, 123)
>>>>>> +       if [[ "${REDIR_NTP_ORANGE}" == "on" && 
>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>> +       fi
>>>>>> +
>>>>>> +       if ! activate_custom_redirections; then return 1; fi;
>>>>>> +
>>>>>> +       return 0;
>>>>>> +}
>>>>>> +
>>>>>> +case "${1}" in
>>>>>> +       start)
>>>>>> +               boot_mesg "Loading port redirections..."
>>>>>> +               activate_redirections;
>>>>>> +               evaluate_retval;
>>>>>> +               ;;
>>>>>> +
>>>>>> +       stop)   
>>>>>> +               boot_mesg "Removing port redirections..."
>>>>>> +               remove_chain;
>>>>>> +               evaluate_retval;
>>>>>> +               ;;
>>>>>> +
>>>>>> +       restart)
>>>>>> +               ${0} stop
>>>>>> +               ${0} start
>>>>>> +               ;;
>>>>>> +
>>>>>> +       *)
>>>>>> +               echo "Usage: ${0} {start|stop|restart}"
>>>>>> +               exit 1
>>>>>> +               ;;
>>>>>> +esac
>>>>>> +
>>>>>> +# End $rc_base/init.d/portredir
>>>>>> diff --git a/src/misc-progs/Makefile b/src/misc-progs/Makefile
>>>>>> index 7c3ef7529..850f8fdcc 100644
>>>>>> --- a/src/misc-progs/Makefile
>>>>>> +++ b/src/misc-progs/Makefile
>>>>>> @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
>>>>>>         wirelessctrl getipstat qosctrl \
>>>>>>         redctrl syslogdctrl extrahdctrl sambactrl \
>>>>>>         smartctrl clamavctrl addonctrl pakfire mpfirectrl
>>>>>> wlanapctrl \
>>>>>> -       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>>> rebuildroutes \
>>>>>> +       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>>> rebuildroutes portredirctrl \
>>>>>>         getconntracktable wirelessclient torctrl ddnsctrl
>>>>>> unboundctrl \
>>>>>>         captivectrl
>>>>>> 
>>>>>> diff --git a/src/misc-progs/portredirctrl.c b/src/misc-
>>>>>> progs/portredirctrl.c
>>>>>> new file mode 100644
>>>>>> index 000000000..7897d711c
>>>>>> --- /dev/null
>>>>>> +++ b/src/misc-progs/portredirctrl.c
>>>>>> @@ -0,0 +1,47 @@
>>>>>> +/* This file is part of the IPFire Firewall.
>>>>>> + *
>>>>>> + * This program is distributed under the terms of the GNU
>>>>>> General Public
>>>>>> + * Licence.  See the file COPYING for details.
>>>>>> + *
>>>>>> + */
>>>>>> +
>>>>>> +#include <stdlib.h>
>>>>>> +#include <stdio.h>
>>>>>> +#include <string.h>
>>>>>> +#include <unistd.h>
>>>>>> +#include <sys/types.h>
>>>>>> +#include <fcntl.h>
>>>>>> +#include "setuid.h"
>>>>>> +
>>>>>> +int main(int argc, char *argv[]) {
>>>>>> +       if (!(initsetuid()))
>>>>>> +               exit(1);
>>>>>> +
>>>>>> +       // Check what command is asked
>>>>>> +        if (argc < 2) {
>>>>>> +                fprintf(stderr, "\nNo argument
>>>>>> given.\n\nportredirctrl
>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>> +                exit(1);
>>>>>> +        }
>>>>>> +
>>>>>> +        if (strcmp(argv[1], "start") == 0) {
>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>> start");
>>>>>> +        } else if (strcmp(argv[1], "stop") == 0) {
>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>> stop");
>>>>>> +        } else if (strcmp(argv[1], "restart") == 0) {
>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>> restart");
>>>>>> +       } else if (strcmp(argv[1], "enable") == 0) {
>>>>>> +               safe_system("touch
>>>>>> /var/ipfire/portredir/enable");
>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>> /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>> /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>> /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
>>>>>> +       } else if (strcmp(argv[1], "disable") == 0) {
>>>>>> +               safe_system("/etc/rc.d/init.d/portredir stop");
>>>>>> +               safe_system("unlink
>>>>>> /var/ipfire/portredir/enable");
>>>>>> +               safe_system("rm -rf /etc/rc.d/rc*.d/*portredir
>>>>>>> /dev/null 2>&1");
>>>>>> +        } else {
>>>>>> +                fprintf(stderr, "\nBad argument
>>>>>> given.\n\nportredirctrl
>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>> +                exit(1);
>>>>>> +        }
>>>>>> +
>>>>>> +       return 0;
>>>>>> +}
>>>>>> diff --git a/src/paks/portredir/install.sh
>>>>>> b/src/paks/portredir/install.sh
>>>>>> new file mode 100644
>>>>>> index 000000000..9f69aeae2
>>>>>> --- /dev/null
>>>>>> +++ b/src/paks/portredir/install.sh
>>>>>> @@ -0,0 +1,32 @@
>>>>>> +#!/bin/bash
>>>>>> +##############################################################
>>>>>> ##############
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# This file is part of the IPFire
>>>>>> Firewall.                                #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>> modify           #
>>>>>> +# it under the terms of the GNU General Public License as
>>>>>> published by     #
>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>> License, or        #
>>>>>> +# (at your option) any later
>>>>>> version.                                      #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>> useful,                #
>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>> of           #
>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>> the            #
>>>>>> +# GNU General Public License for more
>>>>>> details.                             #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# You should have received a copy of the GNU General Public
>>>>>> License        #
>>>>>> +# along with IPFire; if not, write to the Free
>>>>>> Software                    #
>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>> 02111-1307 USA #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# Copyright (C) 2021 IPFire-Team
>>>>>> <info@ipfire.org>.                        #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +##############################################################
>>>>>> ##############
>>>>>> +#
>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>> +extract_files
>>>>>> +restore_backup ${NAME}
>>>>>> +
>>>>>> +/usr/local/bin/update-lang-cache
>>>>>> +
>>>>>> +chown root:nobody /usr/local/bin/portredirctrl
>>>>>> +chmod 4750 /usr/local/bin/portredirctrl
>>>>>> +chmod u+s /usr/local/bin/portredirctrl
>>>>>> diff --git a/src/paks/portredir/uninstall.sh
>>>>>> b/src/paks/portredir/uninstall.sh
>>>>>> new file mode 100644
>>>>>> index 000000000..df9270125
>>>>>> --- /dev/null
>>>>>> +++ b/src/paks/portredir/uninstall.sh
>>>>>> @@ -0,0 +1,28 @@
>>>>>> +#!/bin/bash
>>>>>> +##############################################################
>>>>>> ##############
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# This file is part of the IPFire
>>>>>> Firewall.                                #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>> modify           #
>>>>>> +# it under the terms of the GNU General Public License as
>>>>>> published by     #
>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>> License, or        #
>>>>>> +# (at your option) any later
>>>>>> version.                                      #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>> useful,                #
>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>> of           #
>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>> the            #
>>>>>> +# GNU General Public License for more
>>>>>> details.                             #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# You should have received a copy of the GNU General Public
>>>>>> License        #
>>>>>> +# along with IPFire; if not, write to the Free
>>>>>> Software                    #
>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>> 02111-1307 USA #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>> <info@ipfire.org>.                        #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +##############################################################
>>>>>> ##############
>>>>>> +#
>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>> +make_backup ${NAME}
>>>>>> +remove_files
>>>>>> +
>>>>>> +/usr/local/bin/update-lang-cache
>>>>>> diff --git a/src/paks/portredir/update.sh
>>>>>> b/src/paks/portredir/update.sh
>>>>>> new file mode 100644
>>>>>> index 000000000..89c40d0d7
>>>>>> --- /dev/null
>>>>>> +++ b/src/paks/portredir/update.sh
>>>>>> @@ -0,0 +1,26 @@
>>>>>> +#!/bin/bash
>>>>>> +##############################################################
>>>>>> ##############
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# This file is part of the IPFire
>>>>>> Firewall.                                #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>> modify           #
>>>>>> +# it under the terms of the GNU General Public License as
>>>>>> published by     #
>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>> License, or        #
>>>>>> +# (at your option) any later
>>>>>> version.                                      #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>> useful,                #
>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>> of           #
>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>> the            #
>>>>>> +# GNU General Public License for more
>>>>>> details.                             #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# You should have received a copy of the GNU General Public
>>>>>> License        #
>>>>>> +# along with IPFire; if not, write to the Free
>>>>>> Software                    #
>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>> 02111-1307 USA #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>> <info@ipfire.org>.                        #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +##############################################################
>>>>>> ##############
>>>>>> +#
>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>> +./uninstall.sh
>>>>>> +./install.sh
>>>>>> -- 
>>>>>> 2.18.0
>>>>>> 
>>>>> 
>>>> 
>>> 
>> 
>
  
Jon Murphy July 1, 2021, 3:04 p.m. UTC | #7
> You probably want “any” as destination.

Those are the only choices that allow a Save.  When I enter an IP address I get this error:


Error messages

Source and destination IP addresses are from the same subnet.  



Jon

> On Jul 1, 2021, at 3:08 AM, Michael Tremer <michael.tremer@ipfire.org> wrote:
> 
> Hey Jon,
> 
> You probably want “any” as destination.
> 
> -Michael
> 
>> On 1 Jul 2021, at 04:08, Jon Murphy <jcmurphy26@gmail.com> wrote:
>> 
>> Hi Stefan,
>> 
>> Thank you for taking this on!
>> 
>> I applied the patchwork.ipfire patch.
>> 
>> I think I entered something wrong since I cannot get things to work.  I tried both with Destination Firewall GREEN & Firewall RED.
>> 
>> 
>> Does the Firewall Rule seem right?
>> 
>> Best regards,
>> Jon
>> 
>> 
>> Here is the rule I set up:
>> 
>> 
>> <Screen Shot 2021-06-30 at 9.53.52 PM.png>
>> 
>> <Screen Shot 2021-06-30 at 9.53.10 PM.png>
>> 
>> 
>> And this is what I see with conntrack:
>> 
>> conntrack -E -e NEW,UPDATE | grep -e "=53 "
>> 
>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=51169 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=51169
>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=54168 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=54168
>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=56094 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=56094
>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=52964 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=52964
>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=53279 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=53279
>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=61657 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=61657
>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=57723 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=57723
>> 
>> 
>> 
>> 
>>> On Jun 30, 2021, at 2:14 PM, Stefan Schantl <stefan.schantl@ipfire.org> wrote:
>>> 
>>> Hello Matthias, Hello Michael, Hello Jon, Hello *,
>>> 
>>> I've followed the conversation on this list since the first mail and
>>> thoughts about forcing DNS traffic to use the local resolver.
>>> 
>>> It was a very long journey and lot of time and work has been spent to
>>> get to the present point.
>>> 
>>> As Michael requested here, I've digged through the lines of the perl
>>> script which is responsible for creating the firewall rules and
>>> surprisingly found that everyting which is needed to create generic
>>> REDIRECT rules already was written in the past - it just did not work
>>> as designed/expected.
>>> 
>>> Finaly I was able to adjust these lines of code and to repair that
>>> feature.
>>> 
>>> A redirect rule can be created by picking a single host or group of
>>> hosts or entire network(s) as source, selecting NAT (DNAT) and choosing
>>> the Firewall itself as target.
>>> 
>>> The protocol or service or service group which should be redirected has
>>> to be selected afterwards. If you want to redirect a given port to
>>> another one it can be specified as "Target port".
>>> 
>>> All created redirect rules are displayed as "input rules".
>>> 
>>> 
>>> The patch directly can be accessed here:
>>> 
>>> https://patchwork.ipfire.org/project/ipfire/patch/20210630184031.7726-1-stefan.schantl@ipfire.org/
>>> 
>>> Best regards,
>>> 
>>> -Stefan
>>> 
>>>> Hello,
>>>> 
>>>>> On 28 Jun 2021, at 18:53, Jon Murphy <jcmurphy26@gmail.com> wrote:
>>>>> 
>>>>> Hi Michael!  Happy Monday!
>>>>> 
>>>>> 
>>>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>>>> of code instead of adding many hundreds of lines?
>>>>>> 
>>>>>> Please can someone elaborate on this more?
>>>>> 
>>>>> Doing a DNS redirect, via the WegBUI, has been an issue since
>>>>> 2015.  I found this quote in the old forum:
>>>>> 
>>>>> "Having investigated a bit more I have concluded that it's not
>>>>> currently possible to create such rules through the WUI.
>>>>> 
>>>>> There are a number of obstacles:
>>>>> 1. It is not allowed to create a rule where source IP and
>>>>> destination nat IP is on the same subnetwork (e.g. GREEN), WUI
>>>>> error message: "Source and destination IP addresses are from the
>>>>> same subnet."
>>>>> 
>>>>> 2. WUI will not allow you to create a rule without a destination
>>>>> (the filtered packet must adhere to a destination, not only a port)
>>>>> and the destination MUST be an IP address of one of the IPFire
>>>>> interfaces, which limits whats possible a great deal." 
>>>> 
>>>> And these cannot be changed?
>>>> 
>>>>> And I found this from 2016:
>>>>> https://bugzilla.ipfire.org/show_bug.cgi?id=11168
>>>>> 
>>>>> So I am guessing that no one has been able to determine a way to
>>>>> extend the WebGUI.  
>>>> 
>>>> Has anyone tried? I do not see any obvious reasons why this should
>>>> not be possible.
>>>> 
>>>>> I am curious - Who created the 
>>>>> https://ipfire:444/cgi-bin/firewall.cgi page?  And could they help?
>>>> 
>>>> -Michael
>>>> 
>>>>> Jon
>>>>> 
>>>>> 
>>>>>> On Jun 28, 2021, at 11:04 AM, Michael Tremer <
>>>>>> michael.tremer@ipfire.org> wrote:
>>>>>> 
>>>>>> Hello Matthias,
>>>>>> 
>>>>>>> On 27 Jun 2021, at 14:48, Matthias Fischer <
>>>>>>> matthias.fischer@ipfire.org> wrote:
>>>>>>> 
>>>>>>> From: Marcel Lorenz <marcel.lorenz@ipfire.org>
>>>>>> 
>>>>>> Thank you for sending this patch on Marcel’s behalf, but I would
>>>>>> much more prefer if he would submit his patches on his own. I do
>>>>>> not see why that isn’t possible.
>>>>>> 
>>>>>>> Please note:
>>>>>>> This is a new addon written by Marcel Lorenz <
>>>>>>> marcel.lorenz@ipfire.org>.
>>>>>>> 
>>>>>>> It adds a new GUI to IPFire for DNS/NTP *and* user specific
>>>>>>> port redirections.
>>>>>>> 
>>>>>>> How its working:
>>>>>>> It has exactly the same functionalities as "Forcing
>>>>>>> DNS/NTP..."  - and some more.
>>>>>>> 
>>>>>>> By setting switches, DNS/NTP requests are automatically
>>>>>>> redirected to the local IPFire DNS/NTP servers.
>>>>>>> 
>>>>>>> Additionally, the user can specify custom redirections.
>>>>>>> 
>>>>>>> These rules are added to a new chain in PREROUTING =>
>>>>>>> PORT_REDIRECT.
>>>>>>> 
>>>>>>> To avoid problems with (e.g.) transparent 'squid'
>>>>>>> configurations,
>>>>>>> redirection rules are added automatically before existing
>>>>>>> 'squid' rules.
>>>>>> 
>>>>>> This message does unfortunately not say why this add-on would be
>>>>>> useful. I am emphasising this again and again that it is not very
>>>>>> important how something is done specially. That should be
>>>>>> commented in the code and other implementation details should
>>>>>> also be documented there.
>>>>>> 
>>>>>> As I have stated on this functionality many times before, I do
>>>>>> not see why this is necessary at all.
>>>>>> 
>>>>>> Why is this an add-on?
>>>>>> 
>>>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>>>> of code instead of adding many hundreds of lines?
>>>>>> 
>>>>>> Please can someone elaborate on this more?
>>>>>> 
>>>>>> -Michael
>>>>>> 
>>>>>>> Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
>>>>>>> ---
>>>>>>> config/portredir/EX-portredir.menu    |   6 +
>>>>>>> config/portredir/lang/portredir.de.pl |  19 +
>>>>>>> config/portredir/lang/portredir.en.pl |  19 +
>>>>>>> config/portredir/portredir-backup     |   1 +
>>>>>>> config/portredir/portredir.cgi        | 525
>>>>>>> ++++++++++++++++++++++++++
>>>>>>> config/rootfiles/common/misc-progs    |   1 +
>>>>>>> config/rootfiles/packages/portredir   |  11 +
>>>>>>> lfs/portredir                         |  85 +++++
>>>>>>> make.sh                               |   1 +
>>>>>>> src/initscripts/packages/portredir    | 191 ++++++++++
>>>>>>> src/misc-progs/Makefile               |   2 +-
>>>>>>> src/misc-progs/portredirctrl.c        |  47 +++
>>>>>>> src/paks/portredir/install.sh         |  32 ++
>>>>>>> src/paks/portredir/uninstall.sh       |  28 ++
>>>>>>> src/paks/portredir/update.sh          |  26 ++
>>>>>>> 15 files changed, 993 insertions(+), 1 deletion(-)
>>>>>>> create mode 100644 config/portredir/EX-portredir.menu
>>>>>>> create mode 100644 config/portredir/lang/portredir.de.pl
>>>>>>> create mode 100644 config/portredir/lang/portredir.en.pl
>>>>>>> create mode 100644 config/portredir/portredir-backup
>>>>>>> create mode 100644 config/portredir/portredir.cgi
>>>>>>> create mode 100644 config/rootfiles/packages/portredir
>>>>>>> create mode 100644 lfs/portredir
>>>>>>> create mode 100644 src/initscripts/packages/portredir
>>>>>>> create mode 100644 src/misc-progs/portredirctrl.c
>>>>>>> create mode 100644 src/paks/portredir/install.sh
>>>>>>> create mode 100644 src/paks/portredir/uninstall.sh
>>>>>>> create mode 100644 src/paks/portredir/update.sh
>>>>>>> 
>>>>>>> diff --git a/config/portredir/EX-portredir.menu
>>>>>>> b/config/portredir/EX-portredir.menu
>>>>>>> new file mode 100644
>>>>>>> index 000000000..8376e8053
>>>>>>> --- /dev/null
>>>>>>> +++ b/config/portredir/EX-portredir.menu
>>>>>>> @@ -0,0 +1,6 @@
>>>>>>> +    $subfirewall->{'95.portredir'} = {
>>>>>>> +                               'caption' =>
>>>>>>> $Lang::tr{'portredir port redirections'},
>>>>>>> +                               'uri' => '/cgi-
>>>>>>> bin/portredir.cgi',
>>>>>>> +                               'title' =>
>>>>>>> "$Lang::tr{'portredir port redirections'}",
>>>>>>> +                               'enabled' => 1
>>>>>>> +                               };
>>>>>>> diff --git a/config/portredir/lang/portredir.de.pl
>>>>>>> b/config/portredir/lang/portredir.de.pl
>>>>>>> new file mode 100644
>>>>>>> index 000000000..b932d4a85
>>>>>>> --- /dev/null
>>>>>>> +++ b/config/portredir/lang/portredir.de.pl
>>>>>>> @@ -0,0 +1,19 @@
>>>>>>> +%tr = (
>>>>>>> +%tr,
>>>>>>> +'portredir enable addon' => 'Addon aktivieren',
>>>>>>> +'portredir common settings' => 'Allgemeine Einstellungen',
>>>>>>> +'portredir port redirections' => 'Portumleitungen',
>>>>>>> +'portredir fw for interface' => 'Firewalloptionen für das
>>>>>>> Interface',
>>>>>>> +'portredir enable user redirections' => 'Aktiviere
>>>>>>> benutzerdefinierte Portumleitungen',
>>>>>>> +'portredir force local dns' => 'Erzwinge lokale DNS-Server',
>>>>>>> +'portredir force local ntp' => 'Erzwinge lokale NTP-Server',
>>>>>>> +'portredir custom redirections' => 'Benutzerdefinierte
>>>>>>> Portumleitungen',
>>>>>>> +'portredir remove rule' => 'Entferne Regel',
>>>>>>> +'portredir add rule' => 'Hinzufügen',
>>>>>>> +'portredir no entries' => 'Keine Einträge vorhanden.',
>>>>>>> +'portredir invalid address' => 'Ungültige Host-Addresse.',
>>>>>>> +'portredir empty input' => 'Fehlende Angabe: Bitte geben Sie
>>>>>>> einen gültigen Host an.',
>>>>>>> +'portredir save to activate' => 'Speichern, um Änderungen zu
>>>>>>> aktivieren',
>>>>>>> +);
>>>>>>> +
>>>>>>> +#EOF
>>>>>>> diff --git a/config/portredir/lang/portredir.en.pl
>>>>>>> b/config/portredir/lang/portredir.en.pl
>>>>>>> new file mode 100644
>>>>>>> index 000000000..f442f3eaa
>>>>>>> --- /dev/null
>>>>>>> +++ b/config/portredir/lang/portredir.en.pl
>>>>>>> @@ -0,0 +1,19 @@
>>>>>>> +%tr = (
>>>>>>> +%tr,
>>>>>>> +'portredir enable addon' => 'Enable addon',
>>>>>>> +'portredir common settings' => 'Common settings',
>>>>>>> +'portredir port redirections' => 'Port redirections',
>>>>>>> +'portredir fw for interface' => 'Firewall options for
>>>>>>> interface',
>>>>>>> +'portredir enable user redirections' => 'Enable user port
>>>>>>> redirections',
>>>>>>> +'portredir force local dns' => 'Enforce local DNS servers',
>>>>>>> +'portredir force local ntp' => 'Enforce local NTP servers',
>>>>>>> +'portredir custom redirections' => 'Custom port redirections',
>>>>>>> +'portredir remove rule' => 'Remove rule',
>>>>>>> +'portredir add rule' => 'Add new',
>>>>>>> +'portredir no entries' => 'No entries at the moment.',
>>>>>>> +'portredir invalid address' => 'Invalid host address.',
>>>>>>> +'portredir empty input' => 'Empty input: Please enter a valid
>>>>>>> host.',
>>>>>>> +'portredir save to activate' => 'Save to activate changes',
>>>>>>> +);
>>>>>>> +
>>>>>>> +#EOF
>>>>>>> diff --git a/config/portredir/portredir-backup
>>>>>>> b/config/portredir/portredir-backup
>>>>>>> new file mode 100644
>>>>>>> index 000000000..bd2ada742
>>>>>>> --- /dev/null
>>>>>>> +++ b/config/portredir/portredir-backup
>>>>>>> @@ -0,0 +1 @@
>>>>>>> +/var/ipfire/portredir
>>>>>>> diff --git a/config/portredir/portredir.cgi
>>>>>>> b/config/portredir/portredir.cgi
>>>>>>> new file mode 100644
>>>>>>> index 000000000..4913dda3f
>>>>>>> --- /dev/null
>>>>>>> +++ b/config/portredir/portredir.cgi
>>>>>>> @@ -0,0 +1,525 @@
>>>>>>> +#!/usr/bin/perl
>>>>>>> +##############################################################
>>>>>>> #################
>>>>>>> +#                                                             
>>>>>>>                #
>>>>>>> +# IPFire.org - A linux based
>>>>>>> firewall                                         #
>>>>>>> +# Copyright (C) 2021  IPFire Team 
>>>>>>> <info@ipfire.org>                          #
>>>>>>> +#                                                             
>>>>>>>                #
>>>>>>> +# This program is free software: you can redistribute it
>>>>>>> and/or modify        #
>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>> published by        #
>>>>>>> +# the Free Software Foundation, either version 3 of the
>>>>>>> License, or           #
>>>>>>> +# (at your option) any later
>>>>>>> version.                                         #
>>>>>>> +#                                                             
>>>>>>>                #
>>>>>>> +# This program is distributed in the hope that it will be
>>>>>>> useful,             #
>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>> of              #
>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>> the               #
>>>>>>> +# GNU General Public License for more
>>>>>>> details.                                #
>>>>>>> +#                                                             
>>>>>>>                #
>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>> License           #
>>>>>>> +# along with this program.  If not, see <
>>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>>> +#                                                             
>>>>>>>                #
>>>>>>> +##############################################################
>>>>>>> #################
>>>>>>> +
>>>>>>> +use strict;
>>>>>>> +
>>>>>>> +# enable only the following on debugging purpose
>>>>>>> +use warnings;
>>>>>>> +use CGI::Carp 'fatalsToBrowser';
>>>>>>> +
>>>>>>> +require '/var/ipfire/general-functions.pl';
>>>>>>> +require "${General::swroot}/lang.pl";
>>>>>>> +require "${General::swroot}/header.pl";
>>>>>>> +
>>>>>>> +# File declarations
>>>>>>> +my $settingsfile = "${General::swroot}/portredir/settings";
>>>>>>> +my $redirectsfile = "${General::swroot}/portredir/redirects";
>>>>>>> +
>>>>>>> +# Create empty settingsfiles if they does not exist yet
>>>>>>> +unless (-e "$settingsfile") { system ("touch $settingsfile");
>>>>>>> }
>>>>>>> +unless (-e "$redirectsfile") { system ("touch
>>>>>>> $redirectsfile"); }
>>>>>>> +
>>>>>>> +# load ipfire settings
>>>>>>> +our %netsettings = ();
>>>>>>> +our %color = ();
>>>>>>> +&General::readhash("${General::swroot}/ethernet/settings",
>>>>>>> \%netsettings);
>>>>>>> +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include
>>>>>>> /colors.txt", \%color);
>>>>>>> +
>>>>>>> +my %settings=();
>>>>>>> +my %portredirs=();
>>>>>>> +my %checked=(); # Checkbox manipulations
>>>>>>> +my $errormessage='';
>>>>>>> +my %selected=();
>>>>>>> +our %redirects=();
>>>>>>> +
>>>>>>> +$settings{'ACTION'} = '';
>>>>>>> +$settings{'REDIR_ENABLE_ADDON'}="off";
>>>>>>> +$settings{'REDIR_CUSTOM_GREEN'}="off";
>>>>>>> +$settings{'REDIR_CUSTOM_BLUE'}="off";
>>>>>>> +$settings{'REDIR_CUSTOM_ORANGE'}="off";
>>>>>>> +$settings{'REDIR_DNS_GREEN'}="off";
>>>>>>> +$settings{'REDIR_NTP_GREEN'}="off";
>>>>>>> +$settings{'REDIR_DNS_BLUE'}="off";
>>>>>>> +$settings{'REDIR_NTP_BLUE'}="off";
>>>>>>> +$settings{'REDIR_DNS_ORANGE'}="off";
>>>>>>> +$settings{'REDIR_NTP_ORANGE'}="off";
>>>>>>> +
>>>>>>> +&Header::showhttpheaders();
>>>>>>> +
>>>>>>> +# Get GUI values
>>>>>>> +&Header::getcgihash(\%settings);
>>>>>>> +
>>>>>>> +# Save action
>>>>>>> +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
>>>>>>> +
>>>>>>> +       # If custom rules enabled, deactivate default rules on
>>>>>>> interface
>>>>>>> +       if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
>>>>>>> +               $settings{'REDIR_DNS_GREEN'}="off";
>>>>>>> +               $settings{'REDIR_NTP_GREEN'}="off";
>>>>>>> +       }
>>>>>>> +
>>>>>>> +       if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
>>>>>>> +               $settings{'REDIR_DNS_BLUE'}="off";
>>>>>>> +               $settings{'REDIR_NTP_BLUE'}="off";
>>>>>>> +       }
>>>>>>> +
>>>>>>> +       if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
>>>>>>> +               $settings{'REDIR_DNS_ORANGE'}="off";
>>>>>>> +               $settings{'REDIR_NTP_ORANGE'}="off";
>>>>>>> +       }
>>>>>>> +
>>>>>>> +       &General::writehash($settingsfile, \%settings);
>>>>>>> +
>>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
>>>>>>> +               system ('/usr/local/bin/portredirctrl restart
>>>>>>>> /dev/null 2>&1');
>>>>>>> +               system ('/usr/local/bin/portredirctrl enable
>>>>>>>> /dev/null 2>&1');
>>>>>>> +               &General::log('portredir addon: port
>>>>>>> redirections enabled');
>>>>>>> +       }
>>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
>>>>>>> +               system ('/usr/local/bin/portredirctrl disable
>>>>>>>> /dev/null 2>&1');
>>>>>>> +               system ('/usr/local/bin/portredirctrl stop
>>>>>>>> /dev/null 2>&1');
>>>>>>> +               &General::log('portredir addon: port
>>>>>>> redirections disabled');
>>>>>>> +       }
>>>>>>> +
>>>>>>> +# Add/edit an entry to the redirectsfile.
>>>>>>> +
>>>>>>> +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) ||
>>>>>>> ($settings{'ACTION'} eq $Lang::tr{'update'})) {
>>>>>>> +
>>>>>>> +       # Check if any input has been performed.
>>>>>>> +       if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
>>>>>>> +
>>>>>>> +               # Check if the given input is no valid IP-
>>>>>>> address, display an error message.
>>>>>>> +               if
>>>>>>> (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
>>>>>>> +                       $errormessage = "$Lang::tr{'portredir
>>>>>>> invalid address'}";
>>>>>>> +               }
>>>>>>> +       } else {
>>>>>>> +               $errormessage = "$Lang::tr{'portredir empty
>>>>>>> input'}";
>>>>>>> +       }
>>>>>>> +
>>>>>>> +       # Go further if there was no error.
>>>>>>> +       if ($errormessage eq '') {
>>>>>>> +               my %redirects = ();
>>>>>>> +               my $id;
>>>>>>> +               my $status;
>>>>>>> +
>>>>>>> +               # Assign hash values.
>>>>>>> +               my $new_entry_interface =
>>>>>>> $settings{'REDIR_ENTRY_INTERFACE'};
>>>>>>> +               my $new_entry_protocol =
>>>>>>> $settings{'REDIR_ENTRY_PROTOCOL'};
>>>>>>> +               my $new_entry_port =
>>>>>>> $settings{'REDIR_ENTRY_PORT'};
>>>>>>> +               my $new_entry_address =
>>>>>>> $settings{'REDIR_ENTRY_ADDRESS'};
>>>>>>> +               my $new_entry_remark =
>>>>>>> $settings{'REDIR_ENTRY_REMARK'};
>>>>>>> +
>>>>>>> +               # Read-in redirectsfile.
>>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>>> \%redirects);
>>>>>>> +
>>>>>>> +               # Check if we should edit an existing entry and
>>>>>>> got an ID.
>>>>>>> +               if (($settings{'ACTION'} eq
>>>>>>> $Lang::tr{'update'}) && ($settings{'ID'})) {
>>>>>>> +                       # Assin the provided id.
>>>>>>> +                       $id = $settings{'ID'};
>>>>>>> +
>>>>>>> +                       # Undef the given ID.
>>>>>>> +                       undef($settings{'ID'});
>>>>>>> +
>>>>>>> +                       # Grab the configured status of the
>>>>>>> corresponding entry.
>>>>>>> +                       $status = $redirects{$id}[4];
>>>>>>> +               } else {
>>>>>>> +                       # Each newly added entry automatically
>>>>>>> should be enabled.
>>>>>>> +                       $status = "enabled";
>>>>>>> +
>>>>>>> +                       # Generate the ID for the new entry.
>>>>>>> +                       #
>>>>>>> +                       # Sort the keys by their ID and store
>>>>>>> them in an array.
>>>>>>> +                       my @keys = sort { $a <=> $b } keys
>>>>>>> %redirects;
>>>>>>> +
>>>>>>> +                       # Reverse the key array.
>>>>>>> +                       my @reversed = reverse(@keys);
>>>>>>> +
>>>>>>> +                       # Obtain the last used id.
>>>>>>> +                       my $last_id = @reversed[0];
>>>>>>> +
>>>>>>> +                       # Increase the last id by one and use
>>>>>>> it as id for the new entry.
>>>>>>> +                       $id = ++$last_id;
>>>>>>> +               }
>>>>>>> +
>>>>>>> +               # Add/Modify the entry to/in the redirects
>>>>>>> hash.
>>>>>>> +               $redirects{$id} = ["$new_entry_interface",
>>>>>>> "$new_entry_protocol", "$new_entry_port",
>>>>>>> "$new_entry_address","$status", "$new_entry_remark"];
>>>>>>> +
>>>>>>> +               # Write the changed redirects hash to the
>>>>>>> redirects file.
>>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>>> \%redirects);
>>>>>>> +       }
>>>>>>> +
>>>>>>> +# Toggle Enabled/Disabled for an existing entry on the
>>>>>>> redirects list.
>>>>>>> +
>>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable
>>>>>>> disable'}) {
>>>>>>> +       my %redirects = ();
>>>>>>> +
>>>>>>> +       # Only go further, if an ID has been passed.
>>>>>>> +       if ($settings{'ID'}) {
>>>>>>> +               # Assign the given ID.
>>>>>>> +               my $id = $settings{'ID'};
>>>>>>> +
>>>>>>> +               # Undef the given ID.
>>>>>>> +               undef($settings{'ID'});
>>>>>>> +
>>>>>>> +               # Read-in ignoredfile.
>>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>>> \%redirects);
>>>>>>> +
>>>>>>> +               # Grab the configured status of the
>>>>>>> corresponding entry.
>>>>>>> +               my $status = $redirects{$id}[4];
>>>>>>> +
>>>>>>> +               # Switch the status.
>>>>>>> +               if ($status eq "disabled") {
>>>>>>> +                       $status = "enabled";
>>>>>>> +               } else {
>>>>>>> +                       $status = "disabled";
>>>>>>> +               }
>>>>>>> +
>>>>>>> +               # Modify the status of the existing entry.
>>>>>>> +               $redirects{$id} = ["$redirects{$id}[0]",
>>>>>>> "$redirects{$id}[1]", "$redirects{$id}[2]",
>>>>>>> "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
>>>>>>> +
>>>>>>> +               # Write the changed ignored hash to the
>>>>>>> redirects file.
>>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>>> \%redirects);
>>>>>>> +       }
>>>>>>> +
>>>>>>> +# Remove entry from redirects list.
>>>>>>> +
>>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
>>>>>>> +       my %redirects = ();
>>>>>>> +
>>>>>>> +       # Read-in redirectsfile.
>>>>>>> +       &General::readhasharray($redirectsfile, \%redirects);
>>>>>>> +
>>>>>>> +       # move data on key up
>>>>>>> +       foreach my $key (sort keys %redirects) {
>>>>>>> +               if ($key >= $settings{'ID'}) {
>>>>>>> +                       my $next = $key + 1;
>>>>>>> +                       if (exists $redirects{$next}) {
>>>>>>> +                               foreach my $i (0 ..
>>>>>>> $#{$redirects{$next}}) { $redirects{$key}[$i] =
>>>>>>> $redirects{$next}[$i]; }
>>>>>>> +                       }
>>>>>>> +               }
>>>>>>> +       }
>>>>>>> +
>>>>>>> +       my $last_key = (sort {$a <=> $b} keys %redirects)[-1];
>>>>>>> +       delete $redirects{$last_key};
>>>>>>> +
>>>>>>> +       # Undef the given ID.
>>>>>>> +       undef($settings{'ID'});
>>>>>>> +
>>>>>>> +       # Write the changed redirects hash to file.
>>>>>>> +       &General::writehasharray($redirectsfile, \%redirects);
>>>>>>> +}
>>>>>>> +
>>>>>>> +# Load settings from file
>>>>>>> +&General::readhash($settingsfile, \%settings);
>>>>>>> +&General::readhasharray($redirectsfile, \%redirects);
>>>>>>> +
>>>>>>> +# Call functions to generate whole page.
>>>>>>> +&Header::openpage($Lang::tr{'portredir port redirections'}, 1,
>>>>>>> '');
>>>>>>> +&Header::openbigbox('100%', 'left', '', $errormessage);
>>>>>>> +
>>>>>>> +if ($errormessage) {
>>>>>>> +        &Header::openbox('100%', 'left', $Lang::tr{'warning
>>>>>>> messages'});
>>>>>>> +        print "<font color='red'>$errormessage&nbsp;</font>";
>>>>>>> +        &Header::closebox();
>>>>>>> +}
>>>>>>> +
>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_ADDON'}
>>>>>>> } = "checked='checked'";
>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_GREEN'}
>>>>>>> } = "checked='checked'";
>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BLUE'}}
>>>>>>> = "checked='checked'";
>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_ORANGE
>>>>>>> '}} = "checked='checked'";
>>>>>>> +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
>>>>>>> +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
>>>>>>> +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}} =
>>>>>>> "checked='checked'";
>>>>>>> +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
>>>>>>> +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
>>>>>>> +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}} =
>>>>>>> "checked='checked'";
>>>>>>> +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
>>>>>>> +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
>>>>>>> +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}} =
>>>>>>> "checked='checked'";
>>>>>>> +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
>>>>>>> +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
>>>>>>> +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}} =
>>>>>>> "checked='checked'";
>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE'}} =
>>>>>>> "checked='checked'";
>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE'}} =
>>>>>>> "checked='checked'";
>>>>>>> +
>>>>>>> +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTRY_INTE
>>>>>>> RFACE'}} = 'selected';
>>>>>>> +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY_PROTO
>>>>>>> COL'}} = 'selected';
>>>>>>> +
>>>>>>> +&showMainBox();
>>>>>>> +&showRedirectsBox();
>>>>>>> +
>>>>>>> +&Header::closebigbox();
>>>>>>> +&Header::closepage();
>>>>>>> +
>>>>>>> +# Function to show main settings and options.
>>>>>>> +sub showMainBox() {
>>>>>>> +
>>>>>>> +       &Header::openbox('100%', 'center',
>>>>>>> "$Lang::tr{'settings'}");
>>>>>>> +       print "<form method='post'
>>>>>>> action='$ENV{'SCRIPT_NAME'}'>";
>>>>>>> +
>>>>>>> +print <<END;
>>>>>>> +<table width='80%' cellspacing='0' border='0'>
>>>>>>> +       <tr><td colspan='2' class='base'
>>>>>>> bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir common
>>>>>>> settings'}</b></td></tr
>>>>>>> +       <tr>
>>>>>>> +               <td width='25%'
>>>>>>> class='base'>$Lang::tr{'portredir enable addon'}:</td>
>>>>>>> +               <td><input type='checkbox'
>>>>>>> name='REDIR_ENABLE_ADDON' $checked{'REDIR_ENABLE_ADDON'}{'on'}
>>>>>>> /></td>
>>>>>>> +       </tr>
>>>>>>> +       <tr><td colspan='2'></td></tr>
>>>>>>> +       <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>>> +END
>>>>>>> +
>>>>>>> +       # create html table with header line 1
>>>>>>> +       print "<table width='80%' cellspacing='0'
>>>>>>> border='0'><tr>";
>>>>>>> +       print "<th class='base' width='40%'
>>>>>>> align='left'><b>$Lang::tr{'portredir fw for interface'}</th>";
>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<th
>>>>>>> class='base' width='10%'><b><font
>>>>>>> color=green>$Lang::tr{'green'}</font></th>";
>>>>>>> +               } else {                 print "<th
>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<th
>>>>>>> class='base' width='10%'><b><font
>>>>>>> color=blue>$Lang::tr{'blue'}</font></th>";
>>>>>>> +               } else {                 print "<th
>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<th
>>>>>>> class='base' width='10%'><b><font
>>>>>>> color=orange>$Lang::tr{'orange'}</font></th>";
>>>>>>> +               } else {                 print "<th
>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>> +
>>>>>>> +       # the empty right row
>>>>>>> +       print "<th class='base'
>>>>>>> width='30%'><td></td></th></tr>";
>>>>>>> +
>>>>>>> +       # line 2
>>>>>>> +       print "<tr><td>$Lang::tr{'portredir force local
>>>>>>> dns'}</td>";
>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>> name='REDIR_DNS_GREEN'
>>>>>>> $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print
>>>>>>> "<td></td>";}
>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>> name='REDIR_DNS_BLUE' $checked{'REDIR_DNS_BLUE'}{'on'}></td>";}
>>>>>>> else { print "<td></td>";}
>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>> name='REDIR_DNS_ORANGE'
>>>>>>> $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print
>>>>>>> "<td></td>";}
>>>>>>> +
>>>>>>> +       # line 3
>>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir force local
>>>>>>> ntp'}</td>";
>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>> name='REDIR_NTP_GREEN'
>>>>>>> $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print
>>>>>>> "<td></td>";}
>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>> name='REDIR_NTP_BLUE' $checked{'REDIR_NTP_BLUE'}{'on'}></td>";}
>>>>>>> else { print "<td></td>";}
>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>> name='REDIR_NTP_ORANGE'
>>>>>>> $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print
>>>>>>> "<td></td>";}
>>>>>>> +
>>>>>>> +       # line 4
>>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir enable user
>>>>>>> redirections'}</td>";
>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>> name='REDIR_CUSTOM_GREEN'
>>>>>>> $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else { print
>>>>>>> "<td></td>";}
>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>> name='REDIR_CUSTOM_BLUE'
>>>>>>> $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print
>>>>>>> "<td></td>";}
>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>> name='REDIR_CUSTOM_ORANGE'
>>>>>>> $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else { print
>>>>>>> "<td></td>";}
>>>>>>> +
>>>>>>> +       print <<END;
>>>>>>> +       </tr></table>
>>>>>>> +       <table width='80%' cellspacing='0' border='0'>
>>>>>>> +               <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>>> +               <tr><td align='left'><b>$Lang::tr{'portredir
>>>>>>> save to activate'}</b></td><td width='5%' align='center'><input
>>>>>>> type='submit' name='ACTION' value='  $Lang::tr{'save'} 
>>>>>>> '></td></tr>
>>>>>>> +       </table></form>
>>>>>>> +END
>>>>>>> +
>>>>>>> +&Header::closebox();
>>>>>>> +}
>>>>>>> +
>>>>>>> +# Function to show elements of the redirects file and allow to
>>>>>>> add or remove single members of it.
>>>>>>> +sub showRedirectsBox() {
>>>>>>> +        &Header::openbox('100%', 'center',
>>>>>>> "$Lang::tr{'portredir custom redirections'}");
>>>>>>> +
>>>>>>> +       print <<END;
>>>>>>> +               <table width='80%' cellspacing='1' border='0'>
>>>>>>> +                       <tr>
>>>>>>> +                               <td class='base'
>>>>>>> bgcolor='$color{'color20'}'
>>>>>>> align='center'><b>$Lang::tr{'interface'}</b></td>
>>>>>>> +                               <td class='base'
>>>>>>> bgcolor='$color{'color20'}'
>>>>>>> align='center'><b>$Lang::tr{'protocol'}</b></td>
>>>>>>> +                               <td class='base'
>>>>>>> bgcolor='$color{'color20'}'
>>>>>>> align='center'><b>$Lang::tr{'port'}</b></td>
>>>>>>> +                               <td class='base'
>>>>>>> bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'ip
>>>>>>> address'}</b></td>
>>>>>>> +                               <td class='base'
>>>>>>> bgcolor='$color{'color20'}'
>>>>>>> align='center'><b>$Lang::tr{'remark'}</b></td>
>>>>>>> +                               <td class='base' colspan='3'
>>>>>>> bgcolor='$color{'color20'}'></td>
>>>>>>> +                       </tr>
>>>>>>> +END
>>>>>>> +                       # Check if some rules have been added
>>>>>>> to be redirects.
>>>>>>> +                       if (keys (%redirects)) {
>>>>>>> +                               my $col = "";
>>>>>>> +
>>>>>>> +                               # List all entries of the hash.
>>>>>>> +                               foreach my $key (sort keys
>>>>>>> %redirects){
>>>>>>> +
>>>>>>> +                                       # Assign data array
>>>>>>> positions to some nice variable names.
>>>>>>> +                                       my $interface =
>>>>>>> $redirects{$key}[0];
>>>>>>> +                                       my $protocol =
>>>>>>> $redirects{$key}[1];
>>>>>>> +                                       my $port  =
>>>>>>> $redirects{$key}[2];
>>>>>>> +                                       my $address =
>>>>>>> $redirects{$key}[3];
>>>>>>> +                                       my $status  =
>>>>>>> $redirects{$key}[4];
>>>>>>> +                                       my $remark  =
>>>>>>> $redirects{$key}[5];
>>>>>>> +
>>>>>>> +                                       # Check if the key (id)
>>>>>>> number is even or not.
>>>>>>> +                                       if ($settings{'ID'} eq
>>>>>>> $key) {
>>>>>>> +                                               $col="bgcolor='
>>>>>>> ${Header::colouryellow}'";
>>>>>>> +                                       } elsif ($key % 2) {
>>>>>>> +                                               $col="bgcolor='
>>>>>>> $color{'color22'}'";
>>>>>>> +                                       } else {
>>>>>>> +                                               $col="bgcolor='
>>>>>>> $color{'color20'}'";
>>>>>>> +                                       }
>>>>>>> +
>>>>>>> +                                       # Choose icon for the
>>>>>>> checkbox.
>>>>>>> +                                       my $gif;
>>>>>>> +                                       my $gdesc;
>>>>>>> +
>>>>>>> +                                       # Check if the status
>>>>>>> is enabled and select the correct image and description.
>>>>>>> +                                       if ($status eq
>>>>>>> 'enabled' ) {
>>>>>>> +                                               $gif =
>>>>>>> 'on.gif';
>>>>>>> +                                               $gdesc =
>>>>>>> $Lang::tr{'click to disable'};
>>>>>>> +                                       } else {
>>>>>>> +                                               $gif =
>>>>>>> 'off.gif';
>>>>>>> +                                               $gdesc =
>>>>>>> $Lang::tr{'click to enable'};
>>>>>>> +                                       }
>>>>>>> +
>>>>>>> +                                       print <<END;
>>>>>>> +                                       <tr>
>>>>>>> +                                               <td width='15%'
>>>>>>> class='base' align='center' $col><b><font
>>>>>>> color=$interface>$Lang::tr{$interface}</font></b></td>
>>>>>>> +                                               <td width='10%'
>>>>>>> class='base' align='center' $col>$protocol</td>
>>>>>>> +                                               <td width='10%'
>>>>>>> class='base' align='center' $col>$port</td>
>>>>>>> +                                               <td width='15%'
>>>>>>> class='base' align='center' $col>&nbsp;$address</td>
>>>>>>> +                                               <td width='40%'
>>>>>>> class='base' align='center' $col>&nbsp;$remark</td>
>>>>>>> +                                               <td
>>>>>>> align='center' $col>
>>>>>>> +                                                       <form
>>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>> +                                                              
>>>>>>> <input type='hidden' name='ACTION' value='$Lang::tr{'toggle
>>>>>>> enable disable'}' />
>>>>>>> +                                                              
>>>>>>> <input type='image' name='$Lang::tr{'toggle enable disable'}'
>>>>>>> src='/images/$gif' alt='$gdesc' title='$gdesc' />
>>>>>>> +                                                              
>>>>>>> <input type='hidden' name='ID' value='$key' />
>>>>>>> +                                                       </form>
>>>>>>> +                                               </td>
>>>>>>> +                                               <td
>>>>>>> align='center' $col>
>>>>>>> +                                                       <form
>>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>> +                                                              
>>>>>>> <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}'
>>>>>>> />
>>>>>>> +                                                              
>>>>>>> <input type='image' name='$Lang::tr{'edit'}'
>>>>>>> src='/images/edit.gif' alt='$Lang::tr{'edit'}'
>>>>>>> title='$Lang::tr{'edit'}' />
>>>>>>> +                                                              
>>>>>>> <input type='hidden' name='ID' value='$key' />
>>>>>>> +                                                       </form>
>>>>>>> +                                               </td>
>>>>>>> +                                               <td
>>>>>>> align='center' $col>
>>>>>>> +                                                       <form
>>>>>>> method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>> +                                                              
>>>>>>> <input type='image' name='$Lang::tr{'remove'}'
>>>>>>> src='/images/delete.gif' title='$Lang::tr{'remove'}'
>>>>>>> alt='$Lang::tr{'remove'}'>
>>>>>>> +                                                              
>>>>>>> <input type='hidden' name='ID' value='$key'>
>>>>>>> +                                                              
>>>>>>> <input type='hidden' name='ACTION'
>>>>>>> value='$Lang::tr{'remove'}'>
>>>>>>> +                                                       </form>
>>>>>>> +                                               </td>
>>>>>>> +                                       </tr>
>>>>>>> +END
>>>>>>> +                               }
>>>>>>> +                       } else {
>>>>>>> +                               # Print notice that currently
>>>>>>> no ports are redirected.
>>>>>>> +                               print "<tr>\n";
>>>>>>> +                               print "<td class='base'
>>>>>>> colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
>>>>>>> +                               print "</tr>\n";
>>>>>>> +                       }
>>>>>>> +
>>>>>>> +               print "</table>\n";
>>>>>>> +
>>>>>>> +       # Section to add new elements or edit existing ones.
>>>>>>> +       print <<END;
>>>>>>> +       <br>
>>>>>>> +       <hr>
>>>>>>> +       <br>
>>>>>>> +       <div align='center'>
>>>>>>> +               <table width='100%' cellspacing='0' border='0'>
>>>>>>> +END
>>>>>>> +
>>>>>>> +       # Assign correct headline and button text.
>>>>>>> +       my $buttontext;
>>>>>>> +       my $entry_interface;
>>>>>>> +       my $entry_protocol;
>>>>>>> +       my $entry_port;
>>>>>>> +       my $entry_address;
>>>>>>> +       my $entry_remark;
>>>>>>> +
>>>>>>> +       # Check if an ID (key) has been given, in this case an
>>>>>>> existing entry should be edited.
>>>>>>> +       if ($settings{'ID'} ne '') {
>>>>>>> +               $buttontext = $Lang::tr{'update'};
>>>>>>> +               print "<tr><td class='boldbase'
>>>>>>> colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
>>>>>>> +
>>>>>>> +               # Grab address and remark for the given key.
>>>>>>> +               $entry_interface =
>>>>>>> $redirects{$settings{'ID'}}[0];
>>>>>>> +               $entry_protocol =
>>>>>>> $redirects{$settings{'ID'}}[1];
>>>>>>> +               $entry_port = $redirects{$settings{'ID'}}[2];
>>>>>>> +               $entry_address =
>>>>>>> $redirects{$settings{'ID'}}[3];
>>>>>>> +               $entry_remark = $redirects{$settings{'ID'}}[5];
>>>>>>> +
>>>>>>> +       } else {
>>>>>>> +               $buttontext = $Lang::tr{'add'};
>>>>>>> +               print "<tr><td class='boldbase'
>>>>>>> colspan='11'><b>$Lang::tr{'dnsforward add a new
>>>>>>> entry'}</b></td></tr>\n";
>>>>>>> +               print "<tr><td>&nbsp</td></tr>\n";
>>>>>>> +       }
>>>>>>> +
>>>>>>> +       print <<END;
>>>>>>> +                       <tr>
>>>>>>> +                               <td class='base' width='1%' 
>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>> +                               <td class='base' width='15%'
>>>>>>> bgcolor='$color{'color22'}'
>>>>>>> align='left'><b>$Lang::tr{'interface'}</b></td>
>>>>>>> +                               <td class='base' width='10%'
>>>>>>> bgcolor='$color{'color22'}'
>>>>>>> align='left'><b>$Lang::tr{'protocol'}</b></td>
>>>>>>> +                               <td class='base' width='10%'
>>>>>>> bgcolor='$color{'color22'}'
>>>>>>> align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
>>>>>>> +                               <td class='base' width='13%'
>>>>>>> bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'ip
>>>>>>> address'}</b></td>
>>>>>>> +                               <td class='base' width='30%'
>>>>>>> bgcolor='$color{'color22'}'
>>>>>>> align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
>>>>>>> +                               <td class='base' width='15%'
>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>> +                               <td class='base' width='1%' 
>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>> +                       </tr>
>>>>>>> +
>>>>>>> +                       <form method='post'
>>>>>>> action='$ENV{'SCRIPT_NAME'}'>
>>>>>>> +                       <input type='hidden' name='ID'
>>>>>>> value='$settings{'ID'}'>
>>>>>>> +                       <tr>
>>>>>>> +                               <td class='base'></td>
>>>>>>> +                               <td><select style='width:90px;'
>>>>>>> id='interface' name='REDIR_ENTRY_INTERFACE'>
>>>>>>> +END
>>>>>>> +                               if ($netsettings{'GREEN_DEV'})
>>>>>>> {
>>>>>>> +                                       if ($entry_interface eq
>>>>>>> "green") {
>>>>>>> +                                               print "<option
>>>>>>> value='green' selected='selected'
>>>>>>> {'green'}>$Lang::tr{'green'}</option>";
>>>>>>> +                                       } else { print "<option
>>>>>>> value='green' {'green'}>$Lang::tr{'green'}</option>";}
>>>>>>> +                               }
>>>>>>> +                               if ($netsettings{'BLUE_DEV'}) {
>>>>>>> +                                       if ($entry_interface eq
>>>>>>> "blue") { 
>>>>>>> +                                               print "<option
>>>>>>> value='blue' selected='selected'
>>>>>>> {'blue'}>$Lang::tr{'blue'}</option>";
>>>>>>> +                                       } else { print "<option
>>>>>>> value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
>>>>>>> +                               }
>>>>>>> +                               if ($netsettings{'ORANGE_DEV'})
>>>>>>> {
>>>>>>> +                                       if ($entry_interface eq
>>>>>>> "orange") { 
>>>>>>> +                                               print "<option
>>>>>>> value='orange' selected='selected'
>>>>>>> {'orange'}>$Lang::tr{'orange'}</option>";
>>>>>>> +                                       } else { print "<option
>>>>>>> value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
>>>>>>> +                               }
>>>>>>> +
>>>>>>> +                       print "</select><td><select
>>>>>>> style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
>>>>>>> +                       if ((!$entry_protocol) ||
>>>>>>> ($entry_protocol eq "tcp")) {
>>>>>>> +                               print "<option
>>>>>>> selected='selected' id='protocol' value='tcp'
>>>>>>> {'tcp'}>tcp</option>";
>>>>>>> +                               print "<option value='udp'
>>>>>>> {'udp'}>udp</option>";
>>>>>>> +                       } elsif ($entry_protocol eq "udp") {
>>>>>>> +                               print "<option value='tcp'
>>>>>>> {'tcp'}>tcp</option>";
>>>>>>> +                               print "<option
>>>>>>> selected='selected' value='udp' {'udp'}>udp</option>";
>>>>>>> +                       }
>>>>>>> +       print <<END;
>>>>>>> +                               </select></td>
>>>>>>> +                               <td><input type='text'
>>>>>>> name='REDIR_ENTRY_PORT'    value='$entry_port'   
>>>>>>> size='4'></td>
>>>>>>> +                               <td><input type='text'
>>>>>>> name='REDIR_ENTRY_ADDRESS' value='$entry_address'
>>>>>>> size='14'></td>
>>>>>>> +                               <td><input type='text'
>>>>>>> name='REDIR_ENTRY_REMARK'  value='$entry_remark' 
>>>>>>> size='35'></td>
>>>>>>> +                               <td width='10%'
>>>>>>> align='center'><input type='submit' name='ACTION' value=' 
>>>>>>> $buttontext  '></td>
>>>>>>> +                               <td class='base'></td>
>>>>>>> +                       </tr>
>>>>>>> +                       </form>
>>>>>>> +               </table>
>>>>>>> +       </div>
>>>>>>> +END
>>>>>>> +       &Header::closebox();
>>>>>>> +}
>>>>>>> diff --git a/config/rootfiles/common/misc-progs
>>>>>>> b/config/rootfiles/common/misc-progs
>>>>>>> index d6594b3f8..fbad2af8b 100644
>>>>>>> --- a/config/rootfiles/common/misc-progs
>>>>>>> +++ b/config/rootfiles/common/misc-progs
>>>>>>> @@ -17,6 +17,7 @@ usr/local/bin/logwatch
>>>>>>> #usr/local/bin/mpfirectrl
>>>>>>> usr/local/bin/openvpnctrl
>>>>>>> usr/local/bin/pakfire
>>>>>>> +#usr/local/bin/portredirctrl
>>>>>>> usr/local/bin/qosctrl
>>>>>>> usr/local/bin/rebuildhosts
>>>>>>> usr/local/bin/rebuildroutes
>>>>>>> diff --git a/config/rootfiles/packages/portredir
>>>>>>> b/config/rootfiles/packages/portredir
>>>>>>> new file mode 100644
>>>>>>> index 000000000..4b4ba8366
>>>>>>> --- /dev/null
>>>>>>> +++ b/config/rootfiles/packages/portredir
>>>>>>> @@ -0,0 +1,11 @@
>>>>>>> +etc/rc.d/init.d/portredir
>>>>>>> +etc/rc.d/rc0.d/K77portredir
>>>>>>> +etc/rc.d/rc3.d/S23portredir
>>>>>>> +etc/rc.d/rc6.d/K77portredir
>>>>>>> +srv/web/ipfire/cgi-bin/portredir.cgi
>>>>>>> +usr/local/bin/portredirctrl
>>>>>>> +var/ipfire/addon-lang/portredir.de.pl
>>>>>>> +var/ipfire/addon-lang/portredir.en.pl
>>>>>>> +var/ipfire/backup/addons/includes/portredir
>>>>>>> +var/ipfire/menu.d/EX-portredir.menu
>>>>>>> +var/ipfire/portredir
>>>>>>> diff --git a/lfs/portredir b/lfs/portredir
>>>>>>> new file mode 100644
>>>>>>> index 000000000..a4911f71f
>>>>>>> --- /dev/null
>>>>>>> +++ b/lfs/portredir
>>>>>>> @@ -0,0 +1,85 @@
>>>>>>> +##############################################################
>>>>>>> #################
>>>>>>> +#                                                             
>>>>>>>                #
>>>>>>> +# IPFire.org - A linux based
>>>>>>> firewall                                         #
>>>>>>> +# Copyright (C) 2007-2021  IPFire Team 
>>>>>>> <info@ipfire.org>                     #
>>>>>>> +#                                                             
>>>>>>>                #
>>>>>>> +# This program is free software: you can redistribute it
>>>>>>> and/or modify        #
>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>> published by        #
>>>>>>> +# the Free Software Foundation, either version 3 of the
>>>>>>> License, or           #
>>>>>>> +# (at your option) any later
>>>>>>> version.                                         #
>>>>>>> +#                                                             
>>>>>>>                #
>>>>>>> +# This program is distributed in the hope that it will be
>>>>>>> useful,             #
>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>> of              #
>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>> the               #
>>>>>>> +# GNU General Public License for more
>>>>>>> details.                                #
>>>>>>> +#                                                             
>>>>>>>                #
>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>> License           #
>>>>>>> +# along with this program.  If not, see <
>>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>>> +#                                                             
>>>>>>>                #
>>>>>>> +##############################################################
>>>>>>> #################
>>>>>>> +
>>>>>>> +##############################################################
>>>>>>> #################
>>>>>>> +# Definitions
>>>>>>> +##############################################################
>>>>>>> #################
>>>>>>> +
>>>>>>> +include Config
>>>>>>> +
>>>>>>> +VER        = 1.0
>>>>>>> +
>>>>>>> +THISAPP    = portredir-$(VER)
>>>>>>> +DIR_APP    = $(DIR_SRC)/$(THISAPP)
>>>>>>> +TARGET     = $(DIR_INFO)/$(THISAPP)
>>>>>>> +PROG       = portredir
>>>>>>> +PAK_VER    = 1
>>>>>>> +
>>>>>>> +##############################################################
>>>>>>> #################
>>>>>>> +# Top-level Rules
>>>>>>> +##############################################################
>>>>>>> #################
>>>>>>> +
>>>>>>> +install : $(TARGET)
>>>>>>> +
>>>>>>> +check :
>>>>>>> +
>>>>>>> +download :
>>>>>>> +
>>>>>>> +md5 :
>>>>>>> +
>>>>>>> +dist: 
>>>>>>> +       @$(PAK)
>>>>>>> +
>>>>>>> +##############################################################
>>>>>>> #################
>>>>>>> +# Installation Details
>>>>>>> +##############################################################
>>>>>>> #################
>>>>>>> +
>>>>>>> +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
>>>>>>> +       @$(PREBUILD)
>>>>>>> +       @rm -rf $(DIR_APP) && cd $(DIR_SRC)
>>>>>>> +
>>>>>>> +       #install cgi 
>>>>>>> +       install -v -m 755 $(DIR_CONF)/portredir/portredir.cgi
>>>>>>> /srv/web/ipfire/cgi-bin/
>>>>>>> +
>>>>>>> +       #create configuration dir 
>>>>>>> +       -mkdir -pv /var/ipfire/portredir/
>>>>>>> +       chown -R nobody:nobody /var/ipfire/portredir/
>>>>>>> +
>>>>>>> +       # Install include file for backup
>>>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/portredir-
>>>>>>> backup /var/ipfire/backup/addons/includes/portredir
>>>>>>> +
>>>>>>> +       # Install menu file
>>>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/EX-
>>>>>>> portredir.menu /var/ipfire/menu.d/
>>>>>>> +       chown nobody:nobody /var/ipfire/menu.d/EX-
>>>>>>> portredir.menu
>>>>>>> +
>>>>>>> +       # Install addon-specific language-files
>>>>>>> +       install -v -m 644
>>>>>>> $(DIR_CONF)/portredir/lang/portredir.*.pl /var/ipfire/addon-
>>>>>>> lang/
>>>>>>> +
>>>>>>> +       #install initscripts
>>>>>>> +       $(call INSTALL_INITSCRIPT,portredir)
>>>>>>> +
>>>>>>> +       # Create symlinks for runlevel interaction.
>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>> /etc/rc.d/rc3.d/S23portredir
>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>> /etc/rc.d/rc0.d/K77portredir
>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>> /etc/rc.d/rc6.d/K77portredir
>>>>>>> +
>>>>>>> +       @rm -rf $(DIR_APP)
>>>>>>> +       @$(POSTBUILD)
>>>>>>> +
>>>>>>> diff --git a/make.sh b/make.sh
>>>>>>> index fc03ebcd5..ab9fe881a 100755
>>>>>>> --- a/make.sh
>>>>>>> +++ b/make.sh
>>>>>>> @@ -1623,6 +1623,7 @@ buildipfire() {
>>>>>>> lfsmake2 socat
>>>>>>> lfsmake2 libcdada
>>>>>>> lfsmake2 pmacct
>>>>>>> +  lfsmake2 portredir
>>>>>>> }
>>>>>>> 
>>>>>>> buildinstaller() {
>>>>>>> diff --git a/src/initscripts/packages/portredir
>>>>>>> b/src/initscripts/packages/portredir
>>>>>>> new file mode 100644
>>>>>>> index 000000000..cc57fb9cc
>>>>>>> --- /dev/null
>>>>>>> +++ b/src/initscripts/packages/portredir
>>>>>>> @@ -0,0 +1,191 @@
>>>>>>> +#!/bin/sh
>>>>>>> +##############################################################
>>>>>>> ##########
>>>>>>> +# Begin $rc_base/init.d/portredir
>>>>>>> +#
>>>>>>> +# Description : portredir init script for DNS/NTP and custom 
>>>>>>> +#              port redirection rules
>>>>>>> +#
>>>>>>> +##############################################################
>>>>>>> ##########
>>>>>>> +
>>>>>>> +. /etc/sysconfig/rc
>>>>>>> +. ${rc_functions}
>>>>>>> +
>>>>>>> +IPT="/sbin/iptables";
>>>>>>> +parent_chain="PREROUTING";
>>>>>>> +chain="PORT_REDIRECT";
>>>>>>> +
>>>>>>> +confdir="/var/ipfire/portredir";
>>>>>>> +settingsfile="${confdir}/settings";
>>>>>>> +redirectsfile="${confdir}/redirects";
>>>>>>> +SYSLOG="NO";
>>>>>>> +VERBOSE="NO";
>>>>>>> +
>>>>>>> +eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings);
>>>>>>> +eval $(/usr/local/bin/readhash ${settingsfile});
>>>>>>> +
>>>>>>> +logtext() {
>>>>>>> +       if [ "${SYSLOG}" = "YES" ]; then logger -t "portredir"
>>>>>>> ${1}; fi;
>>>>>>> +       if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
>>>>>>> +
>>>>>>> +create_chain() {
>>>>>>> +
>>>>>>> +       local line=$(${IPT} -t nat -L ${parent_chain} --line-
>>>>>>> numbers |grep "SQUID" |awk '{printf($1)}');
>>>>>>> +
>>>>>>> +       if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z
>>>>>>> "${REDIR_ENABLE_ADDON}" ]]; then
>>>>>>> +               logtext "addon not enabled in web
>>>>>>> interface...";
>>>>>>> +               echo "Portredir addon not enabled in web
>>>>>>> interface...";
>>>>>>> +               exit 0;
>>>>>>> +       fi;
>>>>>>> +
>>>>>>> +       if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>>>> ${chain})" ]; then
>>>>>>> +               ${IPT} -t nat -N ${chain};
>>>>>>> +
>>>>>>> +               if [ ! -z "${line}" ]; then
>>>>>>> +                       logtext "create chain ${chain} and link
>>>>>>> in ${parent_chain} at position ${line}...";
>>>>>>> +                       ${IPT} -t nat -I ${parent_chain}
>>>>>>> ${line} -j ${chain};
>>>>>>> +               else
>>>>>>> +                       logtext "create chain ${chain} and link
>>>>>>> in ${parent_chain} at last position...";
>>>>>>> +                       ${IPT} -t nat -A ${parent_chain} -j
>>>>>>> ${chain};
>>>>>>> +               fi
>>>>>>> +       else
>>>>>>> +               return 1;
>>>>>>> +       fi;
>>>>>>> +       return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +remove_chain() {
>>>>>>> +       if [ ! -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>>>> ${chain})" ]; then
>>>>>>> +               logtext "remove chain ${chain} and link in
>>>>>>> ${parent_chain} from system...";
>>>>>>> +               ${IPT} -t nat -D "${parent_chain}" -j ${chain};
>>>>>>> +               ${IPT} -t nat -F ${chain};
>>>>>>> +               ${IPT} -t nat -X ${chain};
>>>>>>> +       else
>>>>>>> +               return 1;
>>>>>>> +       fi;
>>>>>>> +       return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +activate_custom_redirections() {
>>>>>>> +       
>>>>>>> +       local array=();
>>>>>>> +       local redirects=();
>>>>>>> +       local i;
>>>>>>> +       index=();
>>>>>>> +       iface=();
>>>>>>> +       protocol=();
>>>>>>> +       port=();
>>>>>>> +       targetip=();
>>>>>>> +       enabled=();
>>>>>>> +
>>>>>>> +       IFS=$'\n' read -d '' -ra redirects < ${redirectsfile};
>>>>>>> +
>>>>>>> +       for i in "${!redirects[@]}"
>>>>>>> +       do
>>>>>>> +               IFS=$',' read -ra array <<< ${redirects[i]};
>>>>>>> +               index[i]=${array[0]};
>>>>>>> +               iface[i]=${array[1]};
>>>>>>> +               protocol[i]=${array[2]};
>>>>>>> +               port[i]=${array[3]};
>>>>>>> +               targetip[i]=${array[4]};
>>>>>>> +               enabled[i]=${array[5]};
>>>>>>> +       done
>>>>>>> +
>>>>>>> +       for i in "${!index[@]}"
>>>>>>> +       do
>>>>>>> +               if [[ ! -z "${GREEN_DEV}" &&  "${iface[i]}" =
>>>>>>> "green" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>> +
>>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>>> ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>>> ${port[i]} ";
>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>> ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>> ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>> +               fi
>>>>>>> +               if [[ ! -z "${BLUE_DEV}" &&  "${iface[i]}" =
>>>>>>> "blue" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>>> ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>>> ${port[i]} ";
>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>> ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>> ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>> +               fi
>>>>>>> +               if [[ ! -z "${ORANGE_DEV}" &&  "${iface[i]}" =
>>>>>>> "orange" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>>> ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>>> ${port[i]} ";
>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>> ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>> ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>> +               fi
>>>>>>> +       done
>>>>>>> +       unset array redirects i index iface protocol port
>>>>>>> targetip enabled;
>>>>>>> +       return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +activate_redirections() {
>>>>>>> +
>>>>>>> +       if ! create_chain; then return 1; fi;
>>>>>>> +       
>>>>>>> +       # Force DNS REDIRECTs on GREEN (udp, tcp, 53)
>>>>>>> +       if [[ "${REDIR_DNS_GREEN}" == "on" && 
>>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>>> udp -m udp --dport domain -j REDIRECT;
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>>> ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>>> tcp -m tcp --dport domain -j REDIRECT;
>>>>>>> +       fi
>>>>>>> +
>>>>>>> +       # Force DNS REDIRECTs on BLUE (udp, tcp, 53)
>>>>>>> +       if [[ "${REDIR_DNS_BLUE}" == "on" && 
>>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>>>> -m udp --dport domain -j REDIRECT
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>>> ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p tcp
>>>>>>> -m tcp --dport domain -j REDIRECT
>>>>>>> +       fi
>>>>>>> +
>>>>>>> +       # Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
>>>>>>> +       if [[ "${REDIR_DNS_ORANGE}" == "on" && 
>>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>>> udp -m udp --dport domain -j REDIRECT
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>>> ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>>> tcp -m tcp --dport domain -j REDIRECT
>>>>>>> +       fi
>>>>>>> +
>>>>>>> +       # Force NTP REDIRECTs on GREEN (udp, 123)
>>>>>>> +       if [[ "${REDIR_NTP_GREEN}" == "on" && 
>>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>>> +       fi
>>>>>>> +
>>>>>>> +       # Force NTP REDIRECTs on BLUE (udp, 123)
>>>>>>> +       if [[ "${REDIR_NTP_BLUE}" == "on" && 
>>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>>>> -m udp --dport ntp -j REDIRECT
>>>>>>> +       fi
>>>>>>> +
>>>>>>> +       # Force NTP REDIRECTs on ORANGE (udp, 123)
>>>>>>> +       if [[ "${REDIR_NTP_ORANGE}" == "on" && 
>>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>>> +       fi
>>>>>>> +
>>>>>>> +       if ! activate_custom_redirections; then return 1; fi;
>>>>>>> +
>>>>>>> +       return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +case "${1}" in
>>>>>>> +       start)
>>>>>>> +               boot_mesg "Loading port redirections..."
>>>>>>> +               activate_redirections;
>>>>>>> +               evaluate_retval;
>>>>>>> +               ;;
>>>>>>> +
>>>>>>> +       stop)   
>>>>>>> +               boot_mesg "Removing port redirections..."
>>>>>>> +               remove_chain;
>>>>>>> +               evaluate_retval;
>>>>>>> +               ;;
>>>>>>> +
>>>>>>> +       restart)
>>>>>>> +               ${0} stop
>>>>>>> +               ${0} start
>>>>>>> +               ;;
>>>>>>> +
>>>>>>> +       *)
>>>>>>> +               echo "Usage: ${0} {start|stop|restart}"
>>>>>>> +               exit 1
>>>>>>> +               ;;
>>>>>>> +esac
>>>>>>> +
>>>>>>> +# End $rc_base/init.d/portredir
>>>>>>> diff --git a/src/misc-progs/Makefile b/src/misc-progs/Makefile
>>>>>>> index 7c3ef7529..850f8fdcc 100644
>>>>>>> --- a/src/misc-progs/Makefile
>>>>>>> +++ b/src/misc-progs/Makefile
>>>>>>> @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
>>>>>>>        wirelessctrl getipstat qosctrl \
>>>>>>>        redctrl syslogdctrl extrahdctrl sambactrl \
>>>>>>>        smartctrl clamavctrl addonctrl pakfire mpfirectrl
>>>>>>> wlanapctrl \
>>>>>>> -       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>>>> rebuildroutes \
>>>>>>> +       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>>>> rebuildroutes portredirctrl \
>>>>>>>        getconntracktable wirelessclient torctrl ddnsctrl
>>>>>>> unboundctrl \
>>>>>>>        captivectrl
>>>>>>> 
>>>>>>> diff --git a/src/misc-progs/portredirctrl.c b/src/misc-
>>>>>>> progs/portredirctrl.c
>>>>>>> new file mode 100644
>>>>>>> index 000000000..7897d711c
>>>>>>> --- /dev/null
>>>>>>> +++ b/src/misc-progs/portredirctrl.c
>>>>>>> @@ -0,0 +1,47 @@
>>>>>>> +/* This file is part of the IPFire Firewall.
>>>>>>> + *
>>>>>>> + * This program is distributed under the terms of the GNU
>>>>>>> General Public
>>>>>>> + * Licence.  See the file COPYING for details.
>>>>>>> + *
>>>>>>> + */
>>>>>>> +
>>>>>>> +#include <stdlib.h>
>>>>>>> +#include <stdio.h>
>>>>>>> +#include <string.h>
>>>>>>> +#include <unistd.h>
>>>>>>> +#include <sys/types.h>
>>>>>>> +#include <fcntl.h>
>>>>>>> +#include "setuid.h"
>>>>>>> +
>>>>>>> +int main(int argc, char *argv[]) {
>>>>>>> +       if (!(initsetuid()))
>>>>>>> +               exit(1);
>>>>>>> +
>>>>>>> +       // Check what command is asked
>>>>>>> +        if (argc < 2) {
>>>>>>> +                fprintf(stderr, "\nNo argument
>>>>>>> given.\n\nportredirctrl
>>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>>> +                exit(1);
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        if (strcmp(argv[1], "start") == 0) {
>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>> start");
>>>>>>> +        } else if (strcmp(argv[1], "stop") == 0) {
>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>> stop");
>>>>>>> +        } else if (strcmp(argv[1], "restart") == 0) {
>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>> restart");
>>>>>>> +       } else if (strcmp(argv[1], "enable") == 0) {
>>>>>>> +               safe_system("touch
>>>>>>> /var/ipfire/portredir/enable");
>>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>>> /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
>>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>>> /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
>>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>>> /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
>>>>>>> +       } else if (strcmp(argv[1], "disable") == 0) {
>>>>>>> +               safe_system("/etc/rc.d/init.d/portredir stop");
>>>>>>> +               safe_system("unlink
>>>>>>> /var/ipfire/portredir/enable");
>>>>>>> +               safe_system("rm -rf /etc/rc.d/rc*.d/*portredir
>>>>>>>> /dev/null 2>&1");
>>>>>>> +        } else {
>>>>>>> +                fprintf(stderr, "\nBad argument
>>>>>>> given.\n\nportredirctrl
>>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>>> +                exit(1);
>>>>>>> +        }
>>>>>>> +
>>>>>>> +       return 0;
>>>>>>> +}
>>>>>>> diff --git a/src/paks/portredir/install.sh
>>>>>>> b/src/paks/portredir/install.sh
>>>>>>> new file mode 100644
>>>>>>> index 000000000..9f69aeae2
>>>>>>> --- /dev/null
>>>>>>> +++ b/src/paks/portredir/install.sh
>>>>>>> @@ -0,0 +1,32 @@
>>>>>>> +#!/bin/bash
>>>>>>> +##############################################################
>>>>>>> ##############
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +# This file is part of the IPFire
>>>>>>> Firewall.                                #
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>>> modify           #
>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>> published by     #
>>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>>> License, or        #
>>>>>>> +# (at your option) any later
>>>>>>> version.                                      #
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>>> useful,                #
>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>> of           #
>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>> the            #
>>>>>>> +# GNU General Public License for more
>>>>>>> details.                             #
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>> License        #
>>>>>>> +# along with IPFire; if not, write to the Free
>>>>>>> Software                    #
>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>>> 02111-1307 USA #
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +# Copyright (C) 2021 IPFire-Team
>>>>>>> <info@ipfire.org>.                        #
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +##############################################################
>>>>>>> ##############
>>>>>>> +#
>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>> +extract_files
>>>>>>> +restore_backup ${NAME}
>>>>>>> +
>>>>>>> +/usr/local/bin/update-lang-cache
>>>>>>> +
>>>>>>> +chown root:nobody /usr/local/bin/portredirctrl
>>>>>>> +chmod 4750 /usr/local/bin/portredirctrl
>>>>>>> +chmod u+s /usr/local/bin/portredirctrl
>>>>>>> diff --git a/src/paks/portredir/uninstall.sh
>>>>>>> b/src/paks/portredir/uninstall.sh
>>>>>>> new file mode 100644
>>>>>>> index 000000000..df9270125
>>>>>>> --- /dev/null
>>>>>>> +++ b/src/paks/portredir/uninstall.sh
>>>>>>> @@ -0,0 +1,28 @@
>>>>>>> +#!/bin/bash
>>>>>>> +##############################################################
>>>>>>> ##############
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +# This file is part of the IPFire
>>>>>>> Firewall.                                #
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>>> modify           #
>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>> published by     #
>>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>>> License, or        #
>>>>>>> +# (at your option) any later
>>>>>>> version.                                      #
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>>> useful,                #
>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>> of           #
>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>> the            #
>>>>>>> +# GNU General Public License for more
>>>>>>> details.                             #
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>> License        #
>>>>>>> +# along with IPFire; if not, write to the Free
>>>>>>> Software                    #
>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>>> 02111-1307 USA #
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>>> <info@ipfire.org>.                        #
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +##############################################################
>>>>>>> ##############
>>>>>>> +#
>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>> +make_backup ${NAME}
>>>>>>> +remove_files
>>>>>>> +
>>>>>>> +/usr/local/bin/update-lang-cache
>>>>>>> diff --git a/src/paks/portredir/update.sh
>>>>>>> b/src/paks/portredir/update.sh
>>>>>>> new file mode 100644
>>>>>>> index 000000000..89c40d0d7
>>>>>>> --- /dev/null
>>>>>>> +++ b/src/paks/portredir/update.sh
>>>>>>> @@ -0,0 +1,26 @@
>>>>>>> +#!/bin/bash
>>>>>>> +##############################################################
>>>>>>> ##############
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +# This file is part of the IPFire
>>>>>>> Firewall.                                #
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>>> modify           #
>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>> published by     #
>>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>>> License, or        #
>>>>>>> +# (at your option) any later
>>>>>>> version.                                      #
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>>> useful,                #
>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>> of           #
>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>> the            #
>>>>>>> +# GNU General Public License for more
>>>>>>> details.                             #
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>> License        #
>>>>>>> +# along with IPFire; if not, write to the Free
>>>>>>> Software                    #
>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>>> 02111-1307 USA #
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>>> <info@ipfire.org>.                        #
>>>>>>> +#                                                             
>>>>>>>             #
>>>>>>> +##############################################################
>>>>>>> ##############
>>>>>>> +#
>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>> +./uninstall.sh
>>>>>>> +./install.sh
>>>>>>> -- 
>>>>>>> 2.18.0
>>>>>>> 
>>>>>> 
>>>>> 
>>>> 
>>> 
>> 
>
  
Michael Tremer July 1, 2021, 3:24 p.m. UTC | #8
@Stefan: There should be an exception in the UI that these rules can be created when REDIRECT is being used.

> On 1 Jul 2021, at 16:04, Jon Murphy <jcmurphy26@gmail.com> wrote:
> 
>> You probably want “any” as destination.
> 
> Those are the only choices that allow a Save.  When I enter an IP address I get this error:
> 
> 
> Error messages
> 
> Source and destination IP addresses are from the same subnet.  
> 
> 
> 
> Jon
> 
>> On Jul 1, 2021, at 3:08 AM, Michael Tremer <michael.tremer@ipfire.org> wrote:
>> 
>> Hey Jon,
>> 
>> You probably want “any” as destination.
>> 
>> -Michael
>> 
>>> On 1 Jul 2021, at 04:08, Jon Murphy <jcmurphy26@gmail.com> wrote:
>>> 
>>> Hi Stefan,
>>> 
>>> Thank you for taking this on!
>>> 
>>> I applied the patchwork.ipfire patch.
>>> 
>>> I think I entered something wrong since I cannot get things to work.  I tried both with Destination Firewall GREEN & Firewall RED.
>>> 
>>> 
>>> Does the Firewall Rule seem right?
>>> 
>>> Best regards,
>>> Jon
>>> 
>>> 
>>> Here is the rule I set up:
>>> 
>>> 
>>> <Screen Shot 2021-06-30 at 9.53.52 PM.png>
>>> 
>>> <Screen Shot 2021-06-30 at 9.53.10 PM.png>
>>> 
>>> 
>>> And this is what I see with conntrack:
>>> 
>>> conntrack -E -e NEW,UPDATE | grep -e "=53 "
>>> 
>>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=51169 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=51169
>>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=54168 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=54168
>>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=56094 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=56094
>>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=52964 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=52964
>>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=53279 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=53279
>>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=61657 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=61657
>>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=57723 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=57723
>>> 
>>> 
>>> 
>>> 
>>>> On Jun 30, 2021, at 2:14 PM, Stefan Schantl <stefan.schantl@ipfire.org> wrote:
>>>> 
>>>> Hello Matthias, Hello Michael, Hello Jon, Hello *,
>>>> 
>>>> I've followed the conversation on this list since the first mail and
>>>> thoughts about forcing DNS traffic to use the local resolver.
>>>> 
>>>> It was a very long journey and lot of time and work has been spent to
>>>> get to the present point.
>>>> 
>>>> As Michael requested here, I've digged through the lines of the perl
>>>> script which is responsible for creating the firewall rules and
>>>> surprisingly found that everyting which is needed to create generic
>>>> REDIRECT rules already was written in the past - it just did not work
>>>> as designed/expected.
>>>> 
>>>> Finaly I was able to adjust these lines of code and to repair that
>>>> feature.
>>>> 
>>>> A redirect rule can be created by picking a single host or group of
>>>> hosts or entire network(s) as source, selecting NAT (DNAT) and choosing
>>>> the Firewall itself as target.
>>>> 
>>>> The protocol or service or service group which should be redirected has
>>>> to be selected afterwards. If you want to redirect a given port to
>>>> another one it can be specified as "Target port".
>>>> 
>>>> All created redirect rules are displayed as "input rules".
>>>> 
>>>> 
>>>> The patch directly can be accessed here:
>>>> 
>>>> https://patchwork.ipfire.org/project/ipfire/patch/20210630184031.7726-1-stefan.schantl@ipfire.org/
>>>> 
>>>> Best regards,
>>>> 
>>>> -Stefan
>>>> 
>>>>> Hello,
>>>>> 
>>>>>> On 28 Jun 2021, at 18:53, Jon Murphy <jcmurphy26@gmail.com> wrote:
>>>>>> 
>>>>>> Hi Michael!  Happy Monday!
>>>>>> 
>>>>>> 
>>>>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>>>>> of code instead of adding many hundreds of lines?
>>>>>>> 
>>>>>>> Please can someone elaborate on this more?
>>>>>> 
>>>>>> Doing a DNS redirect, via the WegBUI, has been an issue since
>>>>>> 2015.  I found this quote in the old forum:
>>>>>> 
>>>>>> "Having investigated a bit more I have concluded that it's not
>>>>>> currently possible to create such rules through the WUI.
>>>>>> 
>>>>>> There are a number of obstacles:
>>>>>> 1. It is not allowed to create a rule where source IP and
>>>>>> destination nat IP is on the same subnetwork (e.g. GREEN), WUI
>>>>>> error message: "Source and destination IP addresses are from the
>>>>>> same subnet."
>>>>>> 
>>>>>> 2. WUI will not allow you to create a rule without a destination
>>>>>> (the filtered packet must adhere to a destination, not only a port)
>>>>>> and the destination MUST be an IP address of one of the IPFire
>>>>>> interfaces, which limits whats possible a great deal." 
>>>>> 
>>>>> And these cannot be changed?
>>>>> 
>>>>>> And I found this from 2016:
>>>>>> https://bugzilla.ipfire.org/show_bug.cgi?id=11168
>>>>>> 
>>>>>> So I am guessing that no one has been able to determine a way to
>>>>>> extend the WebGUI.  
>>>>> 
>>>>> Has anyone tried? I do not see any obvious reasons why this should
>>>>> not be possible.
>>>>> 
>>>>>> I am curious - Who created the 
>>>>>> https://ipfire:444/cgi-bin/firewall.cgi page?  And could they help?
>>>>> 
>>>>> -Michael
>>>>> 
>>>>>> Jon
>>>>>> 
>>>>>> 
>>>>>>> On Jun 28, 2021, at 11:04 AM, Michael Tremer <
>>>>>>> michael.tremer@ipfire.org> wrote:
>>>>>>> 
>>>>>>> Hello Matthias,
>>>>>>> 
>>>>>>>> On 27 Jun 2021, at 14:48, Matthias Fischer <
>>>>>>>> matthias.fischer@ipfire.org> wrote:
>>>>>>>> 
>>>>>>>> From: Marcel Lorenz <marcel.lorenz@ipfire.org>
>>>>>>> 
>>>>>>> Thank you for sending this patch on Marcel’s behalf, but I would
>>>>>>> much more prefer if he would submit his patches on his own. I do
>>>>>>> not see why that isn’t possible.
>>>>>>> 
>>>>>>>> Please note:
>>>>>>>> This is a new addon written by Marcel Lorenz <
>>>>>>>> marcel.lorenz@ipfire.org>.
>>>>>>>> 
>>>>>>>> It adds a new GUI to IPFire for DNS/NTP *and* user specific
>>>>>>>> port redirections.
>>>>>>>> 
>>>>>>>> How its working:
>>>>>>>> It has exactly the same functionalities as "Forcing
>>>>>>>> DNS/NTP..."  - and some more.
>>>>>>>> 
>>>>>>>> By setting switches, DNS/NTP requests are automatically
>>>>>>>> redirected to the local IPFire DNS/NTP servers.
>>>>>>>> 
>>>>>>>> Additionally, the user can specify custom redirections.
>>>>>>>> 
>>>>>>>> These rules are added to a new chain in PREROUTING =>
>>>>>>>> PORT_REDIRECT.
>>>>>>>> 
>>>>>>>> To avoid problems with (e.g.) transparent 'squid'
>>>>>>>> configurations,
>>>>>>>> redirection rules are added automatically before existing
>>>>>>>> 'squid' rules.
>>>>>>> 
>>>>>>> This message does unfortunately not say why this add-on would be
>>>>>>> useful. I am emphasising this again and again that it is not very
>>>>>>> important how something is done specially. That should be
>>>>>>> commented in the code and other implementation details should
>>>>>>> also be documented there.
>>>>>>> 
>>>>>>> As I have stated on this functionality many times before, I do
>>>>>>> not see why this is necessary at all.
>>>>>>> 
>>>>>>> Why is this an add-on?
>>>>>>> 
>>>>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>>>>> of code instead of adding many hundreds of lines?
>>>>>>> 
>>>>>>> Please can someone elaborate on this more?
>>>>>>> 
>>>>>>> -Michael
>>>>>>> 
>>>>>>>> Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
>>>>>>>> ---
>>>>>>>> config/portredir/EX-portredir.menu    |   6 +
>>>>>>>> config/portredir/lang/portredir.de.pl |  19 +
>>>>>>>> config/portredir/lang/portredir.en.pl |  19 +
>>>>>>>> config/portredir/portredir-backup     |   1 +
>>>>>>>> config/portredir/portredir.cgi        | 525
>>>>>>>> ++++++++++++++++++++++++++
>>>>>>>> config/rootfiles/common/misc-progs    |   1 +
>>>>>>>> config/rootfiles/packages/portredir   |  11 +
>>>>>>>> lfs/portredir                         |  85 +++++
>>>>>>>> make.sh                               |   1 +
>>>>>>>> src/initscripts/packages/portredir    | 191 ++++++++++
>>>>>>>> src/misc-progs/Makefile               |   2 +-
>>>>>>>> src/misc-progs/portredirctrl.c        |  47 +++
>>>>>>>> src/paks/portredir/install.sh         |  32 ++
>>>>>>>> src/paks/portredir/uninstall.sh       |  28 ++
>>>>>>>> src/paks/portredir/update.sh          |  26 ++
>>>>>>>> 15 files changed, 993 insertions(+), 1 deletion(-)
>>>>>>>> create mode 100644 config/portredir/EX-portredir.menu
>>>>>>>> create mode 100644 config/portredir/lang/portredir.de.pl
>>>>>>>> create mode 100644 config/portredir/lang/portredir.en.pl
>>>>>>>> create mode 100644 config/portredir/portredir-backup
>>>>>>>> create mode 100644 config/portredir/portredir.cgi
>>>>>>>> create mode 100644 config/rootfiles/packages/portredir
>>>>>>>> create mode 100644 lfs/portredir
>>>>>>>> create mode 100644 src/initscripts/packages/portredir
>>>>>>>> create mode 100644 src/misc-progs/portredirctrl.c
>>>>>>>> create mode 100644 src/paks/portredir/install.sh
>>>>>>>> create mode 100644 src/paks/portredir/uninstall.sh
>>>>>>>> create mode 100644 src/paks/portredir/update.sh
>>>>>>>> 
>>>>>>>> diff --git a/config/portredir/EX-portredir.menu
>>>>>>>> b/config/portredir/EX-portredir.menu
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..8376e8053
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/portredir/EX-portredir.menu
>>>>>>>> @@ -0,0 +1,6 @@
>>>>>>>> +    $subfirewall->{'95.portredir'} = {
>>>>>>>> +                               'caption' =>
>>>>>>>> $Lang::tr{'portredir port redirections'},
>>>>>>>> +                               'uri' => '/cgi-
>>>>>>>> bin/portredir.cgi',
>>>>>>>> +                               'title' =>
>>>>>>>> "$Lang::tr{'portredir port redirections'}",
>>>>>>>> +                               'enabled' => 1
>>>>>>>> +                               };
>>>>>>>> diff --git a/config/portredir/lang/portredir.de.pl
>>>>>>>> b/config/portredir/lang/portredir.de.pl
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..b932d4a85
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/portredir/lang/portredir.de.pl
>>>>>>>> @@ -0,0 +1,19 @@
>>>>>>>> +%tr = (
>>>>>>>> +%tr,
>>>>>>>> +'portredir enable addon' => 'Addon aktivieren',
>>>>>>>> +'portredir common settings' => 'Allgemeine Einstellungen',
>>>>>>>> +'portredir port redirections' => 'Portumleitungen',
>>>>>>>> +'portredir fw for interface' => 'Firewalloptionen für das
>>>>>>>> Interface',
>>>>>>>> +'portredir enable user redirections' => 'Aktiviere
>>>>>>>> benutzerdefinierte Portumleitungen',
>>>>>>>> +'portredir force local dns' => 'Erzwinge lokale DNS-Server',
>>>>>>>> +'portredir force local ntp' => 'Erzwinge lokale NTP-Server',
>>>>>>>> +'portredir custom redirections' => 'Benutzerdefinierte
>>>>>>>> Portumleitungen',
>>>>>>>> +'portredir remove rule' => 'Entferne Regel',
>>>>>>>> +'portredir add rule' => 'Hinzufügen',
>>>>>>>> +'portredir no entries' => 'Keine Einträge vorhanden.',
>>>>>>>> +'portredir invalid address' => 'Ungültige Host-Addresse.',
>>>>>>>> +'portredir empty input' => 'Fehlende Angabe: Bitte geben Sie
>>>>>>>> einen gültigen Host an.',
>>>>>>>> +'portredir save to activate' => 'Speichern, um Änderungen zu
>>>>>>>> aktivieren',
>>>>>>>> +);
>>>>>>>> +
>>>>>>>> +#EOF
>>>>>>>> diff --git a/config/portredir/lang/portredir.en.pl
>>>>>>>> b/config/portredir/lang/portredir.en.pl
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..f442f3eaa
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/portredir/lang/portredir.en.pl
>>>>>>>> @@ -0,0 +1,19 @@
>>>>>>>> +%tr = (
>>>>>>>> +%tr,
>>>>>>>> +'portredir enable addon' => 'Enable addon',
>>>>>>>> +'portredir common settings' => 'Common settings',
>>>>>>>> +'portredir port redirections' => 'Port redirections',
>>>>>>>> +'portredir fw for interface' => 'Firewall options for
>>>>>>>> interface',
>>>>>>>> +'portredir enable user redirections' => 'Enable user port
>>>>>>>> redirections',
>>>>>>>> +'portredir force local dns' => 'Enforce local DNS servers',
>>>>>>>> +'portredir force local ntp' => 'Enforce local NTP servers',
>>>>>>>> +'portredir custom redirections' => 'Custom port redirections',
>>>>>>>> +'portredir remove rule' => 'Remove rule',
>>>>>>>> +'portredir add rule' => 'Add new',
>>>>>>>> +'portredir no entries' => 'No entries at the moment.',
>>>>>>>> +'portredir invalid address' => 'Invalid host address.',
>>>>>>>> +'portredir empty input' => 'Empty input: Please enter a valid
>>>>>>>> host.',
>>>>>>>> +'portredir save to activate' => 'Save to activate changes',
>>>>>>>> +);
>>>>>>>> +
>>>>>>>> +#EOF
>>>>>>>> diff --git a/config/portredir/portredir-backup
>>>>>>>> b/config/portredir/portredir-backup
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..bd2ada742
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/portredir/portredir-backup
>>>>>>>> @@ -0,0 +1 @@
>>>>>>>> +/var/ipfire/portredir
>>>>>>>> diff --git a/config/portredir/portredir.cgi
>>>>>>>> b/config/portredir/portredir.cgi
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..4913dda3f
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/portredir/portredir.cgi
>>>>>>>> @@ -0,0 +1,525 @@
>>>>>>>> +#!/usr/bin/perl
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +# IPFire.org - A linux based
>>>>>>>> firewall                                         #
>>>>>>>> +# Copyright (C) 2021  IPFire Team 
>>>>>>>> <info@ipfire.org>                          #
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +# This program is free software: you can redistribute it
>>>>>>>> and/or modify        #
>>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>>> published by        #
>>>>>>>> +# the Free Software Foundation, either version 3 of the
>>>>>>>> License, or           #
>>>>>>>> +# (at your option) any later
>>>>>>>> version.                                         #
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +# This program is distributed in the hope that it will be
>>>>>>>> useful,             #
>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>>> of              #
>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>>> the               #
>>>>>>>> +# GNU General Public License for more
>>>>>>>> details.                                #
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>>> License           #
>>>>>>>> +# along with this program.  If not, see <
>>>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +
>>>>>>>> +use strict;
>>>>>>>> +
>>>>>>>> +# enable only the following on debugging purpose
>>>>>>>> +use warnings;
>>>>>>>> +use CGI::Carp 'fatalsToBrowser';
>>>>>>>> +
>>>>>>>> +require '/var/ipfire/general-functions.pl';
>>>>>>>> +require "${General::swroot}/lang.pl";
>>>>>>>> +require "${General::swroot}/header.pl";
>>>>>>>> +
>>>>>>>> +# File declarations
>>>>>>>> +my $settingsfile = "${General::swroot}/portredir/settings";
>>>>>>>> +my $redirectsfile = "${General::swroot}/portredir/redirects";
>>>>>>>> +
>>>>>>>> +# Create empty settingsfiles if they does not exist yet
>>>>>>>> +unless (-e "$settingsfile") { system ("touch $settingsfile");
>>>>>>>> }
>>>>>>>> +unless (-e "$redirectsfile") { system ("touch
>>>>>>>> $redirectsfile"); }
>>>>>>>> +
>>>>>>>> +# load ipfire settings
>>>>>>>> +our %netsettings = ();
>>>>>>>> +our %color = ();
>>>>>>>> +&General::readhash("${General::swroot}/ethernet/settings",
>>>>>>>> \%netsettings);
>>>>>>>> +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include
>>>>>>>> /colors.txt", \%color);
>>>>>>>> +
>>>>>>>> +my %settings=();
>>>>>>>> +my %portredirs=();
>>>>>>>> +my %checked=(); # Checkbox manipulations
>>>>>>>> +my $errormessage='';
>>>>>>>> +my %selected=();
>>>>>>>> +our %redirects=();
>>>>>>>> +
>>>>>>>> +$settings{'ACTION'} = '';
>>>>>>>> +$settings{'REDIR_ENABLE_ADDON'}="off";
>>>>>>>> +$settings{'REDIR_CUSTOM_GREEN'}="off";
>>>>>>>> +$settings{'REDIR_CUSTOM_BLUE'}="off";
>>>>>>>> +$settings{'REDIR_CUSTOM_ORANGE'}="off";
>>>>>>>> +$settings{'REDIR_DNS_GREEN'}="off";
>>>>>>>> +$settings{'REDIR_NTP_GREEN'}="off";
>>>>>>>> +$settings{'REDIR_DNS_BLUE'}="off";
>>>>>>>> +$settings{'REDIR_NTP_BLUE'}="off";
>>>>>>>> +$settings{'REDIR_DNS_ORANGE'}="off";
>>>>>>>> +$settings{'REDIR_NTP_ORANGE'}="off";
>>>>>>>> +
>>>>>>>> +&Header::showhttpheaders();
>>>>>>>> +
>>>>>>>> +# Get GUI values
>>>>>>>> +&Header::getcgihash(\%settings);
>>>>>>>> +
>>>>>>>> +# Save action
>>>>>>>> +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
>>>>>>>> +
>>>>>>>> +       # If custom rules enabled, deactivate default rules on
>>>>>>>> interface
>>>>>>>> +       if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
>>>>>>>> +               $settings{'REDIR_DNS_GREEN'}="off";
>>>>>>>> +               $settings{'REDIR_NTP_GREEN'}="off";
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
>>>>>>>> +               $settings{'REDIR_DNS_BLUE'}="off";
>>>>>>>> +               $settings{'REDIR_NTP_BLUE'}="off";
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
>>>>>>>> +               $settings{'REDIR_DNS_ORANGE'}="off";
>>>>>>>> +               $settings{'REDIR_NTP_ORANGE'}="off";
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       &General::writehash($settingsfile, \%settings);
>>>>>>>> +
>>>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
>>>>>>>> +               system ('/usr/local/bin/portredirctrl restart
>>>>>>>>> /dev/null 2>&1');
>>>>>>>> +               system ('/usr/local/bin/portredirctrl enable
>>>>>>>>> /dev/null 2>&1');
>>>>>>>> +               &General::log('portredir addon: port
>>>>>>>> redirections enabled');
>>>>>>>> +       }
>>>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
>>>>>>>> +               system ('/usr/local/bin/portredirctrl disable
>>>>>>>>> /dev/null 2>&1');
>>>>>>>> +               system ('/usr/local/bin/portredirctrl stop
>>>>>>>>> /dev/null 2>&1');
>>>>>>>> +               &General::log('portredir addon: port
>>>>>>>> redirections disabled');
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +# Add/edit an entry to the redirectsfile.
>>>>>>>> +
>>>>>>>> +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) ||
>>>>>>>> ($settings{'ACTION'} eq $Lang::tr{'update'})) {
>>>>>>>> +
>>>>>>>> +       # Check if any input has been performed.
>>>>>>>> +       if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
>>>>>>>> +
>>>>>>>> +               # Check if the given input is no valid IP-
>>>>>>>> address, display an error message.
>>>>>>>> +               if
>>>>>>>> (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
>>>>>>>> +                       $errormessage = "$Lang::tr{'portredir
>>>>>>>> invalid address'}";
>>>>>>>> +               }
>>>>>>>> +       } else {
>>>>>>>> +               $errormessage = "$Lang::tr{'portredir empty
>>>>>>>> input'}";
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       # Go further if there was no error.
>>>>>>>> +       if ($errormessage eq '') {
>>>>>>>> +               my %redirects = ();
>>>>>>>> +               my $id;
>>>>>>>> +               my $status;
>>>>>>>> +
>>>>>>>> +               # Assign hash values.
>>>>>>>> +               my $new_entry_interface =
>>>>>>>> $settings{'REDIR_ENTRY_INTERFACE'};
>>>>>>>> +               my $new_entry_protocol =
>>>>>>>> $settings{'REDIR_ENTRY_PROTOCOL'};
>>>>>>>> +               my $new_entry_port =
>>>>>>>> $settings{'REDIR_ENTRY_PORT'};
>>>>>>>> +               my $new_entry_address =
>>>>>>>> $settings{'REDIR_ENTRY_ADDRESS'};
>>>>>>>> +               my $new_entry_remark =
>>>>>>>> $settings{'REDIR_ENTRY_REMARK'};
>>>>>>>> +
>>>>>>>> +               # Read-in redirectsfile.
>>>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>>>> \%redirects);
>>>>>>>> +
>>>>>>>> +               # Check if we should edit an existing entry and
>>>>>>>> got an ID.
>>>>>>>> +               if (($settings{'ACTION'} eq
>>>>>>>> $Lang::tr{'update'}) && ($settings{'ID'})) {
>>>>>>>> +                       # Assin the provided id.
>>>>>>>> +                       $id = $settings{'ID'};
>>>>>>>> +
>>>>>>>> +                       # Undef the given ID.
>>>>>>>> +                       undef($settings{'ID'});
>>>>>>>> +
>>>>>>>> +                       # Grab the configured status of the
>>>>>>>> corresponding entry.
>>>>>>>> +                       $status = $redirects{$id}[4];
>>>>>>>> +               } else {
>>>>>>>> +                       # Each newly added entry automatically
>>>>>>>> should be enabled.
>>>>>>>> +                       $status = "enabled";
>>>>>>>> +
>>>>>>>> +                       # Generate the ID for the new entry.
>>>>>>>> +                       #
>>>>>>>> +                       # Sort the keys by their ID and store
>>>>>>>> them in an array.
>>>>>>>> +                       my @keys = sort { $a <=> $b } keys
>>>>>>>> %redirects;
>>>>>>>> +
>>>>>>>> +                       # Reverse the key array.
>>>>>>>> +                       my @reversed = reverse(@keys);
>>>>>>>> +
>>>>>>>> +                       # Obtain the last used id.
>>>>>>>> +                       my $last_id = @reversed[0];
>>>>>>>> +
>>>>>>>> +                       # Increase the last id by one and use
>>>>>>>> it as id for the new entry.
>>>>>>>> +                       $id = ++$last_id;
>>>>>>>> +               }
>>>>>>>> +
>>>>>>>> +               # Add/Modify the entry to/in the redirects
>>>>>>>> hash.
>>>>>>>> +               $redirects{$id} = ["$new_entry_interface",
>>>>>>>> "$new_entry_protocol", "$new_entry_port",
>>>>>>>> "$new_entry_address","$status", "$new_entry_remark"];
>>>>>>>> +
>>>>>>>> +               # Write the changed redirects hash to the
>>>>>>>> redirects file.
>>>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>>>> \%redirects);
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +# Toggle Enabled/Disabled for an existing entry on the
>>>>>>>> redirects list.
>>>>>>>> +
>>>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable
>>>>>>>> disable'}) {
>>>>>>>> +       my %redirects = ();
>>>>>>>> +
>>>>>>>> +       # Only go further, if an ID has been passed.
>>>>>>>> +       if ($settings{'ID'}) {
>>>>>>>> +               # Assign the given ID.
>>>>>>>> +               my $id = $settings{'ID'};
>>>>>>>> +
>>>>>>>> +               # Undef the given ID.
>>>>>>>> +               undef($settings{'ID'});
>>>>>>>> +
>>>>>>>> +               # Read-in ignoredfile.
>>>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>>>> \%redirects);
>>>>>>>> +
>>>>>>>> +               # Grab the configured status of the
>>>>>>>> corresponding entry.
>>>>>>>> +               my $status = $redirects{$id}[4];
>>>>>>>> +
>>>>>>>> +               # Switch the status.
>>>>>>>> +               if ($status eq "disabled") {
>>>>>>>> +                       $status = "enabled";
>>>>>>>> +               } else {
>>>>>>>> +                       $status = "disabled";
>>>>>>>> +               }
>>>>>>>> +
>>>>>>>> +               # Modify the status of the existing entry.
>>>>>>>> +               $redirects{$id} = ["$redirects{$id}[0]",
>>>>>>>> "$redirects{$id}[1]", "$redirects{$id}[2]",
>>>>>>>> "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
>>>>>>>> +
>>>>>>>> +               # Write the changed ignored hash to the
>>>>>>>> redirects file.
>>>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>>>> \%redirects);
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +# Remove entry from redirects list.
>>>>>>>> +
>>>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
>>>>>>>> +       my %redirects = ();
>>>>>>>> +
>>>>>>>> +       # Read-in redirectsfile.
>>>>>>>> +       &General::readhasharray($redirectsfile, \%redirects);
>>>>>>>> +
>>>>>>>> +       # move data on key up
>>>>>>>> +       foreach my $key (sort keys %redirects) {
>>>>>>>> +               if ($key >= $settings{'ID'}) {
>>>>>>>> +                       my $next = $key + 1;
>>>>>>>> +                       if (exists $redirects{$next}) {
>>>>>>>> +                               foreach my $i (0 ..
>>>>>>>> $#{$redirects{$next}}) { $redirects{$key}[$i] =
>>>>>>>> $redirects{$next}[$i]; }
>>>>>>>> +                       }
>>>>>>>> +               }
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       my $last_key = (sort {$a <=> $b} keys %redirects)[-1];
>>>>>>>> +       delete $redirects{$last_key};
>>>>>>>> +
>>>>>>>> +       # Undef the given ID.
>>>>>>>> +       undef($settings{'ID'});
>>>>>>>> +
>>>>>>>> +       # Write the changed redirects hash to file.
>>>>>>>> +       &General::writehasharray($redirectsfile, \%redirects);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +# Load settings from file
>>>>>>>> +&General::readhash($settingsfile, \%settings);
>>>>>>>> +&General::readhasharray($redirectsfile, \%redirects);
>>>>>>>> +
>>>>>>>> +# Call functions to generate whole page.
>>>>>>>> +&Header::openpage($Lang::tr{'portredir port redirections'}, 1,
>>>>>>>> '');
>>>>>>>> +&Header::openbigbox('100%', 'left', '', $errormessage);
>>>>>>>> +
>>>>>>>> +if ($errormessage) {
>>>>>>>> +        &Header::openbox('100%', 'left', $Lang::tr{'warning
>>>>>>>> messages'});
>>>>>>>> +        print "<font color='red'>$errormessage&nbsp;</font>";
>>>>>>>> +        &Header::closebox();
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_ADDON'}
>>>>>>>> } = "checked='checked'";
>>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_GREEN'}
>>>>>>>> } = "checked='checked'";
>>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BLUE'}}
>>>>>>>> = "checked='checked'";
>>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_ORANGE
>>>>>>>> '}} = "checked='checked'";
>>>>>>>> +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}} =
>>>>>>>> "checked='checked'";
>>>>>>>> +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}} =
>>>>>>>> "checked='checked'";
>>>>>>>> +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}} =
>>>>>>>> "checked='checked'";
>>>>>>>> +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}} =
>>>>>>>> "checked='checked'";
>>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE'}} =
>>>>>>>> "checked='checked'";
>>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE'}} =
>>>>>>>> "checked='checked'";
>>>>>>>> +
>>>>>>>> +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTRY_INTE
>>>>>>>> RFACE'}} = 'selected';
>>>>>>>> +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY_PROTO
>>>>>>>> COL'}} = 'selected';
>>>>>>>> +
>>>>>>>> +&showMainBox();
>>>>>>>> +&showRedirectsBox();
>>>>>>>> +
>>>>>>>> +&Header::closebigbox();
>>>>>>>> +&Header::closepage();
>>>>>>>> +
>>>>>>>> +# Function to show main settings and options.
>>>>>>>> +sub showMainBox() {
>>>>>>>> +
>>>>>>>> +       &Header::openbox('100%', 'center',
>>>>>>>> "$Lang::tr{'settings'}");
>>>>>>>> +       print "<form method='post'
>>>>>>>> action='$ENV{'SCRIPT_NAME'}'>";
>>>>>>>> +
>>>>>>>> +print <<END;
>>>>>>>> +<table width='80%' cellspacing='0' border='0'>
>>>>>>>> +       <tr><td colspan='2' class='base'
>>>>>>>> bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir common
>>>>>>>> settings'}</b></td></tr
>>>>>>>> +       <tr>
>>>>>>>> +               <td width='25%'
>>>>>>>> class='base'>$Lang::tr{'portredir enable addon'}:</td>
>>>>>>>> +               <td><input type='checkbox'
>>>>>>>> name='REDIR_ENABLE_ADDON' $checked{'REDIR_ENABLE_ADDON'}{'on'}
>>>>>>>> /></td>
>>>>>>>> +       </tr>
>>>>>>>> +       <tr><td colspan='2'></td></tr>
>>>>>>>> +       <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>>>> +END
>>>>>>>> +
>>>>>>>> +       # create html table with header line 1
>>>>>>>> +       print "<table width='80%' cellspacing='0'
>>>>>>>> border='0'><tr>";
>>>>>>>> +       print "<th class='base' width='40%'
>>>>>>>> align='left'><b>$Lang::tr{'portredir fw for interface'}</th>";
>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<th
>>>>>>>> class='base' width='10%'><b><font
>>>>>>>> color=green>$Lang::tr{'green'}</font></th>";
>>>>>>>> +               } else {                 print "<th
>>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<th
>>>>>>>> class='base' width='10%'><b><font
>>>>>>>> color=blue>$Lang::tr{'blue'}</font></th>";
>>>>>>>> +               } else {                 print "<th
>>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<th
>>>>>>>> class='base' width='10%'><b><font
>>>>>>>> color=orange>$Lang::tr{'orange'}</font></th>";
>>>>>>>> +               } else {                 print "<th
>>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>>> +
>>>>>>>> +       # the empty right row
>>>>>>>> +       print "<th class='base'
>>>>>>>> width='30%'><td></td></th></tr>";
>>>>>>>> +
>>>>>>>> +       # line 2
>>>>>>>> +       print "<tr><td>$Lang::tr{'portredir force local
>>>>>>>> dns'}</td>";
>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_DNS_GREEN'
>>>>>>>> $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_DNS_BLUE' $checked{'REDIR_DNS_BLUE'}{'on'}></td>";}
>>>>>>>> else { print "<td></td>";}
>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_DNS_ORANGE'
>>>>>>>> $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +
>>>>>>>> +       # line 3
>>>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir force local
>>>>>>>> ntp'}</td>";
>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_NTP_GREEN'
>>>>>>>> $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_NTP_BLUE' $checked{'REDIR_NTP_BLUE'}{'on'}></td>";}
>>>>>>>> else { print "<td></td>";}
>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_NTP_ORANGE'
>>>>>>>> $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +
>>>>>>>> +       # line 4
>>>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir enable user
>>>>>>>> redirections'}</td>";
>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_CUSTOM_GREEN'
>>>>>>>> $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_CUSTOM_BLUE'
>>>>>>>> $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_CUSTOM_ORANGE'
>>>>>>>> $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +
>>>>>>>> +       print <<END;
>>>>>>>> +       </tr></table>
>>>>>>>> +       <table width='80%' cellspacing='0' border='0'>
>>>>>>>> +               <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>>>> +               <tr><td align='left'><b>$Lang::tr{'portredir
>>>>>>>> save to activate'}</b></td><td width='5%' align='center'><input
>>>>>>>> type='submit' name='ACTION' value='  $Lang::tr{'save'} 
>>>>>>>> '></td></tr>
>>>>>>>> +       </table></form>
>>>>>>>> +END
>>>>>>>> +
>>>>>>>> +&Header::closebox();
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +# Function to show elements of the redirects file and allow to
>>>>>>>> add or remove single members of it.
>>>>>>>> +sub showRedirectsBox() {
>>>>>>>> +        &Header::openbox('100%', 'center',
>>>>>>>> "$Lang::tr{'portredir custom redirections'}");
>>>>>>>> +
>>>>>>>> +       print <<END;
>>>>>>>> +               <table width='80%' cellspacing='1' border='0'>
>>>>>>>> +                       <tr>
>>>>>>>> +                               <td class='base'
>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>> align='center'><b>$Lang::tr{'interface'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>> align='center'><b>$Lang::tr{'protocol'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>> align='center'><b>$Lang::tr{'port'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'ip
>>>>>>>> address'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>> align='center'><b>$Lang::tr{'remark'}</b></td>
>>>>>>>> +                               <td class='base' colspan='3'
>>>>>>>> bgcolor='$color{'color20'}'></td>
>>>>>>>> +                       </tr>
>>>>>>>> +END
>>>>>>>> +                       # Check if some rules have been added
>>>>>>>> to be redirects.
>>>>>>>> +                       if (keys (%redirects)) {
>>>>>>>> +                               my $col = "";
>>>>>>>> +
>>>>>>>> +                               # List all entries of the hash.
>>>>>>>> +                               foreach my $key (sort keys
>>>>>>>> %redirects){
>>>>>>>> +
>>>>>>>> +                                       # Assign data array
>>>>>>>> positions to some nice variable names.
>>>>>>>> +                                       my $interface =
>>>>>>>> $redirects{$key}[0];
>>>>>>>> +                                       my $protocol =
>>>>>>>> $redirects{$key}[1];
>>>>>>>> +                                       my $port  =
>>>>>>>> $redirects{$key}[2];
>>>>>>>> +                                       my $address =
>>>>>>>> $redirects{$key}[3];
>>>>>>>> +                                       my $status  =
>>>>>>>> $redirects{$key}[4];
>>>>>>>> +                                       my $remark  =
>>>>>>>> $redirects{$key}[5];
>>>>>>>> +
>>>>>>>> +                                       # Check if the key (id)
>>>>>>>> number is even or not.
>>>>>>>> +                                       if ($settings{'ID'} eq
>>>>>>>> $key) {
>>>>>>>> +                                               $col="bgcolor='
>>>>>>>> ${Header::colouryellow}'";
>>>>>>>> +                                       } elsif ($key % 2) {
>>>>>>>> +                                               $col="bgcolor='
>>>>>>>> $color{'color22'}'";
>>>>>>>> +                                       } else {
>>>>>>>> +                                               $col="bgcolor='
>>>>>>>> $color{'color20'}'";
>>>>>>>> +                                       }
>>>>>>>> +
>>>>>>>> +                                       # Choose icon for the
>>>>>>>> checkbox.
>>>>>>>> +                                       my $gif;
>>>>>>>> +                                       my $gdesc;
>>>>>>>> +
>>>>>>>> +                                       # Check if the status
>>>>>>>> is enabled and select the correct image and description.
>>>>>>>> +                                       if ($status eq
>>>>>>>> 'enabled' ) {
>>>>>>>> +                                               $gif =
>>>>>>>> 'on.gif';
>>>>>>>> +                                               $gdesc =
>>>>>>>> $Lang::tr{'click to disable'};
>>>>>>>> +                                       } else {
>>>>>>>> +                                               $gif =
>>>>>>>> 'off.gif';
>>>>>>>> +                                               $gdesc =
>>>>>>>> $Lang::tr{'click to enable'};
>>>>>>>> +                                       }
>>>>>>>> +
>>>>>>>> +                                       print <<END;
>>>>>>>> +                                       <tr>
>>>>>>>> +                                               <td width='15%'
>>>>>>>> class='base' align='center' $col><b><font
>>>>>>>> color=$interface>$Lang::tr{$interface}</font></b></td>
>>>>>>>> +                                               <td width='10%'
>>>>>>>> class='base' align='center' $col>$protocol</td>
>>>>>>>> +                                               <td width='10%'
>>>>>>>> class='base' align='center' $col>$port</td>
>>>>>>>> +                                               <td width='15%'
>>>>>>>> class='base' align='center' $col>&nbsp;$address</td>
>>>>>>>> +                                               <td width='40%'
>>>>>>>> class='base' align='center' $col>&nbsp;$remark</td>
>>>>>>>> +                                               <td
>>>>>>>> align='center' $col>
>>>>>>>> +                                                       <form
>>>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>> +                                                              
>>>>>>>> <input type='hidden' name='ACTION' value='$Lang::tr{'toggle
>>>>>>>> enable disable'}' />
>>>>>>>> +                                                              
>>>>>>>> <input type='image' name='$Lang::tr{'toggle enable disable'}'
>>>>>>>> src='/images/$gif' alt='$gdesc' title='$gdesc' />
>>>>>>>> +                                                              
>>>>>>>> <input type='hidden' name='ID' value='$key' />
>>>>>>>> +                                                       </form>
>>>>>>>> +                                               </td>
>>>>>>>> +                                               <td
>>>>>>>> align='center' $col>
>>>>>>>> +                                                       <form
>>>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>> +                                                              
>>>>>>>> <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}'
>>>>>>>> />
>>>>>>>> +                                                              
>>>>>>>> <input type='image' name='$Lang::tr{'edit'}'
>>>>>>>> src='/images/edit.gif' alt='$Lang::tr{'edit'}'
>>>>>>>> title='$Lang::tr{'edit'}' />
>>>>>>>> +                                                              
>>>>>>>> <input type='hidden' name='ID' value='$key' />
>>>>>>>> +                                                       </form>
>>>>>>>> +                                               </td>
>>>>>>>> +                                               <td
>>>>>>>> align='center' $col>
>>>>>>>> +                                                       <form
>>>>>>>> method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>> +                                                              
>>>>>>>> <input type='image' name='$Lang::tr{'remove'}'
>>>>>>>> src='/images/delete.gif' title='$Lang::tr{'remove'}'
>>>>>>>> alt='$Lang::tr{'remove'}'>
>>>>>>>> +                                                              
>>>>>>>> <input type='hidden' name='ID' value='$key'>
>>>>>>>> +                                                              
>>>>>>>> <input type='hidden' name='ACTION'
>>>>>>>> value='$Lang::tr{'remove'}'>
>>>>>>>> +                                                       </form>
>>>>>>>> +                                               </td>
>>>>>>>> +                                       </tr>
>>>>>>>> +END
>>>>>>>> +                               }
>>>>>>>> +                       } else {
>>>>>>>> +                               # Print notice that currently
>>>>>>>> no ports are redirected.
>>>>>>>> +                               print "<tr>\n";
>>>>>>>> +                               print "<td class='base'
>>>>>>>> colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
>>>>>>>> +                               print "</tr>\n";
>>>>>>>> +                       }
>>>>>>>> +
>>>>>>>> +               print "</table>\n";
>>>>>>>> +
>>>>>>>> +       # Section to add new elements or edit existing ones.
>>>>>>>> +       print <<END;
>>>>>>>> +       <br>
>>>>>>>> +       <hr>
>>>>>>>> +       <br>
>>>>>>>> +       <div align='center'>
>>>>>>>> +               <table width='100%' cellspacing='0' border='0'>
>>>>>>>> +END
>>>>>>>> +
>>>>>>>> +       # Assign correct headline and button text.
>>>>>>>> +       my $buttontext;
>>>>>>>> +       my $entry_interface;
>>>>>>>> +       my $entry_protocol;
>>>>>>>> +       my $entry_port;
>>>>>>>> +       my $entry_address;
>>>>>>>> +       my $entry_remark;
>>>>>>>> +
>>>>>>>> +       # Check if an ID (key) has been given, in this case an
>>>>>>>> existing entry should be edited.
>>>>>>>> +       if ($settings{'ID'} ne '') {
>>>>>>>> +               $buttontext = $Lang::tr{'update'};
>>>>>>>> +               print "<tr><td class='boldbase'
>>>>>>>> colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
>>>>>>>> +
>>>>>>>> +               # Grab address and remark for the given key.
>>>>>>>> +               $entry_interface =
>>>>>>>> $redirects{$settings{'ID'}}[0];
>>>>>>>> +               $entry_protocol =
>>>>>>>> $redirects{$settings{'ID'}}[1];
>>>>>>>> +               $entry_port = $redirects{$settings{'ID'}}[2];
>>>>>>>> +               $entry_address =
>>>>>>>> $redirects{$settings{'ID'}}[3];
>>>>>>>> +               $entry_remark = $redirects{$settings{'ID'}}[5];
>>>>>>>> +
>>>>>>>> +       } else {
>>>>>>>> +               $buttontext = $Lang::tr{'add'};
>>>>>>>> +               print "<tr><td class='boldbase'
>>>>>>>> colspan='11'><b>$Lang::tr{'dnsforward add a new
>>>>>>>> entry'}</b></td></tr>\n";
>>>>>>>> +               print "<tr><td>&nbsp</td></tr>\n";
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       print <<END;
>>>>>>>> +                       <tr>
>>>>>>>> +                               <td class='base' width='1%' 
>>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>>> +                               <td class='base' width='15%'
>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>> align='left'><b>$Lang::tr{'interface'}</b></td>
>>>>>>>> +                               <td class='base' width='10%'
>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>> align='left'><b>$Lang::tr{'protocol'}</b></td>
>>>>>>>> +                               <td class='base' width='10%'
>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>> align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
>>>>>>>> +                               <td class='base' width='13%'
>>>>>>>> bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'ip
>>>>>>>> address'}</b></td>
>>>>>>>> +                               <td class='base' width='30%'
>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>> align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
>>>>>>>> +                               <td class='base' width='15%'
>>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>>> +                               <td class='base' width='1%' 
>>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>>> +                       </tr>
>>>>>>>> +
>>>>>>>> +                       <form method='post'
>>>>>>>> action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>> +                       <input type='hidden' name='ID'
>>>>>>>> value='$settings{'ID'}'>
>>>>>>>> +                       <tr>
>>>>>>>> +                               <td class='base'></td>
>>>>>>>> +                               <td><select style='width:90px;'
>>>>>>>> id='interface' name='REDIR_ENTRY_INTERFACE'>
>>>>>>>> +END
>>>>>>>> +                               if ($netsettings{'GREEN_DEV'})
>>>>>>>> {
>>>>>>>> +                                       if ($entry_interface eq
>>>>>>>> "green") {
>>>>>>>> +                                               print "<option
>>>>>>>> value='green' selected='selected'
>>>>>>>> {'green'}>$Lang::tr{'green'}</option>";
>>>>>>>> +                                       } else { print "<option
>>>>>>>> value='green' {'green'}>$Lang::tr{'green'}</option>";}
>>>>>>>> +                               }
>>>>>>>> +                               if ($netsettings{'BLUE_DEV'}) {
>>>>>>>> +                                       if ($entry_interface eq
>>>>>>>> "blue") { 
>>>>>>>> +                                               print "<option
>>>>>>>> value='blue' selected='selected'
>>>>>>>> {'blue'}>$Lang::tr{'blue'}</option>";
>>>>>>>> +                                       } else { print "<option
>>>>>>>> value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
>>>>>>>> +                               }
>>>>>>>> +                               if ($netsettings{'ORANGE_DEV'})
>>>>>>>> {
>>>>>>>> +                                       if ($entry_interface eq
>>>>>>>> "orange") { 
>>>>>>>> +                                               print "<option
>>>>>>>> value='orange' selected='selected'
>>>>>>>> {'orange'}>$Lang::tr{'orange'}</option>";
>>>>>>>> +                                       } else { print "<option
>>>>>>>> value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
>>>>>>>> +                               }
>>>>>>>> +
>>>>>>>> +                       print "</select><td><select
>>>>>>>> style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
>>>>>>>> +                       if ((!$entry_protocol) ||
>>>>>>>> ($entry_protocol eq "tcp")) {
>>>>>>>> +                               print "<option
>>>>>>>> selected='selected' id='protocol' value='tcp'
>>>>>>>> {'tcp'}>tcp</option>";
>>>>>>>> +                               print "<option value='udp'
>>>>>>>> {'udp'}>udp</option>";
>>>>>>>> +                       } elsif ($entry_protocol eq "udp") {
>>>>>>>> +                               print "<option value='tcp'
>>>>>>>> {'tcp'}>tcp</option>";
>>>>>>>> +                               print "<option
>>>>>>>> selected='selected' value='udp' {'udp'}>udp</option>";
>>>>>>>> +                       }
>>>>>>>> +       print <<END;
>>>>>>>> +                               </select></td>
>>>>>>>> +                               <td><input type='text'
>>>>>>>> name='REDIR_ENTRY_PORT'    value='$entry_port'   
>>>>>>>> size='4'></td>
>>>>>>>> +                               <td><input type='text'
>>>>>>>> name='REDIR_ENTRY_ADDRESS' value='$entry_address'
>>>>>>>> size='14'></td>
>>>>>>>> +                               <td><input type='text'
>>>>>>>> name='REDIR_ENTRY_REMARK'  value='$entry_remark' 
>>>>>>>> size='35'></td>
>>>>>>>> +                               <td width='10%'
>>>>>>>> align='center'><input type='submit' name='ACTION' value=' 
>>>>>>>> $buttontext  '></td>
>>>>>>>> +                               <td class='base'></td>
>>>>>>>> +                       </tr>
>>>>>>>> +                       </form>
>>>>>>>> +               </table>
>>>>>>>> +       </div>
>>>>>>>> +END
>>>>>>>> +       &Header::closebox();
>>>>>>>> +}
>>>>>>>> diff --git a/config/rootfiles/common/misc-progs
>>>>>>>> b/config/rootfiles/common/misc-progs
>>>>>>>> index d6594b3f8..fbad2af8b 100644
>>>>>>>> --- a/config/rootfiles/common/misc-progs
>>>>>>>> +++ b/config/rootfiles/common/misc-progs
>>>>>>>> @@ -17,6 +17,7 @@ usr/local/bin/logwatch
>>>>>>>> #usr/local/bin/mpfirectrl
>>>>>>>> usr/local/bin/openvpnctrl
>>>>>>>> usr/local/bin/pakfire
>>>>>>>> +#usr/local/bin/portredirctrl
>>>>>>>> usr/local/bin/qosctrl
>>>>>>>> usr/local/bin/rebuildhosts
>>>>>>>> usr/local/bin/rebuildroutes
>>>>>>>> diff --git a/config/rootfiles/packages/portredir
>>>>>>>> b/config/rootfiles/packages/portredir
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..4b4ba8366
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/rootfiles/packages/portredir
>>>>>>>> @@ -0,0 +1,11 @@
>>>>>>>> +etc/rc.d/init.d/portredir
>>>>>>>> +etc/rc.d/rc0.d/K77portredir
>>>>>>>> +etc/rc.d/rc3.d/S23portredir
>>>>>>>> +etc/rc.d/rc6.d/K77portredir
>>>>>>>> +srv/web/ipfire/cgi-bin/portredir.cgi
>>>>>>>> +usr/local/bin/portredirctrl
>>>>>>>> +var/ipfire/addon-lang/portredir.de.pl
>>>>>>>> +var/ipfire/addon-lang/portredir.en.pl
>>>>>>>> +var/ipfire/backup/addons/includes/portredir
>>>>>>>> +var/ipfire/menu.d/EX-portredir.menu
>>>>>>>> +var/ipfire/portredir
>>>>>>>> diff --git a/lfs/portredir b/lfs/portredir
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..a4911f71f
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/lfs/portredir
>>>>>>>> @@ -0,0 +1,85 @@
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +# IPFire.org - A linux based
>>>>>>>> firewall                                         #
>>>>>>>> +# Copyright (C) 2007-2021  IPFire Team 
>>>>>>>> <info@ipfire.org>                     #
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +# This program is free software: you can redistribute it
>>>>>>>> and/or modify        #
>>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>>> published by        #
>>>>>>>> +# the Free Software Foundation, either version 3 of the
>>>>>>>> License, or           #
>>>>>>>> +# (at your option) any later
>>>>>>>> version.                                         #
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +# This program is distributed in the hope that it will be
>>>>>>>> useful,             #
>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>>> of              #
>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>>> the               #
>>>>>>>> +# GNU General Public License for more
>>>>>>>> details.                                #
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>>> License           #
>>>>>>>> +# along with this program.  If not, see <
>>>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +# Definitions
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +
>>>>>>>> +include Config
>>>>>>>> +
>>>>>>>> +VER        = 1.0
>>>>>>>> +
>>>>>>>> +THISAPP    = portredir-$(VER)
>>>>>>>> +DIR_APP    = $(DIR_SRC)/$(THISAPP)
>>>>>>>> +TARGET     = $(DIR_INFO)/$(THISAPP)
>>>>>>>> +PROG       = portredir
>>>>>>>> +PAK_VER    = 1
>>>>>>>> +
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +# Top-level Rules
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +
>>>>>>>> +install : $(TARGET)
>>>>>>>> +
>>>>>>>> +check :
>>>>>>>> +
>>>>>>>> +download :
>>>>>>>> +
>>>>>>>> +md5 :
>>>>>>>> +
>>>>>>>> +dist: 
>>>>>>>> +       @$(PAK)
>>>>>>>> +
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +# Installation Details
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +
>>>>>>>> +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
>>>>>>>> +       @$(PREBUILD)
>>>>>>>> +       @rm -rf $(DIR_APP) && cd $(DIR_SRC)
>>>>>>>> +
>>>>>>>> +       #install cgi 
>>>>>>>> +       install -v -m 755 $(DIR_CONF)/portredir/portredir.cgi
>>>>>>>> /srv/web/ipfire/cgi-bin/
>>>>>>>> +
>>>>>>>> +       #create configuration dir 
>>>>>>>> +       -mkdir -pv /var/ipfire/portredir/
>>>>>>>> +       chown -R nobody:nobody /var/ipfire/portredir/
>>>>>>>> +
>>>>>>>> +       # Install include file for backup
>>>>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/portredir-
>>>>>>>> backup /var/ipfire/backup/addons/includes/portredir
>>>>>>>> +
>>>>>>>> +       # Install menu file
>>>>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/EX-
>>>>>>>> portredir.menu /var/ipfire/menu.d/
>>>>>>>> +       chown nobody:nobody /var/ipfire/menu.d/EX-
>>>>>>>> portredir.menu
>>>>>>>> +
>>>>>>>> +       # Install addon-specific language-files
>>>>>>>> +       install -v -m 644
>>>>>>>> $(DIR_CONF)/portredir/lang/portredir.*.pl /var/ipfire/addon-
>>>>>>>> lang/
>>>>>>>> +
>>>>>>>> +       #install initscripts
>>>>>>>> +       $(call INSTALL_INITSCRIPT,portredir)
>>>>>>>> +
>>>>>>>> +       # Create symlinks for runlevel interaction.
>>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc3.d/S23portredir
>>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc0.d/K77portredir
>>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc6.d/K77portredir
>>>>>>>> +
>>>>>>>> +       @rm -rf $(DIR_APP)
>>>>>>>> +       @$(POSTBUILD)
>>>>>>>> +
>>>>>>>> diff --git a/make.sh b/make.sh
>>>>>>>> index fc03ebcd5..ab9fe881a 100755
>>>>>>>> --- a/make.sh
>>>>>>>> +++ b/make.sh
>>>>>>>> @@ -1623,6 +1623,7 @@ buildipfire() {
>>>>>>>> lfsmake2 socat
>>>>>>>> lfsmake2 libcdada
>>>>>>>> lfsmake2 pmacct
>>>>>>>> +  lfsmake2 portredir
>>>>>>>> }
>>>>>>>> 
>>>>>>>> buildinstaller() {
>>>>>>>> diff --git a/src/initscripts/packages/portredir
>>>>>>>> b/src/initscripts/packages/portredir
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..cc57fb9cc
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/src/initscripts/packages/portredir
>>>>>>>> @@ -0,0 +1,191 @@
>>>>>>>> +#!/bin/sh
>>>>>>>> +##############################################################
>>>>>>>> ##########
>>>>>>>> +# Begin $rc_base/init.d/portredir
>>>>>>>> +#
>>>>>>>> +# Description : portredir init script for DNS/NTP and custom 
>>>>>>>> +#              port redirection rules
>>>>>>>> +#
>>>>>>>> +##############################################################
>>>>>>>> ##########
>>>>>>>> +
>>>>>>>> +. /etc/sysconfig/rc
>>>>>>>> +. ${rc_functions}
>>>>>>>> +
>>>>>>>> +IPT="/sbin/iptables";
>>>>>>>> +parent_chain="PREROUTING";
>>>>>>>> +chain="PORT_REDIRECT";
>>>>>>>> +
>>>>>>>> +confdir="/var/ipfire/portredir";
>>>>>>>> +settingsfile="${confdir}/settings";
>>>>>>>> +redirectsfile="${confdir}/redirects";
>>>>>>>> +SYSLOG="NO";
>>>>>>>> +VERBOSE="NO";
>>>>>>>> +
>>>>>>>> +eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings);
>>>>>>>> +eval $(/usr/local/bin/readhash ${settingsfile});
>>>>>>>> +
>>>>>>>> +logtext() {
>>>>>>>> +       if [ "${SYSLOG}" = "YES" ]; then logger -t "portredir"
>>>>>>>> ${1}; fi;
>>>>>>>> +       if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
>>>>>>>> +
>>>>>>>> +create_chain() {
>>>>>>>> +
>>>>>>>> +       local line=$(${IPT} -t nat -L ${parent_chain} --line-
>>>>>>>> numbers |grep "SQUID" |awk '{printf($1)}');
>>>>>>>> +
>>>>>>>> +       if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z
>>>>>>>> "${REDIR_ENABLE_ADDON}" ]]; then
>>>>>>>> +               logtext "addon not enabled in web
>>>>>>>> interface...";
>>>>>>>> +               echo "Portredir addon not enabled in web
>>>>>>>> interface...";
>>>>>>>> +               exit 0;
>>>>>>>> +       fi;
>>>>>>>> +
>>>>>>>> +       if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>>>>> ${chain})" ]; then
>>>>>>>> +               ${IPT} -t nat -N ${chain};
>>>>>>>> +
>>>>>>>> +               if [ ! -z "${line}" ]; then
>>>>>>>> +                       logtext "create chain ${chain} and link
>>>>>>>> in ${parent_chain} at position ${line}...";
>>>>>>>> +                       ${IPT} -t nat -I ${parent_chain}
>>>>>>>> ${line} -j ${chain};
>>>>>>>> +               else
>>>>>>>> +                       logtext "create chain ${chain} and link
>>>>>>>> in ${parent_chain} at last position...";
>>>>>>>> +                       ${IPT} -t nat -A ${parent_chain} -j
>>>>>>>> ${chain};
>>>>>>>> +               fi
>>>>>>>> +       else
>>>>>>>> +               return 1;
>>>>>>>> +       fi;
>>>>>>>> +       return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +remove_chain() {
>>>>>>>> +       if [ ! -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>>>>> ${chain})" ]; then
>>>>>>>> +               logtext "remove chain ${chain} and link in
>>>>>>>> ${parent_chain} from system...";
>>>>>>>> +               ${IPT} -t nat -D "${parent_chain}" -j ${chain};
>>>>>>>> +               ${IPT} -t nat -F ${chain};
>>>>>>>> +               ${IPT} -t nat -X ${chain};
>>>>>>>> +       else
>>>>>>>> +               return 1;
>>>>>>>> +       fi;
>>>>>>>> +       return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +activate_custom_redirections() {
>>>>>>>> +       
>>>>>>>> +       local array=();
>>>>>>>> +       local redirects=();
>>>>>>>> +       local i;
>>>>>>>> +       index=();
>>>>>>>> +       iface=();
>>>>>>>> +       protocol=();
>>>>>>>> +       port=();
>>>>>>>> +       targetip=();
>>>>>>>> +       enabled=();
>>>>>>>> +
>>>>>>>> +       IFS=$'\n' read -d '' -ra redirects < ${redirectsfile};
>>>>>>>> +
>>>>>>>> +       for i in "${!redirects[@]}"
>>>>>>>> +       do
>>>>>>>> +               IFS=$',' read -ra array <<< ${redirects[i]};
>>>>>>>> +               index[i]=${array[0]};
>>>>>>>> +               iface[i]=${array[1]};
>>>>>>>> +               protocol[i]=${array[2]};
>>>>>>>> +               port[i]=${array[3]};
>>>>>>>> +               targetip[i]=${array[4]};
>>>>>>>> +               enabled[i]=${array[5]};
>>>>>>>> +       done
>>>>>>>> +
>>>>>>>> +       for i in "${!index[@]}"
>>>>>>>> +       do
>>>>>>>> +               if [[ ! -z "${GREEN_DEV}" &&  "${iface[i]}" =
>>>>>>>> "green" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>>> +
>>>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>>>> ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>>>> ${port[i]} ";
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>>> +               fi
>>>>>>>> +               if [[ ! -z "${BLUE_DEV}" &&  "${iface[i]}" =
>>>>>>>> "blue" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>>>> ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>>>> ${port[i]} ";
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>>> +               fi
>>>>>>>> +               if [[ ! -z "${ORANGE_DEV}" &&  "${iface[i]}" =
>>>>>>>> "orange" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>>>> ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>>>> ${port[i]} ";
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>>> +               fi
>>>>>>>> +       done
>>>>>>>> +       unset array redirects i index iface protocol port
>>>>>>>> targetip enabled;
>>>>>>>> +       return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +activate_redirections() {
>>>>>>>> +
>>>>>>>> +       if ! create_chain; then return 1; fi;
>>>>>>>> +       
>>>>>>>> +       # Force DNS REDIRECTs on GREEN (udp, tcp, 53)
>>>>>>>> +       if [[ "${REDIR_DNS_GREEN}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>>>> udp -m udp --dport domain -j REDIRECT;
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>>>> ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>>>> tcp -m tcp --dport domain -j REDIRECT;
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       # Force DNS REDIRECTs on BLUE (udp, tcp, 53)
>>>>>>>> +       if [[ "${REDIR_DNS_BLUE}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>>>>> -m udp --dport domain -j REDIRECT
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>>>> ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p tcp
>>>>>>>> -m tcp --dport domain -j REDIRECT
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       # Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
>>>>>>>> +       if [[ "${REDIR_DNS_ORANGE}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>>>> udp -m udp --dport domain -j REDIRECT
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>>>> ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>>>> tcp -m tcp --dport domain -j REDIRECT
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       # Force NTP REDIRECTs on GREEN (udp, 123)
>>>>>>>> +       if [[ "${REDIR_NTP_GREEN}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       # Force NTP REDIRECTs on BLUE (udp, 123)
>>>>>>>> +       if [[ "${REDIR_NTP_BLUE}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>>>>> -m udp --dport ntp -j REDIRECT
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       # Force NTP REDIRECTs on ORANGE (udp, 123)
>>>>>>>> +       if [[ "${REDIR_NTP_ORANGE}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       if ! activate_custom_redirections; then return 1; fi;
>>>>>>>> +
>>>>>>>> +       return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +case "${1}" in
>>>>>>>> +       start)
>>>>>>>> +               boot_mesg "Loading port redirections..."
>>>>>>>> +               activate_redirections;
>>>>>>>> +               evaluate_retval;
>>>>>>>> +               ;;
>>>>>>>> +
>>>>>>>> +       stop)   
>>>>>>>> +               boot_mesg "Removing port redirections..."
>>>>>>>> +               remove_chain;
>>>>>>>> +               evaluate_retval;
>>>>>>>> +               ;;
>>>>>>>> +
>>>>>>>> +       restart)
>>>>>>>> +               ${0} stop
>>>>>>>> +               ${0} start
>>>>>>>> +               ;;
>>>>>>>> +
>>>>>>>> +       *)
>>>>>>>> +               echo "Usage: ${0} {start|stop|restart}"
>>>>>>>> +               exit 1
>>>>>>>> +               ;;
>>>>>>>> +esac
>>>>>>>> +
>>>>>>>> +# End $rc_base/init.d/portredir
>>>>>>>> diff --git a/src/misc-progs/Makefile b/src/misc-progs/Makefile
>>>>>>>> index 7c3ef7529..850f8fdcc 100644
>>>>>>>> --- a/src/misc-progs/Makefile
>>>>>>>> +++ b/src/misc-progs/Makefile
>>>>>>>> @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
>>>>>>>>        wirelessctrl getipstat qosctrl \
>>>>>>>>        redctrl syslogdctrl extrahdctrl sambactrl \
>>>>>>>>        smartctrl clamavctrl addonctrl pakfire mpfirectrl
>>>>>>>> wlanapctrl \
>>>>>>>> -       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>>>>> rebuildroutes \
>>>>>>>> +       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>>>>> rebuildroutes portredirctrl \
>>>>>>>>        getconntracktable wirelessclient torctrl ddnsctrl
>>>>>>>> unboundctrl \
>>>>>>>>        captivectrl
>>>>>>>> 
>>>>>>>> diff --git a/src/misc-progs/portredirctrl.c b/src/misc-
>>>>>>>> progs/portredirctrl.c
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..7897d711c
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/src/misc-progs/portredirctrl.c
>>>>>>>> @@ -0,0 +1,47 @@
>>>>>>>> +/* This file is part of the IPFire Firewall.
>>>>>>>> + *
>>>>>>>> + * This program is distributed under the terms of the GNU
>>>>>>>> General Public
>>>>>>>> + * Licence.  See the file COPYING for details.
>>>>>>>> + *
>>>>>>>> + */
>>>>>>>> +
>>>>>>>> +#include <stdlib.h>
>>>>>>>> +#include <stdio.h>
>>>>>>>> +#include <string.h>
>>>>>>>> +#include <unistd.h>
>>>>>>>> +#include <sys/types.h>
>>>>>>>> +#include <fcntl.h>
>>>>>>>> +#include "setuid.h"
>>>>>>>> +
>>>>>>>> +int main(int argc, char *argv[]) {
>>>>>>>> +       if (!(initsetuid()))
>>>>>>>> +               exit(1);
>>>>>>>> +
>>>>>>>> +       // Check what command is asked
>>>>>>>> +        if (argc < 2) {
>>>>>>>> +                fprintf(stderr, "\nNo argument
>>>>>>>> given.\n\nportredirctrl
>>>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>>>> +                exit(1);
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>> +        if (strcmp(argv[1], "start") == 0) {
>>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>>> start");
>>>>>>>> +        } else if (strcmp(argv[1], "stop") == 0) {
>>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>>> stop");
>>>>>>>> +        } else if (strcmp(argv[1], "restart") == 0) {
>>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>>> restart");
>>>>>>>> +       } else if (strcmp(argv[1], "enable") == 0) {
>>>>>>>> +               safe_system("touch
>>>>>>>> /var/ipfire/portredir/enable");
>>>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
>>>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
>>>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
>>>>>>>> +       } else if (strcmp(argv[1], "disable") == 0) {
>>>>>>>> +               safe_system("/etc/rc.d/init.d/portredir stop");
>>>>>>>> +               safe_system("unlink
>>>>>>>> /var/ipfire/portredir/enable");
>>>>>>>> +               safe_system("rm -rf /etc/rc.d/rc*.d/*portredir
>>>>>>>>> /dev/null 2>&1");
>>>>>>>> +        } else {
>>>>>>>> +                fprintf(stderr, "\nBad argument
>>>>>>>> given.\n\nportredirctrl
>>>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>>>> +                exit(1);
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>> +       return 0;
>>>>>>>> +}
>>>>>>>> diff --git a/src/paks/portredir/install.sh
>>>>>>>> b/src/paks/portredir/install.sh
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..9f69aeae2
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/src/paks/portredir/install.sh
>>>>>>>> @@ -0,0 +1,32 @@
>>>>>>>> +#!/bin/bash
>>>>>>>> +##############################################################
>>>>>>>> ##############
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# This file is part of the IPFire
>>>>>>>> Firewall.                                #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>>>> modify           #
>>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>>> published by     #
>>>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>>>> License, or        #
>>>>>>>> +# (at your option) any later
>>>>>>>> version.                                      #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>>>> useful,                #
>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>>> of           #
>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>>> the            #
>>>>>>>> +# GNU General Public License for more
>>>>>>>> details.                             #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>>> License        #
>>>>>>>> +# along with IPFire; if not, write to the Free
>>>>>>>> Software                    #
>>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>>>> 02111-1307 USA #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# Copyright (C) 2021 IPFire-Team
>>>>>>>> <info@ipfire.org>.                        #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +##############################################################
>>>>>>>> ##############
>>>>>>>> +#
>>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>>> +extract_files
>>>>>>>> +restore_backup ${NAME}
>>>>>>>> +
>>>>>>>> +/usr/local/bin/update-lang-cache
>>>>>>>> +
>>>>>>>> +chown root:nobody /usr/local/bin/portredirctrl
>>>>>>>> +chmod 4750 /usr/local/bin/portredirctrl
>>>>>>>> +chmod u+s /usr/local/bin/portredirctrl
>>>>>>>> diff --git a/src/paks/portredir/uninstall.sh
>>>>>>>> b/src/paks/portredir/uninstall.sh
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..df9270125
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/src/paks/portredir/uninstall.sh
>>>>>>>> @@ -0,0 +1,28 @@
>>>>>>>> +#!/bin/bash
>>>>>>>> +##############################################################
>>>>>>>> ##############
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# This file is part of the IPFire
>>>>>>>> Firewall.                                #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>>>> modify           #
>>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>>> published by     #
>>>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>>>> License, or        #
>>>>>>>> +# (at your option) any later
>>>>>>>> version.                                      #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>>>> useful,                #
>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>>> of           #
>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>>> the            #
>>>>>>>> +# GNU General Public License for more
>>>>>>>> details.                             #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>>> License        #
>>>>>>>> +# along with IPFire; if not, write to the Free
>>>>>>>> Software                    #
>>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>>>> 02111-1307 USA #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>>>> <info@ipfire.org>.                        #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +##############################################################
>>>>>>>> ##############
>>>>>>>> +#
>>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>>> +make_backup ${NAME}
>>>>>>>> +remove_files
>>>>>>>> +
>>>>>>>> +/usr/local/bin/update-lang-cache
>>>>>>>> diff --git a/src/paks/portredir/update.sh
>>>>>>>> b/src/paks/portredir/update.sh
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..89c40d0d7
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/src/paks/portredir/update.sh
>>>>>>>> @@ -0,0 +1,26 @@
>>>>>>>> +#!/bin/bash
>>>>>>>> +##############################################################
>>>>>>>> ##############
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# This file is part of the IPFire
>>>>>>>> Firewall.                                #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>>>> modify           #
>>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>>> published by     #
>>>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>>>> License, or        #
>>>>>>>> +# (at your option) any later
>>>>>>>> version.                                      #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>>>> useful,                #
>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>>> of           #
>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>>> the            #
>>>>>>>> +# GNU General Public License for more
>>>>>>>> details.                             #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>>> License        #
>>>>>>>> +# along with IPFire; if not, write to the Free
>>>>>>>> Software                    #
>>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>>>> 02111-1307 USA #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>>>> <info@ipfire.org>.                        #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +##############################################################
>>>>>>>> ##############
>>>>>>>> +#
>>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>>> +./uninstall.sh
>>>>>>>> +./install.sh
>>>>>>>> -- 
>>>>>>>> 2.18.0
>>>>>>>> 
>>>>>>> 
>>>>>> 
>>>>> 
>>>> 
>>> 
>> 
>
  
Jon Murphy July 1, 2021, 3:55 p.m. UTC | #9
Hmmm.  I tried an ALL (a.k.a ANY) instead and I think it works.  But there is a bug in the UI.


This is what I tried:





(Two images enclosed)


But, when I go to edit this rule the Destination Firewall changes from ALL to GREEN.  So I thought it wasn’t accepted.



(One more image enclosed)


Jon



> On Jul 1, 2021, at 10:24 AM, Michael Tremer <michael.tremer@ipfire.org> wrote:
> 
> @Stefan: There should be an exception in the UI that these rules can be created when REDIRECT is being used.
> 
>> On 1 Jul 2021, at 16:04, Jon Murphy <jcmurphy26@gmail.com> wrote:
>> 
>>> You probably want “any” as destination.
>> 
>> Those are the only choices that allow a Save.  When I enter an IP address I get this error:
>> 
>> 
>> Error messages
>> 
>> Source and destination IP addresses are from the same subnet.  
>> 
>> 
>> 
>> Jon
>> 
>>> On Jul 1, 2021, at 3:08 AM, Michael Tremer <michael.tremer@ipfire.org> wrote:
>>> 
>>> Hey Jon,
>>> 
>>> You probably want “any” as destination.
>>> 
>>> -Michael
>>> 
>>>> On 1 Jul 2021, at 04:08, Jon Murphy <jcmurphy26@gmail.com> wrote:
>>>> 
>>>> Hi Stefan,
>>>> 
>>>> Thank you for taking this on!
>>>> 
>>>> I applied the patchwork.ipfire patch.
>>>> 
>>>> I think I entered something wrong since I cannot get things to work.  I tried both with Destination Firewall GREEN & Firewall RED.
>>>> 
>>>> 
>>>> Does the Firewall Rule seem right?
>>>> 
>>>> Best regards,
>>>> Jon
>>>> 
>>>> 
>>>> Here is the rule I set up:
>>>> 
>>>> 
>>>> <Screen Shot 2021-06-30 at 9.53.52 PM.png>
>>>> 
>>>> <Screen Shot 2021-06-30 at 9.53.10 PM.png>
>>>> 
>>>> 
>>>> And this is what I see with conntrack:
>>>> 
>>>> conntrack -E -e NEW,UPDATE | grep -e "=53 "
>>>> 
>>>>   [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=51169 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=51169
>>>>   [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=54168 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=54168
>>>>   [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=56094 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=56094
>>>>   [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=52964 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=52964
>>>>   [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=53279 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=53279
>>>>   [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=61657 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=61657
>>>>   [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=57723 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=57723
>>>> 
>>>> 
>>>> 
>>>> 
>>>>> On Jun 30, 2021, at 2:14 PM, Stefan Schantl <stefan.schantl@ipfire.org> wrote:
>>>>> 
>>>>> Hello Matthias, Hello Michael, Hello Jon, Hello *,
>>>>> 
>>>>> I've followed the conversation on this list since the first mail and
>>>>> thoughts about forcing DNS traffic to use the local resolver.
>>>>> 
>>>>> It was a very long journey and lot of time and work has been spent to
>>>>> get to the present point.
>>>>> 
>>>>> As Michael requested here, I've digged through the lines of the perl
>>>>> script which is responsible for creating the firewall rules and
>>>>> surprisingly found that everyting which is needed to create generic
>>>>> REDIRECT rules already was written in the past - it just did not work
>>>>> as designed/expected.
>>>>> 
>>>>> Finaly I was able to adjust these lines of code and to repair that
>>>>> feature.
>>>>> 
>>>>> A redirect rule can be created by picking a single host or group of
>>>>> hosts or entire network(s) as source, selecting NAT (DNAT) and choosing
>>>>> the Firewall itself as target.
>>>>> 
>>>>> The protocol or service or service group which should be redirected has
>>>>> to be selected afterwards. If you want to redirect a given port to
>>>>> another one it can be specified as "Target port".
>>>>> 
>>>>> All created redirect rules are displayed as "input rules".
>>>>> 
>>>>> 
>>>>> The patch directly can be accessed here:
>>>>> 
>>>>> https://patchwork.ipfire.org/project/ipfire/patch/20210630184031.7726-1-stefan.schantl@ipfire.org/
>>>>> 
>>>>> Best regards,
>>>>> 
>>>>> -Stefan
>>>>> 
>>>>>> Hello,
>>>>>> 
>>>>>>> On 28 Jun 2021, at 18:53, Jon Murphy <jcmurphy26@gmail.com> wrote:
>>>>>>> 
>>>>>>> Hi Michael!  Happy Monday!
>>>>>>> 
>>>>>>> 
>>>>>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>>>>>> of code instead of adding many hundreds of lines?
>>>>>>>> 
>>>>>>>> Please can someone elaborate on this more?
>>>>>>> 
>>>>>>> Doing a DNS redirect, via the WegBUI, has been an issue since
>>>>>>> 2015.  I found this quote in the old forum:
>>>>>>> 
>>>>>>> "Having investigated a bit more I have concluded that it's not
>>>>>>> currently possible to create such rules through the WUI.
>>>>>>> 
>>>>>>> There are a number of obstacles:
>>>>>>> 1. It is not allowed to create a rule where source IP and
>>>>>>> destination nat IP is on the same subnetwork (e.g. GREEN), WUI
>>>>>>> error message: "Source and destination IP addresses are from the
>>>>>>> same subnet."
>>>>>>> 
>>>>>>> 2. WUI will not allow you to create a rule without a destination
>>>>>>> (the filtered packet must adhere to a destination, not only a port)
>>>>>>> and the destination MUST be an IP address of one of the IPFire
>>>>>>> interfaces, which limits whats possible a great deal." 
>>>>>> 
>>>>>> And these cannot be changed?
>>>>>> 
>>>>>>> And I found this from 2016:
>>>>>>> https://bugzilla.ipfire.org/show_bug.cgi?id=11168
>>>>>>> 
>>>>>>> So I am guessing that no one has been able to determine a way to
>>>>>>> extend the WebGUI.  
>>>>>> 
>>>>>> Has anyone tried? I do not see any obvious reasons why this should
>>>>>> not be possible.
>>>>>> 
>>>>>>> I am curious - Who created the 
>>>>>>> https://ipfire:444/cgi-bin/firewall.cgi page?  And could they help?
>>>>>> 
>>>>>> -Michael
>>>>>> 
>>>>>>> Jon
>>>>>>> 
>>>>>>> 
>>>>>>>> On Jun 28, 2021, at 11:04 AM, Michael Tremer <
>>>>>>>> michael.tremer@ipfire.org> wrote:
>>>>>>>> 
>>>>>>>> Hello Matthias,
>>>>>>>> 
>>>>>>>>> On 27 Jun 2021, at 14:48, Matthias Fischer <
>>>>>>>>> matthias.fischer@ipfire.org> wrote:
>>>>>>>>> 
>>>>>>>>> From: Marcel Lorenz <marcel.lorenz@ipfire.org>
>>>>>>>> 
>>>>>>>> Thank you for sending this patch on Marcel’s behalf, but I would
>>>>>>>> much more prefer if he would submit his patches on his own. I do
>>>>>>>> not see why that isn’t possible.
>>>>>>>> 
>>>>>>>>> Please note:
>>>>>>>>> This is a new addon written by Marcel Lorenz <
>>>>>>>>> marcel.lorenz@ipfire.org>.
>>>>>>>>> 
>>>>>>>>> It adds a new GUI to IPFire for DNS/NTP *and* user specific
>>>>>>>>> port redirections.
>>>>>>>>> 
>>>>>>>>> How its working:
>>>>>>>>> It has exactly the same functionalities as "Forcing
>>>>>>>>> DNS/NTP..."  - and some more.
>>>>>>>>> 
>>>>>>>>> By setting switches, DNS/NTP requests are automatically
>>>>>>>>> redirected to the local IPFire DNS/NTP servers.
>>>>>>>>> 
>>>>>>>>> Additionally, the user can specify custom redirections.
>>>>>>>>> 
>>>>>>>>> These rules are added to a new chain in PREROUTING =>
>>>>>>>>> PORT_REDIRECT.
>>>>>>>>> 
>>>>>>>>> To avoid problems with (e.g.) transparent 'squid'
>>>>>>>>> configurations,
>>>>>>>>> redirection rules are added automatically before existing
>>>>>>>>> 'squid' rules.
>>>>>>>> 
>>>>>>>> This message does unfortunately not say why this add-on would be
>>>>>>>> useful. I am emphasising this again and again that it is not very
>>>>>>>> important how something is done specially. That should be
>>>>>>>> commented in the code and other implementation details should
>>>>>>>> also be documented there.
>>>>>>>> 
>>>>>>>> As I have stated on this functionality many times before, I do
>>>>>>>> not see why this is necessary at all.
>>>>>>>> 
>>>>>>>> Why is this an add-on?
>>>>>>>> 
>>>>>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>>>>>> of code instead of adding many hundreds of lines?
>>>>>>>> 
>>>>>>>> Please can someone elaborate on this more?
>>>>>>>> 
>>>>>>>> -Michael
>>>>>>>> 
>>>>>>>>> Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
>>>>>>>>> ---
>>>>>>>>> config/portredir/EX-portredir.menu    |   6 +
>>>>>>>>> config/portredir/lang/portredir.de.pl |  19 +
>>>>>>>>> config/portredir/lang/portredir.en.pl |  19 +
>>>>>>>>> config/portredir/portredir-backup     |   1 +
>>>>>>>>> config/portredir/portredir.cgi        | 525
>>>>>>>>> ++++++++++++++++++++++++++
>>>>>>>>> config/rootfiles/common/misc-progs    |   1 +
>>>>>>>>> config/rootfiles/packages/portredir   |  11 +
>>>>>>>>> lfs/portredir                         |  85 +++++
>>>>>>>>> make.sh                               |   1 +
>>>>>>>>> src/initscripts/packages/portredir    | 191 ++++++++++
>>>>>>>>> src/misc-progs/Makefile               |   2 +-
>>>>>>>>> src/misc-progs/portredirctrl.c        |  47 +++
>>>>>>>>> src/paks/portredir/install.sh         |  32 ++
>>>>>>>>> src/paks/portredir/uninstall.sh       |  28 ++
>>>>>>>>> src/paks/portredir/update.sh          |  26 ++
>>>>>>>>> 15 files changed, 993 insertions(+), 1 deletion(-)
>>>>>>>>> create mode 100644 config/portredir/EX-portredir.menu
>>>>>>>>> create mode 100644 config/portredir/lang/portredir.de.pl
>>>>>>>>> create mode 100644 config/portredir/lang/portredir.en.pl
>>>>>>>>> create mode 100644 config/portredir/portredir-backup
>>>>>>>>> create mode 100644 config/portredir/portredir.cgi
>>>>>>>>> create mode 100644 config/rootfiles/packages/portredir
>>>>>>>>> create mode 100644 lfs/portredir
>>>>>>>>> create mode 100644 src/initscripts/packages/portredir
>>>>>>>>> create mode 100644 src/misc-progs/portredirctrl.c
>>>>>>>>> create mode 100644 src/paks/portredir/install.sh
>>>>>>>>> create mode 100644 src/paks/portredir/uninstall.sh
>>>>>>>>> create mode 100644 src/paks/portredir/update.sh
>>>>>>>>> 
>>>>>>>>> diff --git a/config/portredir/EX-portredir.menu
>>>>>>>>> b/config/portredir/EX-portredir.menu
>>>>>>>>> new file mode 100644
>>>>>>>>> index 000000000..8376e8053
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/config/portredir/EX-portredir.menu
>>>>>>>>> @@ -0,0 +1,6 @@
>>>>>>>>> +    $subfirewall->{'95.portredir'} = {
>>>>>>>>> +                               'caption' =>
>>>>>>>>> $Lang::tr{'portredir port redirections'},
>>>>>>>>> +                               'uri' => '/cgi-
>>>>>>>>> bin/portredir.cgi',
>>>>>>>>> +                               'title' =>
>>>>>>>>> "$Lang::tr{'portredir port redirections'}",
>>>>>>>>> +                               'enabled' => 1
>>>>>>>>> +                               };
>>>>>>>>> diff --git a/config/portredir/lang/portredir.de.pl
>>>>>>>>> b/config/portredir/lang/portredir.de.pl
>>>>>>>>> new file mode 100644
>>>>>>>>> index 000000000..b932d4a85
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/config/portredir/lang/portredir.de.pl
>>>>>>>>> @@ -0,0 +1,19 @@
>>>>>>>>> +%tr = (
>>>>>>>>> +%tr,
>>>>>>>>> +'portredir enable addon' => 'Addon aktivieren',
>>>>>>>>> +'portredir common settings' => 'Allgemeine Einstellungen',
>>>>>>>>> +'portredir port redirections' => 'Portumleitungen',
>>>>>>>>> +'portredir fw for interface' => 'Firewalloptionen für das
>>>>>>>>> Interface',
>>>>>>>>> +'portredir enable user redirections' => 'Aktiviere
>>>>>>>>> benutzerdefinierte Portumleitungen',
>>>>>>>>> +'portredir force local dns' => 'Erzwinge lokale DNS-Server',
>>>>>>>>> +'portredir force local ntp' => 'Erzwinge lokale NTP-Server',
>>>>>>>>> +'portredir custom redirections' => 'Benutzerdefinierte
>>>>>>>>> Portumleitungen',
>>>>>>>>> +'portredir remove rule' => 'Entferne Regel',
>>>>>>>>> +'portredir add rule' => 'Hinzufügen',
>>>>>>>>> +'portredir no entries' => 'Keine Einträge vorhanden.',
>>>>>>>>> +'portredir invalid address' => 'Ungültige Host-Addresse.',
>>>>>>>>> +'portredir empty input' => 'Fehlende Angabe: Bitte geben Sie
>>>>>>>>> einen gültigen Host an.',
>>>>>>>>> +'portredir save to activate' => 'Speichern, um Änderungen zu
>>>>>>>>> aktivieren',
>>>>>>>>> +);
>>>>>>>>> +
>>>>>>>>> +#EOF
>>>>>>>>> diff --git a/config/portredir/lang/portredir.en.pl
>>>>>>>>> b/config/portredir/lang/portredir.en.pl
>>>>>>>>> new file mode 100644
>>>>>>>>> index 000000000..f442f3eaa
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/config/portredir/lang/portredir.en.pl
>>>>>>>>> @@ -0,0 +1,19 @@
>>>>>>>>> +%tr = (
>>>>>>>>> +%tr,
>>>>>>>>> +'portredir enable addon' => 'Enable addon',
>>>>>>>>> +'portredir common settings' => 'Common settings',
>>>>>>>>> +'portredir port redirections' => 'Port redirections',
>>>>>>>>> +'portredir fw for interface' => 'Firewall options for
>>>>>>>>> interface',
>>>>>>>>> +'portredir enable user redirections' => 'Enable user port
>>>>>>>>> redirections',
>>>>>>>>> +'portredir force local dns' => 'Enforce local DNS servers',
>>>>>>>>> +'portredir force local ntp' => 'Enforce local NTP servers',
>>>>>>>>> +'portredir custom redirections' => 'Custom port redirections',
>>>>>>>>> +'portredir remove rule' => 'Remove rule',
>>>>>>>>> +'portredir add rule' => 'Add new',
>>>>>>>>> +'portredir no entries' => 'No entries at the moment.',
>>>>>>>>> +'portredir invalid address' => 'Invalid host address.',
>>>>>>>>> +'portredir empty input' => 'Empty input: Please enter a valid
>>>>>>>>> host.',
>>>>>>>>> +'portredir save to activate' => 'Save to activate changes',
>>>>>>>>> +);
>>>>>>>>> +
>>>>>>>>> +#EOF
>>>>>>>>> diff --git a/config/portredir/portredir-backup
>>>>>>>>> b/config/portredir/portredir-backup
>>>>>>>>> new file mode 100644
>>>>>>>>> index 000000000..bd2ada742
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/config/portredir/portredir-backup
>>>>>>>>> @@ -0,0 +1 @@
>>>>>>>>> +/var/ipfire/portredir
>>>>>>>>> diff --git a/config/portredir/portredir.cgi
>>>>>>>>> b/config/portredir/portredir.cgi
>>>>>>>>> new file mode 100644
>>>>>>>>> index 000000000..4913dda3f
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/config/portredir/portredir.cgi
>>>>>>>>> @@ -0,0 +1,525 @@
>>>>>>>>> +#!/usr/bin/perl
>>>>>>>>> +##############################################################
>>>>>>>>> #################
>>>>>>>>> +#                                                             
>>>>>>>>>               #
>>>>>>>>> +# IPFire.org - A linux based
>>>>>>>>> firewall                                         #
>>>>>>>>> +# Copyright (C) 2021  IPFire Team 
>>>>>>>>> <info@ipfire.org>                          #
>>>>>>>>> +#                                                             
>>>>>>>>>               #
>>>>>>>>> +# This program is free software: you can redistribute it
>>>>>>>>> and/or modify        #
>>>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>>>> published by        #
>>>>>>>>> +# the Free Software Foundation, either version 3 of the
>>>>>>>>> License, or           #
>>>>>>>>> +# (at your option) any later
>>>>>>>>> version.                                         #
>>>>>>>>> +#                                                             
>>>>>>>>>               #
>>>>>>>>> +# This program is distributed in the hope that it will be
>>>>>>>>> useful,             #
>>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>>>> of              #
>>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>>>> the               #
>>>>>>>>> +# GNU General Public License for more
>>>>>>>>> details.                                #
>>>>>>>>> +#                                                             
>>>>>>>>>               #
>>>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>>>> License           #
>>>>>>>>> +# along with this program.  If not, see <
>>>>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>>>>> +#                                                             
>>>>>>>>>               #
>>>>>>>>> +##############################################################
>>>>>>>>> #################
>>>>>>>>> +
>>>>>>>>> +use strict;
>>>>>>>>> +
>>>>>>>>> +# enable only the following on debugging purpose
>>>>>>>>> +use warnings;
>>>>>>>>> +use CGI::Carp 'fatalsToBrowser';
>>>>>>>>> +
>>>>>>>>> +require '/var/ipfire/general-functions.pl';
>>>>>>>>> +require "${General::swroot}/lang.pl";
>>>>>>>>> +require "${General::swroot}/header.pl";
>>>>>>>>> +
>>>>>>>>> +# File declarations
>>>>>>>>> +my $settingsfile = "${General::swroot}/portredir/settings";
>>>>>>>>> +my $redirectsfile = "${General::swroot}/portredir/redirects";
>>>>>>>>> +
>>>>>>>>> +# Create empty settingsfiles if they does not exist yet
>>>>>>>>> +unless (-e "$settingsfile") { system ("touch $settingsfile");
>>>>>>>>> }
>>>>>>>>> +unless (-e "$redirectsfile") { system ("touch
>>>>>>>>> $redirectsfile"); }
>>>>>>>>> +
>>>>>>>>> +# load ipfire settings
>>>>>>>>> +our %netsettings = ();
>>>>>>>>> +our %color = ();
>>>>>>>>> +&General::readhash("${General::swroot}/ethernet/settings",
>>>>>>>>> \%netsettings);
>>>>>>>>> +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include
>>>>>>>>> /colors.txt", \%color);
>>>>>>>>> +
>>>>>>>>> +my %settings=();
>>>>>>>>> +my %portredirs=();
>>>>>>>>> +my %checked=(); # Checkbox manipulations
>>>>>>>>> +my $errormessage='';
>>>>>>>>> +my %selected=();
>>>>>>>>> +our %redirects=();
>>>>>>>>> +
>>>>>>>>> +$settings{'ACTION'} = '';
>>>>>>>>> +$settings{'REDIR_ENABLE_ADDON'}="off";
>>>>>>>>> +$settings{'REDIR_CUSTOM_GREEN'}="off";
>>>>>>>>> +$settings{'REDIR_CUSTOM_BLUE'}="off";
>>>>>>>>> +$settings{'REDIR_CUSTOM_ORANGE'}="off";
>>>>>>>>> +$settings{'REDIR_DNS_GREEN'}="off";
>>>>>>>>> +$settings{'REDIR_NTP_GREEN'}="off";
>>>>>>>>> +$settings{'REDIR_DNS_BLUE'}="off";
>>>>>>>>> +$settings{'REDIR_NTP_BLUE'}="off";
>>>>>>>>> +$settings{'REDIR_DNS_ORANGE'}="off";
>>>>>>>>> +$settings{'REDIR_NTP_ORANGE'}="off";
>>>>>>>>> +
>>>>>>>>> +&Header::showhttpheaders();
>>>>>>>>> +
>>>>>>>>> +# Get GUI values
>>>>>>>>> +&Header::getcgihash(\%settings);
>>>>>>>>> +
>>>>>>>>> +# Save action
>>>>>>>>> +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
>>>>>>>>> +
>>>>>>>>> +       # If custom rules enabled, deactivate default rules on
>>>>>>>>> interface
>>>>>>>>> +       if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
>>>>>>>>> +               $settings{'REDIR_DNS_GREEN'}="off";
>>>>>>>>> +               $settings{'REDIR_NTP_GREEN'}="off";
>>>>>>>>> +       }
>>>>>>>>> +
>>>>>>>>> +       if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
>>>>>>>>> +               $settings{'REDIR_DNS_BLUE'}="off";
>>>>>>>>> +               $settings{'REDIR_NTP_BLUE'}="off";
>>>>>>>>> +       }
>>>>>>>>> +
>>>>>>>>> +       if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
>>>>>>>>> +               $settings{'REDIR_DNS_ORANGE'}="off";
>>>>>>>>> +               $settings{'REDIR_NTP_ORANGE'}="off";
>>>>>>>>> +       }
>>>>>>>>> +
>>>>>>>>> +       &General::writehash($settingsfile, \%settings);
>>>>>>>>> +
>>>>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
>>>>>>>>> +               system ('/usr/local/bin/portredirctrl restart
>>>>>>>>>> /dev/null 2>&1');
>>>>>>>>> +               system ('/usr/local/bin/portredirctrl enable
>>>>>>>>>> /dev/null 2>&1');
>>>>>>>>> +               &General::log('portredir addon: port
>>>>>>>>> redirections enabled');
>>>>>>>>> +       }
>>>>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
>>>>>>>>> +               system ('/usr/local/bin/portredirctrl disable
>>>>>>>>>> /dev/null 2>&1');
>>>>>>>>> +               system ('/usr/local/bin/portredirctrl stop
>>>>>>>>>> /dev/null 2>&1');
>>>>>>>>> +               &General::log('portredir addon: port
>>>>>>>>> redirections disabled');
>>>>>>>>> +       }
>>>>>>>>> +
>>>>>>>>> +# Add/edit an entry to the redirectsfile.
>>>>>>>>> +
>>>>>>>>> +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) ||
>>>>>>>>> ($settings{'ACTION'} eq $Lang::tr{'update'})) {
>>>>>>>>> +
>>>>>>>>> +       # Check if any input has been performed.
>>>>>>>>> +       if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
>>>>>>>>> +
>>>>>>>>> +               # Check if the given input is no valid IP-
>>>>>>>>> address, display an error message.
>>>>>>>>> +               if
>>>>>>>>> (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
>>>>>>>>> +                       $errormessage = "$Lang::tr{'portredir
>>>>>>>>> invalid address'}";
>>>>>>>>> +               }
>>>>>>>>> +       } else {
>>>>>>>>> +               $errormessage = "$Lang::tr{'portredir empty
>>>>>>>>> input'}";
>>>>>>>>> +       }
>>>>>>>>> +
>>>>>>>>> +       # Go further if there was no error.
>>>>>>>>> +       if ($errormessage eq '') {
>>>>>>>>> +               my %redirects = ();
>>>>>>>>> +               my $id;
>>>>>>>>> +               my $status;
>>>>>>>>> +
>>>>>>>>> +               # Assign hash values.
>>>>>>>>> +               my $new_entry_interface =
>>>>>>>>> $settings{'REDIR_ENTRY_INTERFACE'};
>>>>>>>>> +               my $new_entry_protocol =
>>>>>>>>> $settings{'REDIR_ENTRY_PROTOCOL'};
>>>>>>>>> +               my $new_entry_port =
>>>>>>>>> $settings{'REDIR_ENTRY_PORT'};
>>>>>>>>> +               my $new_entry_address =
>>>>>>>>> $settings{'REDIR_ENTRY_ADDRESS'};
>>>>>>>>> +               my $new_entry_remark =
>>>>>>>>> $settings{'REDIR_ENTRY_REMARK'};
>>>>>>>>> +
>>>>>>>>> +               # Read-in redirectsfile.
>>>>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>>>>> \%redirects);
>>>>>>>>> +
>>>>>>>>> +               # Check if we should edit an existing entry and
>>>>>>>>> got an ID.
>>>>>>>>> +               if (($settings{'ACTION'} eq
>>>>>>>>> $Lang::tr{'update'}) && ($settings{'ID'})) {
>>>>>>>>> +                       # Assin the provided id.
>>>>>>>>> +                       $id = $settings{'ID'};
>>>>>>>>> +
>>>>>>>>> +                       # Undef the given ID.
>>>>>>>>> +                       undef($settings{'ID'});
>>>>>>>>> +
>>>>>>>>> +                       # Grab the configured status of the
>>>>>>>>> corresponding entry.
>>>>>>>>> +                       $status = $redirects{$id}[4];
>>>>>>>>> +               } else {
>>>>>>>>> +                       # Each newly added entry automatically
>>>>>>>>> should be enabled.
>>>>>>>>> +                       $status = "enabled";
>>>>>>>>> +
>>>>>>>>> +                       # Generate the ID for the new entry.
>>>>>>>>> +                       #
>>>>>>>>> +                       # Sort the keys by their ID and store
>>>>>>>>> them in an array.
>>>>>>>>> +                       my @keys = sort { $a <=> $b } keys
>>>>>>>>> %redirects;
>>>>>>>>> +
>>>>>>>>> +                       # Reverse the key array.
>>>>>>>>> +                       my @reversed = reverse(@keys);
>>>>>>>>> +
>>>>>>>>> +                       # Obtain the last used id.
>>>>>>>>> +                       my $last_id = @reversed[0];
>>>>>>>>> +
>>>>>>>>> +                       # Increase the last id by one and use
>>>>>>>>> it as id for the new entry.
>>>>>>>>> +                       $id = ++$last_id;
>>>>>>>>> +               }
>>>>>>>>> +
>>>>>>>>> +               # Add/Modify the entry to/in the redirects
>>>>>>>>> hash.
>>>>>>>>> +               $redirects{$id} = ["$new_entry_interface",
>>>>>>>>> "$new_entry_protocol", "$new_entry_port",
>>>>>>>>> "$new_entry_address","$status", "$new_entry_remark"];
>>>>>>>>> +
>>>>>>>>> +               # Write the changed redirects hash to the
>>>>>>>>> redirects file.
>>>>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>>>>> \%redirects);
>>>>>>>>> +       }
>>>>>>>>> +
>>>>>>>>> +# Toggle Enabled/Disabled for an existing entry on the
>>>>>>>>> redirects list.
>>>>>>>>> +
>>>>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable
>>>>>>>>> disable'}) {
>>>>>>>>> +       my %redirects = ();
>>>>>>>>> +
>>>>>>>>> +       # Only go further, if an ID has been passed.
>>>>>>>>> +       if ($settings{'ID'}) {
>>>>>>>>> +               # Assign the given ID.
>>>>>>>>> +               my $id = $settings{'ID'};
>>>>>>>>> +
>>>>>>>>> +               # Undef the given ID.
>>>>>>>>> +               undef($settings{'ID'});
>>>>>>>>> +
>>>>>>>>> +               # Read-in ignoredfile.
>>>>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>>>>> \%redirects);
>>>>>>>>> +
>>>>>>>>> +               # Grab the configured status of the
>>>>>>>>> corresponding entry.
>>>>>>>>> +               my $status = $redirects{$id}[4];
>>>>>>>>> +
>>>>>>>>> +               # Switch the status.
>>>>>>>>> +               if ($status eq "disabled") {
>>>>>>>>> +                       $status = "enabled";
>>>>>>>>> +               } else {
>>>>>>>>> +                       $status = "disabled";
>>>>>>>>> +               }
>>>>>>>>> +
>>>>>>>>> +               # Modify the status of the existing entry.
>>>>>>>>> +               $redirects{$id} = ["$redirects{$id}[0]",
>>>>>>>>> "$redirects{$id}[1]", "$redirects{$id}[2]",
>>>>>>>>> "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
>>>>>>>>> +
>>>>>>>>> +               # Write the changed ignored hash to the
>>>>>>>>> redirects file.
>>>>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>>>>> \%redirects);
>>>>>>>>> +       }
>>>>>>>>> +
>>>>>>>>> +# Remove entry from redirects list.
>>>>>>>>> +
>>>>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
>>>>>>>>> +       my %redirects = ();
>>>>>>>>> +
>>>>>>>>> +       # Read-in redirectsfile.
>>>>>>>>> +       &General::readhasharray($redirectsfile, \%redirects);
>>>>>>>>> +
>>>>>>>>> +       # move data on key up
>>>>>>>>> +       foreach my $key (sort keys %redirects) {
>>>>>>>>> +               if ($key >= $settings{'ID'}) {
>>>>>>>>> +                       my $next = $key + 1;
>>>>>>>>> +                       if (exists $redirects{$next}) {
>>>>>>>>> +                               foreach my $i (0 ..
>>>>>>>>> $#{$redirects{$next}}) { $redirects{$key}[$i] =
>>>>>>>>> $redirects{$next}[$i]; }
>>>>>>>>> +                       }
>>>>>>>>> +               }
>>>>>>>>> +       }
>>>>>>>>> +
>>>>>>>>> +       my $last_key = (sort {$a <=> $b} keys %redirects)[-1];
>>>>>>>>> +       delete $redirects{$last_key};
>>>>>>>>> +
>>>>>>>>> +       # Undef the given ID.
>>>>>>>>> +       undef($settings{'ID'});
>>>>>>>>> +
>>>>>>>>> +       # Write the changed redirects hash to file.
>>>>>>>>> +       &General::writehasharray($redirectsfile, \%redirects);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +# Load settings from file
>>>>>>>>> +&General::readhash($settingsfile, \%settings);
>>>>>>>>> +&General::readhasharray($redirectsfile, \%redirects);
>>>>>>>>> +
>>>>>>>>> +# Call functions to generate whole page.
>>>>>>>>> +&Header::openpage($Lang::tr{'portredir port redirections'}, 1,
>>>>>>>>> '');
>>>>>>>>> +&Header::openbigbox('100%', 'left', '', $errormessage);
>>>>>>>>> +
>>>>>>>>> +if ($errormessage) {
>>>>>>>>> +        &Header::openbox('100%', 'left', $Lang::tr{'warning
>>>>>>>>> messages'});
>>>>>>>>> +        print "<font color='red'>$errormessage&nbsp;</font>";
>>>>>>>>> +        &Header::closebox();
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
>>>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
>>>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_ADDON'}
>>>>>>>>> } = "checked='checked'";
>>>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
>>>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
>>>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_GREEN'}
>>>>>>>>> } = "checked='checked'";
>>>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
>>>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
>>>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BLUE'}}
>>>>>>>>> = "checked='checked'";
>>>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
>>>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
>>>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_ORANGE
>>>>>>>>> '}} = "checked='checked'";
>>>>>>>>> +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
>>>>>>>>> +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
>>>>>>>>> +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}} =
>>>>>>>>> "checked='checked'";
>>>>>>>>> +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
>>>>>>>>> +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
>>>>>>>>> +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}} =
>>>>>>>>> "checked='checked'";
>>>>>>>>> +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
>>>>>>>>> +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
>>>>>>>>> +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}} =
>>>>>>>>> "checked='checked'";
>>>>>>>>> +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
>>>>>>>>> +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
>>>>>>>>> +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}} =
>>>>>>>>> "checked='checked'";
>>>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
>>>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
>>>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE'}} =
>>>>>>>>> "checked='checked'";
>>>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
>>>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
>>>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE'}} =
>>>>>>>>> "checked='checked'";
>>>>>>>>> +
>>>>>>>>> +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTRY_INTE
>>>>>>>>> RFACE'}} = 'selected';
>>>>>>>>> +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY_PROTO
>>>>>>>>> COL'}} = 'selected';
>>>>>>>>> +
>>>>>>>>> +&showMainBox();
>>>>>>>>> +&showRedirectsBox();
>>>>>>>>> +
>>>>>>>>> +&Header::closebigbox();
>>>>>>>>> +&Header::closepage();
>>>>>>>>> +
>>>>>>>>> +# Function to show main settings and options.
>>>>>>>>> +sub showMainBox() {
>>>>>>>>> +
>>>>>>>>> +       &Header::openbox('100%', 'center',
>>>>>>>>> "$Lang::tr{'settings'}");
>>>>>>>>> +       print "<form method='post'
>>>>>>>>> action='$ENV{'SCRIPT_NAME'}'>";
>>>>>>>>> +
>>>>>>>>> +print <<END;
>>>>>>>>> +<table width='80%' cellspacing='0' border='0'>
>>>>>>>>> +       <tr><td colspan='2' class='base'
>>>>>>>>> bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir common
>>>>>>>>> settings'}</b></td></tr
>>>>>>>>> +       <tr>
>>>>>>>>> +               <td width='25%'
>>>>>>>>> class='base'>$Lang::tr{'portredir enable addon'}:</td>
>>>>>>>>> +               <td><input type='checkbox'
>>>>>>>>> name='REDIR_ENABLE_ADDON' $checked{'REDIR_ENABLE_ADDON'}{'on'}
>>>>>>>>> /></td>
>>>>>>>>> +       </tr>
>>>>>>>>> +       <tr><td colspan='2'></td></tr>
>>>>>>>>> +       <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>>>>> +END
>>>>>>>>> +
>>>>>>>>> +       # create html table with header line 1
>>>>>>>>> +       print "<table width='80%' cellspacing='0'
>>>>>>>>> border='0'><tr>";
>>>>>>>>> +       print "<th class='base' width='40%'
>>>>>>>>> align='left'><b>$Lang::tr{'portredir fw for interface'}</th>";
>>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<th
>>>>>>>>> class='base' width='10%'><b><font
>>>>>>>>> color=green>$Lang::tr{'green'}</font></th>";
>>>>>>>>> +               } else {                 print "<th
>>>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<th
>>>>>>>>> class='base' width='10%'><b><font
>>>>>>>>> color=blue>$Lang::tr{'blue'}</font></th>";
>>>>>>>>> +               } else {                 print "<th
>>>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<th
>>>>>>>>> class='base' width='10%'><b><font
>>>>>>>>> color=orange>$Lang::tr{'orange'}</font></th>";
>>>>>>>>> +               } else {                 print "<th
>>>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>>>> +
>>>>>>>>> +       # the empty right row
>>>>>>>>> +       print "<th class='base'
>>>>>>>>> width='30%'><td></td></th></tr>";
>>>>>>>>> +
>>>>>>>>> +       # line 2
>>>>>>>>> +       print "<tr><td>$Lang::tr{'portredir force local
>>>>>>>>> dns'}</td>";
>>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>> name='REDIR_DNS_GREEN'
>>>>>>>>> $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print
>>>>>>>>> "<td></td>";}
>>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>> name='REDIR_DNS_BLUE' $checked{'REDIR_DNS_BLUE'}{'on'}></td>";}
>>>>>>>>> else { print "<td></td>";}
>>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>> name='REDIR_DNS_ORANGE'
>>>>>>>>> $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print
>>>>>>>>> "<td></td>";}
>>>>>>>>> +
>>>>>>>>> +       # line 3
>>>>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir force local
>>>>>>>>> ntp'}</td>";
>>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>> name='REDIR_NTP_GREEN'
>>>>>>>>> $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print
>>>>>>>>> "<td></td>";}
>>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>> name='REDIR_NTP_BLUE' $checked{'REDIR_NTP_BLUE'}{'on'}></td>";}
>>>>>>>>> else { print "<td></td>";}
>>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>> name='REDIR_NTP_ORANGE'
>>>>>>>>> $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print
>>>>>>>>> "<td></td>";}
>>>>>>>>> +
>>>>>>>>> +       # line 4
>>>>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir enable user
>>>>>>>>> redirections'}</td>";
>>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>> name='REDIR_CUSTOM_GREEN'
>>>>>>>>> $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else { print
>>>>>>>>> "<td></td>";}
>>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>> name='REDIR_CUSTOM_BLUE'
>>>>>>>>> $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print
>>>>>>>>> "<td></td>";}
>>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>> name='REDIR_CUSTOM_ORANGE'
>>>>>>>>> $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else { print
>>>>>>>>> "<td></td>";}
>>>>>>>>> +
>>>>>>>>> +       print <<END;
>>>>>>>>> +       </tr></table>
>>>>>>>>> +       <table width='80%' cellspacing='0' border='0'>
>>>>>>>>> +               <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>>>>> +               <tr><td align='left'><b>$Lang::tr{'portredir
>>>>>>>>> save to activate'}</b></td><td width='5%' align='center'><input
>>>>>>>>> type='submit' name='ACTION' value='  $Lang::tr{'save'} 
>>>>>>>>> '></td></tr>
>>>>>>>>> +       </table></form>
>>>>>>>>> +END
>>>>>>>>> +
>>>>>>>>> +&Header::closebox();
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +# Function to show elements of the redirects file and allow to
>>>>>>>>> add or remove single members of it.
>>>>>>>>> +sub showRedirectsBox() {
>>>>>>>>> +        &Header::openbox('100%', 'center',
>>>>>>>>> "$Lang::tr{'portredir custom redirections'}");
>>>>>>>>> +
>>>>>>>>> +       print <<END;
>>>>>>>>> +               <table width='80%' cellspacing='1' border='0'>
>>>>>>>>> +                       <tr>
>>>>>>>>> +                               <td class='base'
>>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>>> align='center'><b>$Lang::tr{'interface'}</b></td>
>>>>>>>>> +                               <td class='base'
>>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>>> align='center'><b>$Lang::tr{'protocol'}</b></td>
>>>>>>>>> +                               <td class='base'
>>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>>> align='center'><b>$Lang::tr{'port'}</b></td>
>>>>>>>>> +                               <td class='base'
>>>>>>>>> bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'ip
>>>>>>>>> address'}</b></td>
>>>>>>>>> +                               <td class='base'
>>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>>> align='center'><b>$Lang::tr{'remark'}</b></td>
>>>>>>>>> +                               <td class='base' colspan='3'
>>>>>>>>> bgcolor='$color{'color20'}'></td>
>>>>>>>>> +                       </tr>
>>>>>>>>> +END
>>>>>>>>> +                       # Check if some rules have been added
>>>>>>>>> to be redirects.
>>>>>>>>> +                       if (keys (%redirects)) {
>>>>>>>>> +                               my $col = "";
>>>>>>>>> +
>>>>>>>>> +                               # List all entries of the hash.
>>>>>>>>> +                               foreach my $key (sort keys
>>>>>>>>> %redirects){
>>>>>>>>> +
>>>>>>>>> +                                       # Assign data array
>>>>>>>>> positions to some nice variable names.
>>>>>>>>> +                                       my $interface =
>>>>>>>>> $redirects{$key}[0];
>>>>>>>>> +                                       my $protocol =
>>>>>>>>> $redirects{$key}[1];
>>>>>>>>> +                                       my $port  =
>>>>>>>>> $redirects{$key}[2];
>>>>>>>>> +                                       my $address =
>>>>>>>>> $redirects{$key}[3];
>>>>>>>>> +                                       my $status  =
>>>>>>>>> $redirects{$key}[4];
>>>>>>>>> +                                       my $remark  =
>>>>>>>>> $redirects{$key}[5];
>>>>>>>>> +
>>>>>>>>> +                                       # Check if the key (id)
>>>>>>>>> number is even or not.
>>>>>>>>> +                                       if ($settings{'ID'} eq
>>>>>>>>> $key) {
>>>>>>>>> +                                               $col="bgcolor='
>>>>>>>>> ${Header::colouryellow}'";
>>>>>>>>> +                                       } elsif ($key % 2) {
>>>>>>>>> +                                               $col="bgcolor='
>>>>>>>>> $color{'color22'}'";
>>>>>>>>> +                                       } else {
>>>>>>>>> +                                               $col="bgcolor='
>>>>>>>>> $color{'color20'}'";
>>>>>>>>> +                                       }
>>>>>>>>> +
>>>>>>>>> +                                       # Choose icon for the
>>>>>>>>> checkbox.
>>>>>>>>> +                                       my $gif;
>>>>>>>>> +                                       my $gdesc;
>>>>>>>>> +
>>>>>>>>> +                                       # Check if the status
>>>>>>>>> is enabled and select the correct image and description.
>>>>>>>>> +                                       if ($status eq
>>>>>>>>> 'enabled' ) {
>>>>>>>>> +                                               $gif =
>>>>>>>>> 'on.gif';
>>>>>>>>> +                                               $gdesc =
>>>>>>>>> $Lang::tr{'click to disable'};
>>>>>>>>> +                                       } else {
>>>>>>>>> +                                               $gif =
>>>>>>>>> 'off.gif';
>>>>>>>>> +                                               $gdesc =
>>>>>>>>> $Lang::tr{'click to enable'};
>>>>>>>>> +                                       }
>>>>>>>>> +
>>>>>>>>> +                                       print <<END;
>>>>>>>>> +                                       <tr>
>>>>>>>>> +                                               <td width='15%'
>>>>>>>>> class='base' align='center' $col><b><font
>>>>>>>>> color=$interface>$Lang::tr{$interface}</font></b></td>
>>>>>>>>> +                                               <td width='10%'
>>>>>>>>> class='base' align='center' $col>$protocol</td>
>>>>>>>>> +                                               <td width='10%'
>>>>>>>>> class='base' align='center' $col>$port</td>
>>>>>>>>> +                                               <td width='15%'
>>>>>>>>> class='base' align='center' $col>&nbsp;$address</td>
>>>>>>>>> +                                               <td width='40%'
>>>>>>>>> class='base' align='center' $col>&nbsp;$remark</td>
>>>>>>>>> +                                               <td
>>>>>>>>> align='center' $col>
>>>>>>>>> +                                                       <form
>>>>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>>> +                                                              
>>>>>>>>> <input type='hidden' name='ACTION' value='$Lang::tr{'toggle
>>>>>>>>> enable disable'}' />
>>>>>>>>> +                                                              
>>>>>>>>> <input type='image' name='$Lang::tr{'toggle enable disable'}'
>>>>>>>>> src='/images/$gif' alt='$gdesc' title='$gdesc' />
>>>>>>>>> +                                                              
>>>>>>>>> <input type='hidden' name='ID' value='$key' />
>>>>>>>>> +                                                       </form>
>>>>>>>>> +                                               </td>
>>>>>>>>> +                                               <td
>>>>>>>>> align='center' $col>
>>>>>>>>> +                                                       <form
>>>>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>>> +                                                              
>>>>>>>>> <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}'
>>>>>>>>> />
>>>>>>>>> +                                                              
>>>>>>>>> <input type='image' name='$Lang::tr{'edit'}'
>>>>>>>>> src='/images/edit.gif' alt='$Lang::tr{'edit'}'
>>>>>>>>> title='$Lang::tr{'edit'}' />
>>>>>>>>> +                                                              
>>>>>>>>> <input type='hidden' name='ID' value='$key' />
>>>>>>>>> +                                                       </form>
>>>>>>>>> +                                               </td>
>>>>>>>>> +                                               <td
>>>>>>>>> align='center' $col>
>>>>>>>>> +                                                       <form
>>>>>>>>> method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>>> +                                                              
>>>>>>>>> <input type='image' name='$Lang::tr{'remove'}'
>>>>>>>>> src='/images/delete.gif' title='$Lang::tr{'remove'}'
>>>>>>>>> alt='$Lang::tr{'remove'}'>
>>>>>>>>> +                                                              
>>>>>>>>> <input type='hidden' name='ID' value='$key'>
>>>>>>>>> +                                                              
>>>>>>>>> <input type='hidden' name='ACTION'
>>>>>>>>> value='$Lang::tr{'remove'}'>
>>>>>>>>> +                                                       </form>
>>>>>>>>> +                                               </td>
>>>>>>>>> +                                       </tr>
>>>>>>>>> +END
>>>>>>>>> +                               }
>>>>>>>>> +                       } else {
>>>>>>>>> +                               # Print notice that currently
>>>>>>>>> no ports are redirected.
>>>>>>>>> +                               print "<tr>\n";
>>>>>>>>> +                               print "<td class='base'
>>>>>>>>> colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
>>>>>>>>> +                               print "</tr>\n";
>>>>>>>>> +                       }
>>>>>>>>> +
>>>>>>>>> +               print "</table>\n";
>>>>>>>>> +
>>>>>>>>> +       # Section to add new elements or edit existing ones.
>>>>>>>>> +       print <<END;
>>>>>>>>> +       <br>
>>>>>>>>> +       <hr>
>>>>>>>>> +       <br>
>>>>>>>>> +       <div align='center'>
>>>>>>>>> +               <table width='100%' cellspacing='0' border='0'>
>>>>>>>>> +END
>>>>>>>>> +
>>>>>>>>> +       # Assign correct headline and button text.
>>>>>>>>> +       my $buttontext;
>>>>>>>>> +       my $entry_interface;
>>>>>>>>> +       my $entry_protocol;
>>>>>>>>> +       my $entry_port;
>>>>>>>>> +       my $entry_address;
>>>>>>>>> +       my $entry_remark;
>>>>>>>>> +
>>>>>>>>> +       # Check if an ID (key) has been given, in this case an
>>>>>>>>> existing entry should be edited.
>>>>>>>>> +       if ($settings{'ID'} ne '') {
>>>>>>>>> +               $buttontext = $Lang::tr{'update'};
>>>>>>>>> +               print "<tr><td class='boldbase'
>>>>>>>>> colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
>>>>>>>>> +
>>>>>>>>> +               # Grab address and remark for the given key.
>>>>>>>>> +               $entry_interface =
>>>>>>>>> $redirects{$settings{'ID'}}[0];
>>>>>>>>> +               $entry_protocol =
>>>>>>>>> $redirects{$settings{'ID'}}[1];
>>>>>>>>> +               $entry_port = $redirects{$settings{'ID'}}[2];
>>>>>>>>> +               $entry_address =
>>>>>>>>> $redirects{$settings{'ID'}}[3];
>>>>>>>>> +               $entry_remark = $redirects{$settings{'ID'}}[5];
>>>>>>>>> +
>>>>>>>>> +       } else {
>>>>>>>>> +               $buttontext = $Lang::tr{'add'};
>>>>>>>>> +               print "<tr><td class='boldbase'
>>>>>>>>> colspan='11'><b>$Lang::tr{'dnsforward add a new
>>>>>>>>> entry'}</b></td></tr>\n";
>>>>>>>>> +               print "<tr><td>&nbsp</td></tr>\n";
>>>>>>>>> +       }
>>>>>>>>> +
>>>>>>>>> +       print <<END;
>>>>>>>>> +                       <tr>
>>>>>>>>> +                               <td class='base' width='1%' 
>>>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>>>> +                               <td class='base' width='15%'
>>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>>> align='left'><b>$Lang::tr{'interface'}</b></td>
>>>>>>>>> +                               <td class='base' width='10%'
>>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>>> align='left'><b>$Lang::tr{'protocol'}</b></td>
>>>>>>>>> +                               <td class='base' width='10%'
>>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>>> align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
>>>>>>>>> +                               <td class='base' width='13%'
>>>>>>>>> bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'ip
>>>>>>>>> address'}</b></td>
>>>>>>>>> +                               <td class='base' width='30%'
>>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>>> align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
>>>>>>>>> +                               <td class='base' width='15%'
>>>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>>>> +                               <td class='base' width='1%' 
>>>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>>>> +                       </tr>
>>>>>>>>> +
>>>>>>>>> +                       <form method='post'
>>>>>>>>> action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>>> +                       <input type='hidden' name='ID'
>>>>>>>>> value='$settings{'ID'}'>
>>>>>>>>> +                       <tr>
>>>>>>>>> +                               <td class='base'></td>
>>>>>>>>> +                               <td><select style='width:90px;'
>>>>>>>>> id='interface' name='REDIR_ENTRY_INTERFACE'>
>>>>>>>>> +END
>>>>>>>>> +                               if ($netsettings{'GREEN_DEV'})
>>>>>>>>> {
>>>>>>>>> +                                       if ($entry_interface eq
>>>>>>>>> "green") {
>>>>>>>>> +                                               print "<option
>>>>>>>>> value='green' selected='selected'
>>>>>>>>> {'green'}>$Lang::tr{'green'}</option>";
>>>>>>>>> +                                       } else { print "<option
>>>>>>>>> value='green' {'green'}>$Lang::tr{'green'}</option>";}
>>>>>>>>> +                               }
>>>>>>>>> +                               if ($netsettings{'BLUE_DEV'}) {
>>>>>>>>> +                                       if ($entry_interface eq
>>>>>>>>> "blue") { 
>>>>>>>>> +                                               print "<option
>>>>>>>>> value='blue' selected='selected'
>>>>>>>>> {'blue'}>$Lang::tr{'blue'}</option>";
>>>>>>>>> +                                       } else { print "<option
>>>>>>>>> value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
>>>>>>>>> +                               }
>>>>>>>>> +                               if ($netsettings{'ORANGE_DEV'})
>>>>>>>>> {
>>>>>>>>> +                                       if ($entry_interface eq
>>>>>>>>> "orange") { 
>>>>>>>>> +                                               print "<option
>>>>>>>>> value='orange' selected='selected'
>>>>>>>>> {'orange'}>$Lang::tr{'orange'}</option>";
>>>>>>>>> +                                       } else { print "<option
>>>>>>>>> value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
>>>>>>>>> +                               }
>>>>>>>>> +
>>>>>>>>> +                       print "</select><td><select
>>>>>>>>> style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
>>>>>>>>> +                       if ((!$entry_protocol) ||
>>>>>>>>> ($entry_protocol eq "tcp")) {
>>>>>>>>> +                               print "<option
>>>>>>>>> selected='selected' id='protocol' value='tcp'
>>>>>>>>> {'tcp'}>tcp</option>";
>>>>>>>>> +                               print "<option value='udp'
>>>>>>>>> {'udp'}>udp</option>";
>>>>>>>>> +                       } elsif ($entry_protocol eq "udp") {
>>>>>>>>> +                               print "<option value='tcp'
>>>>>>>>> {'tcp'}>tcp</option>";
>>>>>>>>> +                               print "<option
>>>>>>>>> selected='selected' value='udp' {'udp'}>udp</option>";
>>>>>>>>> +                       }
>>>>>>>>> +       print <<END;
>>>>>>>>> +                               </select></td>
>>>>>>>>> +                               <td><input type='text'
>>>>>>>>> name='REDIR_ENTRY_PORT'    value='$entry_port'   
>>>>>>>>> size='4'></td>
>>>>>>>>> +                               <td><input type='text'
>>>>>>>>> name='REDIR_ENTRY_ADDRESS' value='$entry_address'
>>>>>>>>> size='14'></td>
>>>>>>>>> +                               <td><input type='text'
>>>>>>>>> name='REDIR_ENTRY_REMARK'  value='$entry_remark' 
>>>>>>>>> size='35'></td>
>>>>>>>>> +                               <td width='10%'
>>>>>>>>> align='center'><input type='submit' name='ACTION' value=' 
>>>>>>>>> $buttontext  '></td>
>>>>>>>>> +                               <td class='base'></td>
>>>>>>>>> +                       </tr>
>>>>>>>>> +                       </form>
>>>>>>>>> +               </table>
>>>>>>>>> +       </div>
>>>>>>>>> +END
>>>>>>>>> +       &Header::closebox();
>>>>>>>>> +}
>>>>>>>>> diff --git a/config/rootfiles/common/misc-progs
>>>>>>>>> b/config/rootfiles/common/misc-progs
>>>>>>>>> index d6594b3f8..fbad2af8b 100644
>>>>>>>>> --- a/config/rootfiles/common/misc-progs
>>>>>>>>> +++ b/config/rootfiles/common/misc-progs
>>>>>>>>> @@ -17,6 +17,7 @@ usr/local/bin/logwatch
>>>>>>>>> #usr/local/bin/mpfirectrl
>>>>>>>>> usr/local/bin/openvpnctrl
>>>>>>>>> usr/local/bin/pakfire
>>>>>>>>> +#usr/local/bin/portredirctrl
>>>>>>>>> usr/local/bin/qosctrl
>>>>>>>>> usr/local/bin/rebuildhosts
>>>>>>>>> usr/local/bin/rebuildroutes
>>>>>>>>> diff --git a/config/rootfiles/packages/portredir
>>>>>>>>> b/config/rootfiles/packages/portredir
>>>>>>>>> new file mode 100644
>>>>>>>>> index 000000000..4b4ba8366
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/config/rootfiles/packages/portredir
>>>>>>>>> @@ -0,0 +1,11 @@
>>>>>>>>> +etc/rc.d/init.d/portredir
>>>>>>>>> +etc/rc.d/rc0.d/K77portredir
>>>>>>>>> +etc/rc.d/rc3.d/S23portredir
>>>>>>>>> +etc/rc.d/rc6.d/K77portredir
>>>>>>>>> +srv/web/ipfire/cgi-bin/portredir.cgi
>>>>>>>>> +usr/local/bin/portredirctrl
>>>>>>>>> +var/ipfire/addon-lang/portredir.de.pl
>>>>>>>>> +var/ipfire/addon-lang/portredir.en.pl
>>>>>>>>> +var/ipfire/backup/addons/includes/portredir
>>>>>>>>> +var/ipfire/menu.d/EX-portredir.menu
>>>>>>>>> +var/ipfire/portredir
>>>>>>>>> diff --git a/lfs/portredir b/lfs/portredir
>>>>>>>>> new file mode 100644
>>>>>>>>> index 000000000..a4911f71f
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/lfs/portredir
>>>>>>>>> @@ -0,0 +1,85 @@
>>>>>>>>> +##############################################################
>>>>>>>>> #################
>>>>>>>>> +#                                                             
>>>>>>>>>               #
>>>>>>>>> +# IPFire.org - A linux based
>>>>>>>>> firewall                                         #
>>>>>>>>> +# Copyright (C) 2007-2021  IPFire Team 
>>>>>>>>> <info@ipfire.org>                     #
>>>>>>>>> +#                                                             
>>>>>>>>>               #
>>>>>>>>> +# This program is free software: you can redistribute it
>>>>>>>>> and/or modify        #
>>>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>>>> published by        #
>>>>>>>>> +# the Free Software Foundation, either version 3 of the
>>>>>>>>> License, or           #
>>>>>>>>> +# (at your option) any later
>>>>>>>>> version.                                         #
>>>>>>>>> +#                                                             
>>>>>>>>>               #
>>>>>>>>> +# This program is distributed in the hope that it will be
>>>>>>>>> useful,             #
>>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>>>> of              #
>>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>>>> the               #
>>>>>>>>> +# GNU General Public License for more
>>>>>>>>> details.                                #
>>>>>>>>> +#                                                             
>>>>>>>>>               #
>>>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>>>> License           #
>>>>>>>>> +# along with this program.  If not, see <
>>>>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>>>>> +#                                                             
>>>>>>>>>               #
>>>>>>>>> +##############################################################
>>>>>>>>> #################
>>>>>>>>> +
>>>>>>>>> +##############################################################
>>>>>>>>> #################
>>>>>>>>> +# Definitions
>>>>>>>>> +##############################################################
>>>>>>>>> #################
>>>>>>>>> +
>>>>>>>>> +include Config
>>>>>>>>> +
>>>>>>>>> +VER        = 1.0
>>>>>>>>> +
>>>>>>>>> +THISAPP    = portredir-$(VER)
>>>>>>>>> +DIR_APP    = $(DIR_SRC)/$(THISAPP)
>>>>>>>>> +TARGET     = $(DIR_INFO)/$(THISAPP)
>>>>>>>>> +PROG       = portredir
>>>>>>>>> +PAK_VER    = 1
>>>>>>>>> +
>>>>>>>>> +##############################################################
>>>>>>>>> #################
>>>>>>>>> +# Top-level Rules
>>>>>>>>> +##############################################################
>>>>>>>>> #################
>>>>>>>>> +
>>>>>>>>> +install : $(TARGET)
>>>>>>>>> +
>>>>>>>>> +check :
>>>>>>>>> +
>>>>>>>>> +download :
>>>>>>>>> +
>>>>>>>>> +md5 :
>>>>>>>>> +
>>>>>>>>> +dist: 
>>>>>>>>> +       @$(PAK)
>>>>>>>>> +
>>>>>>>>> +##############################################################
>>>>>>>>> #################
>>>>>>>>> +# Installation Details
>>>>>>>>> +##############################################################
>>>>>>>>> #################
>>>>>>>>> +
>>>>>>>>> +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
>>>>>>>>> +       @$(PREBUILD)
>>>>>>>>> +       @rm -rf $(DIR_APP) && cd $(DIR_SRC)
>>>>>>>>> +
>>>>>>>>> +       #install cgi 
>>>>>>>>> +       install -v -m 755 $(DIR_CONF)/portredir/portredir.cgi
>>>>>>>>> /srv/web/ipfire/cgi-bin/
>>>>>>>>> +
>>>>>>>>> +       #create configuration dir 
>>>>>>>>> +       -mkdir -pv /var/ipfire/portredir/
>>>>>>>>> +       chown -R nobody:nobody /var/ipfire/portredir/
>>>>>>>>> +
>>>>>>>>> +       # Install include file for backup
>>>>>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/portredir-
>>>>>>>>> backup /var/ipfire/backup/addons/includes/portredir
>>>>>>>>> +
>>>>>>>>> +       # Install menu file
>>>>>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/EX-
>>>>>>>>> portredir.menu /var/ipfire/menu.d/
>>>>>>>>> +       chown nobody:nobody /var/ipfire/menu.d/EX-
>>>>>>>>> portredir.menu
>>>>>>>>> +
>>>>>>>>> +       # Install addon-specific language-files
>>>>>>>>> +       install -v -m 644
>>>>>>>>> $(DIR_CONF)/portredir/lang/portredir.*.pl /var/ipfire/addon-
>>>>>>>>> lang/
>>>>>>>>> +
>>>>>>>>> +       #install initscripts
>>>>>>>>> +       $(call INSTALL_INITSCRIPT,portredir)
>>>>>>>>> +
>>>>>>>>> +       # Create symlinks for runlevel interaction.
>>>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>>>> /etc/rc.d/rc3.d/S23portredir
>>>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>>>> /etc/rc.d/rc0.d/K77portredir
>>>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>>>> /etc/rc.d/rc6.d/K77portredir
>>>>>>>>> +
>>>>>>>>> +       @rm -rf $(DIR_APP)
>>>>>>>>> +       @$(POSTBUILD)
>>>>>>>>> +
>>>>>>>>> diff --git a/make.sh b/make.sh
>>>>>>>>> index fc03ebcd5..ab9fe881a 100755
>>>>>>>>> --- a/make.sh
>>>>>>>>> +++ b/make.sh
>>>>>>>>> @@ -1623,6 +1623,7 @@ buildipfire() {
>>>>>>>>> lfsmake2 socat
>>>>>>>>> lfsmake2 libcdada
>>>>>>>>> lfsmake2 pmacct
>>>>>>>>> +  lfsmake2 portredir
>>>>>>>>> }
>>>>>>>>> 
>>>>>>>>> buildinstaller() {
>>>>>>>>> diff --git a/src/initscripts/packages/portredir
>>>>>>>>> b/src/initscripts/packages/portredir
>>>>>>>>> new file mode 100644
>>>>>>>>> index 000000000..cc57fb9cc
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/src/initscripts/packages/portredir
>>>>>>>>> @@ -0,0 +1,191 @@
>>>>>>>>> +#!/bin/sh
>>>>>>>>> +##############################################################
>>>>>>>>> ##########
>>>>>>>>> +# Begin $rc_base/init.d/portredir
>>>>>>>>> +#
>>>>>>>>> +# Description : portredir init script for DNS/NTP and custom 
>>>>>>>>> +#              port redirection rules
>>>>>>>>> +#
>>>>>>>>> +##############################################################
>>>>>>>>> ##########
>>>>>>>>> +
>>>>>>>>> +. /etc/sysconfig/rc
>>>>>>>>> +. ${rc_functions}
>>>>>>>>> +
>>>>>>>>> +IPT="/sbin/iptables";
>>>>>>>>> +parent_chain="PREROUTING";
>>>>>>>>> +chain="PORT_REDIRECT";
>>>>>>>>> +
>>>>>>>>> +confdir="/var/ipfire/portredir";
>>>>>>>>> +settingsfile="${confdir}/settings";
>>>>>>>>> +redirectsfile="${confdir}/redirects";
>>>>>>>>> +SYSLOG="NO";
>>>>>>>>> +VERBOSE="NO";
>>>>>>>>> +
>>>>>>>>> +eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings);
>>>>>>>>> +eval $(/usr/local/bin/readhash ${settingsfile});
>>>>>>>>> +
>>>>>>>>> +logtext() {
>>>>>>>>> +       if [ "${SYSLOG}" = "YES" ]; then logger -t "portredir"
>>>>>>>>> ${1}; fi;
>>>>>>>>> +       if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
>>>>>>>>> +
>>>>>>>>> +create_chain() {
>>>>>>>>> +
>>>>>>>>> +       local line=$(${IPT} -t nat -L ${parent_chain} --line-
>>>>>>>>> numbers |grep "SQUID" |awk '{printf($1)}');
>>>>>>>>> +
>>>>>>>>> +       if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z
>>>>>>>>> "${REDIR_ENABLE_ADDON}" ]]; then
>>>>>>>>> +               logtext "addon not enabled in web
>>>>>>>>> interface...";
>>>>>>>>> +               echo "Portredir addon not enabled in web
>>>>>>>>> interface...";
>>>>>>>>> +               exit 0;
>>>>>>>>> +       fi;
>>>>>>>>> +
>>>>>>>>> +       if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>>>>>> ${chain})" ]; then
>>>>>>>>> +               ${IPT} -t nat -N ${chain};
>>>>>>>>> +
>>>>>>>>> +               if [ ! -z "${line}" ]; then
>>>>>>>>> +                       logtext "create chain ${chain} and link
>>>>>>>>> in ${parent_chain} at position ${line}...";
>>>>>>>>> +                       ${IPT} -t nat -I ${parent_chain}
>>>>>>>>> ${line} -j ${chain};
>>>>>>>>> +               else
>>>>>>>>> +                       logtext "create chain ${chain} and link
>>>>>>>>> in ${parent_chain} at last position...";
>>>>>>>>> +                       ${IPT} -t nat -A ${parent_chain} -j
>>>>>>>>> ${chain};
>>>>>>>>> +               fi
>>>>>>>>> +       else
>>>>>>>>> +               return 1;
>>>>>>>>> +       fi;
>>>>>>>>> +       return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +remove_chain() {
>>>>>>>>> +       if [ ! -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>>>>>> ${chain})" ]; then
>>>>>>>>> +               logtext "remove chain ${chain} and link in
>>>>>>>>> ${parent_chain} from system...";
>>>>>>>>> +               ${IPT} -t nat -D "${parent_chain}" -j ${chain};
>>>>>>>>> +               ${IPT} -t nat -F ${chain};
>>>>>>>>> +               ${IPT} -t nat -X ${chain};
>>>>>>>>> +       else
>>>>>>>>> +               return 1;
>>>>>>>>> +       fi;
>>>>>>>>> +       return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +activate_custom_redirections() {
>>>>>>>>> +       
>>>>>>>>> +       local array=();
>>>>>>>>> +       local redirects=();
>>>>>>>>> +       local i;
>>>>>>>>> +       index=();
>>>>>>>>> +       iface=();
>>>>>>>>> +       protocol=();
>>>>>>>>> +       port=();
>>>>>>>>> +       targetip=();
>>>>>>>>> +       enabled=();
>>>>>>>>> +
>>>>>>>>> +       IFS=$'\n' read -d '' -ra redirects < ${redirectsfile};
>>>>>>>>> +
>>>>>>>>> +       for i in "${!redirects[@]}"
>>>>>>>>> +       do
>>>>>>>>> +               IFS=$',' read -ra array <<< ${redirects[i]};
>>>>>>>>> +               index[i]=${array[0]};
>>>>>>>>> +               iface[i]=${array[1]};
>>>>>>>>> +               protocol[i]=${array[2]};
>>>>>>>>> +               port[i]=${array[3]};
>>>>>>>>> +               targetip[i]=${array[4]};
>>>>>>>>> +               enabled[i]=${array[5]};
>>>>>>>>> +       done
>>>>>>>>> +
>>>>>>>>> +       for i in "${!index[@]}"
>>>>>>>>> +       do
>>>>>>>>> +               if [[ ! -z "${GREEN_DEV}" &&  "${iface[i]}" =
>>>>>>>>> "green" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>>>> +
>>>>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>>>>> ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>>>>> ${port[i]} ";
>>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>>> ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>>> ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>>>> +               fi
>>>>>>>>> +               if [[ ! -z "${BLUE_DEV}" &&  "${iface[i]}" =
>>>>>>>>> "blue" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>>>>> ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>>>>> ${port[i]} ";
>>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>>> ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>>> ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>>>> +               fi
>>>>>>>>> +               if [[ ! -z "${ORANGE_DEV}" &&  "${iface[i]}" =
>>>>>>>>> "orange" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>>>>> ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>>>>> ${port[i]} ";
>>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>>> ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>>> ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>>>> +               fi
>>>>>>>>> +       done
>>>>>>>>> +       unset array redirects i index iface protocol port
>>>>>>>>> targetip enabled;
>>>>>>>>> +       return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +activate_redirections() {
>>>>>>>>> +
>>>>>>>>> +       if ! create_chain; then return 1; fi;
>>>>>>>>> +       
>>>>>>>>> +       # Force DNS REDIRECTs on GREEN (udp, tcp, 53)
>>>>>>>>> +       if [[ "${REDIR_DNS_GREEN}" == "on" && 
>>>>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>>>>> udp -m udp --dport domain -j REDIRECT;
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>>>>> ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>>>>> tcp -m tcp --dport domain -j REDIRECT;
>>>>>>>>> +       fi
>>>>>>>>> +
>>>>>>>>> +       # Force DNS REDIRECTs on BLUE (udp, tcp, 53)
>>>>>>>>> +       if [[ "${REDIR_DNS_BLUE}" == "on" && 
>>>>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>>>>>> -m udp --dport domain -j REDIRECT
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>>>>> ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p tcp
>>>>>>>>> -m tcp --dport domain -j REDIRECT
>>>>>>>>> +       fi
>>>>>>>>> +
>>>>>>>>> +       # Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
>>>>>>>>> +       if [[ "${REDIR_DNS_ORANGE}" == "on" && 
>>>>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>>>>> udp -m udp --dport domain -j REDIRECT
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>>>>> ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>>>>> tcp -m tcp --dport domain -j REDIRECT
>>>>>>>>> +       fi
>>>>>>>>> +
>>>>>>>>> +       # Force NTP REDIRECTs on GREEN (udp, 123)
>>>>>>>>> +       if [[ "${REDIR_NTP_GREEN}" == "on" && 
>>>>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>>>>> +       fi
>>>>>>>>> +
>>>>>>>>> +       # Force NTP REDIRECTs on BLUE (udp, 123)
>>>>>>>>> +       if [[ "${REDIR_NTP_BLUE}" == "on" && 
>>>>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>>>>>> -m udp --dport ntp -j REDIRECT
>>>>>>>>> +       fi
>>>>>>>>> +
>>>>>>>>> +       # Force NTP REDIRECTs on ORANGE (udp, 123)
>>>>>>>>> +       if [[ "${REDIR_NTP_ORANGE}" == "on" && 
>>>>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>>>>> +       fi
>>>>>>>>> +
>>>>>>>>> +       if ! activate_custom_redirections; then return 1; fi;
>>>>>>>>> +
>>>>>>>>> +       return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +case "${1}" in
>>>>>>>>> +       start)
>>>>>>>>> +               boot_mesg "Loading port redirections..."
>>>>>>>>> +               activate_redirections;
>>>>>>>>> +               evaluate_retval;
>>>>>>>>> +               ;;
>>>>>>>>> +
>>>>>>>>> +       stop)   
>>>>>>>>> +               boot_mesg "Removing port redirections..."
>>>>>>>>> +               remove_chain;
>>>>>>>>> +               evaluate_retval;
>>>>>>>>> +               ;;
>>>>>>>>> +
>>>>>>>>> +       restart)
>>>>>>>>> +               ${0} stop
>>>>>>>>> +               ${0} start
>>>>>>>>> +               ;;
>>>>>>>>> +
>>>>>>>>> +       *)
>>>>>>>>> +               echo "Usage: ${0} {start|stop|restart}"
>>>>>>>>> +               exit 1
>>>>>>>>> +               ;;
>>>>>>>>> +esac
>>>>>>>>> +
>>>>>>>>> +# End $rc_base/init.d/portredir
>>>>>>>>> diff --git a/src/misc-progs/Makefile b/src/misc-progs/Makefile
>>>>>>>>> index 7c3ef7529..850f8fdcc 100644
>>>>>>>>> --- a/src/misc-progs/Makefile
>>>>>>>>> +++ b/src/misc-progs/Makefile
>>>>>>>>> @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
>>>>>>>>>       wirelessctrl getipstat qosctrl \
>>>>>>>>>       redctrl syslogdctrl extrahdctrl sambactrl \
>>>>>>>>>       smartctrl clamavctrl addonctrl pakfire mpfirectrl
>>>>>>>>> wlanapctrl \
>>>>>>>>> -       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>>>>>> rebuildroutes \
>>>>>>>>> +       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>>>>>> rebuildroutes portredirctrl \
>>>>>>>>>       getconntracktable wirelessclient torctrl ddnsctrl
>>>>>>>>> unboundctrl \
>>>>>>>>>       captivectrl
>>>>>>>>> 
>>>>>>>>> diff --git a/src/misc-progs/portredirctrl.c b/src/misc-
>>>>>>>>> progs/portredirctrl.c
>>>>>>>>> new file mode 100644
>>>>>>>>> index 000000000..7897d711c
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/src/misc-progs/portredirctrl.c
>>>>>>>>> @@ -0,0 +1,47 @@
>>>>>>>>> +/* This file is part of the IPFire Firewall.
>>>>>>>>> + *
>>>>>>>>> + * This program is distributed under the terms of the GNU
>>>>>>>>> General Public
>>>>>>>>> + * Licence.  See the file COPYING for details.
>>>>>>>>> + *
>>>>>>>>> + */
>>>>>>>>> +
>>>>>>>>> +#include <stdlib.h>
>>>>>>>>> +#include <stdio.h>
>>>>>>>>> +#include <string.h>
>>>>>>>>> +#include <unistd.h>
>>>>>>>>> +#include <sys/types.h>
>>>>>>>>> +#include <fcntl.h>
>>>>>>>>> +#include "setuid.h"
>>>>>>>>> +
>>>>>>>>> +int main(int argc, char *argv[]) {
>>>>>>>>> +       if (!(initsetuid()))
>>>>>>>>> +               exit(1);
>>>>>>>>> +
>>>>>>>>> +       // Check what command is asked
>>>>>>>>> +        if (argc < 2) {
>>>>>>>>> +                fprintf(stderr, "\nNo argument
>>>>>>>>> given.\n\nportredirctrl
>>>>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>>>>> +                exit(1);
>>>>>>>>> +        }
>>>>>>>>> +
>>>>>>>>> +        if (strcmp(argv[1], "start") == 0) {
>>>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>>>> start");
>>>>>>>>> +        } else if (strcmp(argv[1], "stop") == 0) {
>>>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>>>> stop");
>>>>>>>>> +        } else if (strcmp(argv[1], "restart") == 0) {
>>>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>>>> restart");
>>>>>>>>> +       } else if (strcmp(argv[1], "enable") == 0) {
>>>>>>>>> +               safe_system("touch
>>>>>>>>> /var/ipfire/portredir/enable");
>>>>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>>>>> /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
>>>>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>>>>> /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
>>>>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>>>>> /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
>>>>>>>>> +       } else if (strcmp(argv[1], "disable") == 0) {
>>>>>>>>> +               safe_system("/etc/rc.d/init.d/portredir stop");
>>>>>>>>> +               safe_system("unlink
>>>>>>>>> /var/ipfire/portredir/enable");
>>>>>>>>> +               safe_system("rm -rf /etc/rc.d/rc*.d/*portredir
>>>>>>>>>> /dev/null 2>&1");
>>>>>>>>> +        } else {
>>>>>>>>> +                fprintf(stderr, "\nBad argument
>>>>>>>>> given.\n\nportredirctrl
>>>>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>>>>> +                exit(1);
>>>>>>>>> +        }
>>>>>>>>> +
>>>>>>>>> +       return 0;
>>>>>>>>> +}
>>>>>>>>> diff --git a/src/paks/portredir/install.sh
>>>>>>>>> b/src/paks/portredir/install.sh
>>>>>>>>> new file mode 100644
>>>>>>>>> index 000000000..9f69aeae2
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/src/paks/portredir/install.sh
>>>>>>>>> @@ -0,0 +1,32 @@
>>>>>>>>> +#!/bin/bash
>>>>>>>>> +##############################################################
>>>>>>>>> ##############
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +# This file is part of the IPFire
>>>>>>>>> Firewall.                                #
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>>>>> modify           #
>>>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>>>> published by     #
>>>>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>>>>> License, or        #
>>>>>>>>> +# (at your option) any later
>>>>>>>>> version.                                      #
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>>>>> useful,                #
>>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>>>> of           #
>>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>>>> the            #
>>>>>>>>> +# GNU General Public License for more
>>>>>>>>> details.                             #
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>>>> License        #
>>>>>>>>> +# along with IPFire; if not, write to the Free
>>>>>>>>> Software                    #
>>>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>>>>> 02111-1307 USA #
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +# Copyright (C) 2021 IPFire-Team
>>>>>>>>> <info@ipfire.org>.                        #
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +##############################################################
>>>>>>>>> ##############
>>>>>>>>> +#
>>>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>>>> +extract_files
>>>>>>>>> +restore_backup ${NAME}
>>>>>>>>> +
>>>>>>>>> +/usr/local/bin/update-lang-cache
>>>>>>>>> +
>>>>>>>>> +chown root:nobody /usr/local/bin/portredirctrl
>>>>>>>>> +chmod 4750 /usr/local/bin/portredirctrl
>>>>>>>>> +chmod u+s /usr/local/bin/portredirctrl
>>>>>>>>> diff --git a/src/paks/portredir/uninstall.sh
>>>>>>>>> b/src/paks/portredir/uninstall.sh
>>>>>>>>> new file mode 100644
>>>>>>>>> index 000000000..df9270125
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/src/paks/portredir/uninstall.sh
>>>>>>>>> @@ -0,0 +1,28 @@
>>>>>>>>> +#!/bin/bash
>>>>>>>>> +##############################################################
>>>>>>>>> ##############
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +# This file is part of the IPFire
>>>>>>>>> Firewall.                                #
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>>>>> modify           #
>>>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>>>> published by     #
>>>>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>>>>> License, or        #
>>>>>>>>> +# (at your option) any later
>>>>>>>>> version.                                      #
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>>>>> useful,                #
>>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>>>> of           #
>>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>>>> the            #
>>>>>>>>> +# GNU General Public License for more
>>>>>>>>> details.                             #
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>>>> License        #
>>>>>>>>> +# along with IPFire; if not, write to the Free
>>>>>>>>> Software                    #
>>>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>>>>> 02111-1307 USA #
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>>>>> <info@ipfire.org>.                        #
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +##############################################################
>>>>>>>>> ##############
>>>>>>>>> +#
>>>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>>>> +make_backup ${NAME}
>>>>>>>>> +remove_files
>>>>>>>>> +
>>>>>>>>> +/usr/local/bin/update-lang-cache
>>>>>>>>> diff --git a/src/paks/portredir/update.sh
>>>>>>>>> b/src/paks/portredir/update.sh
>>>>>>>>> new file mode 100644
>>>>>>>>> index 000000000..89c40d0d7
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/src/paks/portredir/update.sh
>>>>>>>>> @@ -0,0 +1,26 @@
>>>>>>>>> +#!/bin/bash
>>>>>>>>> +##############################################################
>>>>>>>>> ##############
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +# This file is part of the IPFire
>>>>>>>>> Firewall.                                #
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>>>>> modify           #
>>>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>>>> published by     #
>>>>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>>>>> License, or        #
>>>>>>>>> +# (at your option) any later
>>>>>>>>> version.                                      #
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>>>>> useful,                #
>>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>>>> of           #
>>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>>>> the            #
>>>>>>>>> +# GNU General Public License for more
>>>>>>>>> details.                             #
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>>>> License        #
>>>>>>>>> +# along with IPFire; if not, write to the Free
>>>>>>>>> Software                    #
>>>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>>>>> 02111-1307 USA #
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>>>>> <info@ipfire.org>.                        #
>>>>>>>>> +#                                                             
>>>>>>>>>            #
>>>>>>>>> +##############################################################
>>>>>>>>> ##############
>>>>>>>>> +#
>>>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>>>> +./uninstall.sh
>>>>>>>>> +./install.sh
>>>>>>>>> -- 
>>>>>>>>> 2.18.0
>>>>>>>>> 
>>>>>>>> 
>>>>>>> 
>>>>>> 
>>>>> 
>>>> 
>>> 
>> 
>
  
Jon Murphy July 3, 2021, 2:57 a.m. UTC | #10
Stefan,

Please let me know if I should enter this (and the one below) in bugzilla.

On other issue (small).  If I disable logging (and click apply changes) the logging does not stop. 



(One image enclosed)

So when the DNS redirect firewall rule is on, then there are bazillions of log entries.  Not sure how to disable.  

Also, it looks like the iptables are correct.  Looking at the 2nd image: the left side - logging disabled.  Right is logging enabled.



(One more image enclosed)


Jon




> On Jul 1, 2021, at 10:55 AM, Jon Murphy <jcmurphy26@gmail.com> wrote:
> 
> Hmmm.  I tried an ALL (a.k.a ANY) instead and I think it works.  But there is a bug in the UI.
> 
> 
> This is what I tried:
> 
> <Screen Shot 2021-07-01 at 10.48.15 AM.png>
> 
> <Screen Shot 2021-07-01 at 10.50.43 AM.png>
> 
> (Two images enclosed)
> 
> 
> But, when I go to edit this rule the Destination Firewall changes from ALL to GREEN.  So I thought it wasn’t accepted.
> 
> <Screen Shot 2021-07-01 at 10.52.05 AM.png>
> 
> (One more image enclosed)
> 
> 
> Jon
> 
> 
> 
>> On Jul 1, 2021, at 10:24 AM, Michael Tremer <michael.tremer@ipfire.org <mailto:michael.tremer@ipfire.org>> wrote:
>> 
>> @Stefan: There should be an exception in the UI that these rules can be created when REDIRECT is being used.
>> 
>>> On 1 Jul 2021, at 16:04, Jon Murphy <jcmurphy26@gmail.com <mailto:jcmurphy26@gmail.com>> wrote:
>>> 
>>>> You probably want “any” as destination.
>>> 
>>> Those are the only choices that allow a Save.  When I enter an IP address I get this error:
>>> 
>>> 
>>> Error messages
>>> 
>>> Source and destination IP addresses are from the same subnet.  
>>> 
>>> 
>>> 
>>> Jon
>>> 
>>>> On Jul 1, 2021, at 3:08 AM, Michael Tremer <michael.tremer@ipfire.org <mailto:michael.tremer@ipfire.org>> wrote:
>>>> 
>>>> Hey Jon,
>>>> 
>>>> You probably want “any” as destination.
>>>> 
>>>> -Michael
>>>> 
>>>>> On 1 Jul 2021, at 04:08, Jon Murphy <jcmurphy26@gmail.com <mailto:jcmurphy26@gmail.com>> wrote:
>>>>> 
>>>>> Hi Stefan,
>>>>> 
>>>>> Thank you for taking this on!
>>>>> 
>>>>> I applied the patchwork.ipfire patch.
>>>>> 
>>>>> I think I entered something wrong since I cannot get things to work.  I tried both with Destination Firewall GREEN & Firewall RED.
>>>>> 
>>>>> 
>>>>> Does the Firewall Rule seem right?
>>>>> 
>>>>> Best regards,
>>>>> Jon
>>>>> 
>>>>> 
>>>>> Here is the rule I set up:
>>>>> 
>>>>> 
>>>>> <Screen Shot 2021-06-30 at 9.53.52 PM.png>
>>>>> 
>>>>> <Screen Shot 2021-06-30 at 9.53.10 PM.png>
>>>>> 
>>>>> 
>>>>> And this is what I see with conntrack:
>>>>> 
>>>>> conntrack -E -e NEW,UPDATE | grep -e "=53 "
>>>>> 
>>>>>   [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=51169 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=51169
>>>>>   [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=54168 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=54168
>>>>>   [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=56094 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=56094
>>>>>   [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=52964 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=52964
>>>>>   [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=53279 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=53279
>>>>>   [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=61657 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=61657
>>>>>   [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=57723 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=57723
>>>>> 
>>>>> 
>>>>> 
>>>>> 
>>>>>> On Jun 30, 2021, at 2:14 PM, Stefan Schantl <stefan.schantl@ipfire.org <mailto:stefan.schantl@ipfire.org>> wrote:
>>>>>> 
>>>>>> Hello Matthias, Hello Michael, Hello Jon, Hello *,
>>>>>> 
>>>>>> I've followed the conversation on this list since the first mail and
>>>>>> thoughts about forcing DNS traffic to use the local resolver.
>>>>>> 
>>>>>> It was a very long journey and lot of time and work has been spent to
>>>>>> get to the present point.
>>>>>> 
>>>>>> As Michael requested here, I've digged through the lines of the perl
>>>>>> script which is responsible for creating the firewall rules and
>>>>>> surprisingly found that everyting which is needed to create generic
>>>>>> REDIRECT rules already was written in the past - it just did not work
>>>>>> as designed/expected.
>>>>>> 
>>>>>> Finaly I was able to adjust these lines of code and to repair that
>>>>>> feature.
>>>>>> 
>>>>>> A redirect rule can be created by picking a single host or group of
>>>>>> hosts or entire network(s) as source, selecting NAT (DNAT) and choosing
>>>>>> the Firewall itself as target.
>>>>>> 
>>>>>> The protocol or service or service group which should be redirected has
>>>>>> to be selected afterwards. If you want to redirect a given port to
>>>>>> another one it can be specified as "Target port".
>>>>>> 
>>>>>> All created redirect rules are displayed as "input rules".
>>>>>> 
>>>>>> 
>>>>>> The patch directly can be accessed here:
>>>>>> 
>>>>>> https://patchwork.ipfire.org/project/ipfire/patch/20210630184031.7726-1-stefan.schantl@ipfire.org/ <https://patchwork.ipfire.org/project/ipfire/patch/20210630184031.7726-1-stefan.schantl@ipfire.org/>
>>>>>> 
>>>>>> Best regards,
>>>>>> 
>>>>>> -Stefan
>>>>>> 
>>>>>>> Hello,
>>>>>>> 
>>>>>>>> On 28 Jun 2021, at 18:53, Jon Murphy <jcmurphy26@gmail.com> wrote:
>>>>>>>> 
>>>>>>>> Hi Michael!  Happy Monday!
>>>>>>>> 
>>>>>>>> 
>>>>>>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>>>>>>> of code instead of adding many hundreds of lines?
>>>>>>>>> 
>>>>>>>>> Please can someone elaborate on this more?
>>>>>>>> 
>>>>>>>> Doing a DNS redirect, via the WegBUI, has been an issue since
>>>>>>>> 2015.  I found this quote in the old forum:
>>>>>>>> 
>>>>>>>> "Having investigated a bit more I have concluded that it's not
>>>>>>>> currently possible to create such rules through the WUI.
>>>>>>>> 
>>>>>>>> There are a number of obstacles:
>>>>>>>> 1. It is not allowed to create a rule where source IP and
>>>>>>>> destination nat IP is on the same subnetwork (e.g. GREEN), WUI
>>>>>>>> error message: "Source and destination IP addresses are from the
>>>>>>>> same subnet."
>>>>>>>> 
>>>>>>>> 2. WUI will not allow you to create a rule without a destination
>>>>>>>> (the filtered packet must adhere to a destination, not only a port)
>>>>>>>> and the destination MUST be an IP address of one of the IPFire
>>>>>>>> interfaces, which limits whats possible a great deal." 
>>>>>>> 
>>>>>>> And these cannot be changed?
>>>>>>> 
>>>>>>>> And I found this from 2016:
>>>>>>>> https://bugzilla.ipfire.org/show_bug.cgi?id=11168
>>>>>>>> 
>>>>>>>> So I am guessing that no one has been able to determine a way to
>>>>>>>> extend the WebGUI.  
>>>>>>> 
>>>>>>> Has anyone tried? I do not see any obvious reasons why this should
>>>>>>> not be possible.
>>>>>>> 
>>>>>>>> I am curious - Who created the 
>>>>>>>> https://ipfire:444/cgi-bin/firewall.cgi page?  And could they help?
>>>>>>> 
>>>>>>> -Michael
>>>>>>> 
>>>>>>>> Jon
>>>>>>>> 
>>>>>>>> 
>>>>>>>>> On Jun 28, 2021, at 11:04 AM, Michael Tremer <
>>>>>>>>> michael.tremer@ipfire.org> wrote:
>>>>>>>>> 
>>>>>>>>> Hello Matthias,
>>>>>>>>> 
>>>>>>>>>> On 27 Jun 2021, at 14:48, Matthias Fischer <
>>>>>>>>>> matthias.fischer@ipfire.org> wrote:
>>>>>>>>>> 
>>>>>>>>>> From: Marcel Lorenz <marcel.lorenz@ipfire.org>
>>>>>>>>> 
>>>>>>>>> Thank you for sending this patch on Marcel’s behalf, but I would
>>>>>>>>> much more prefer if he would submit his patches on his own. I do
>>>>>>>>> not see why that isn’t possible.
>>>>>>>>> 
>>>>>>>>>> Please note:
>>>>>>>>>> This is a new addon written by Marcel Lorenz <
>>>>>>>>>> marcel.lorenz@ipfire.org>.
>>>>>>>>>> 
>>>>>>>>>> It adds a new GUI to IPFire for DNS/NTP *and* user specific
>>>>>>>>>> port redirections.
>>>>>>>>>> 
>>>>>>>>>> How its working:
>>>>>>>>>> It has exactly the same functionalities as "Forcing
>>>>>>>>>> DNS/NTP..."  - and some more.
>>>>>>>>>> 
>>>>>>>>>> By setting switches, DNS/NTP requests are automatically
>>>>>>>>>> redirected to the local IPFire DNS/NTP servers.
>>>>>>>>>> 
>>>>>>>>>> Additionally, the user can specify custom redirections.
>>>>>>>>>> 
>>>>>>>>>> These rules are added to a new chain in PREROUTING =>
>>>>>>>>>> PORT_REDIRECT.
>>>>>>>>>> 
>>>>>>>>>> To avoid problems with (e.g.) transparent 'squid'
>>>>>>>>>> configurations,
>>>>>>>>>> redirection rules are added automatically before existing
>>>>>>>>>> 'squid' rules.
>>>>>>>>> 
>>>>>>>>> This message does unfortunately not say why this add-on would be
>>>>>>>>> useful. I am emphasising this again and again that it is not very
>>>>>>>>> important how something is done specially. That should be
>>>>>>>>> commented in the code and other implementation details should
>>>>>>>>> also be documented there.
>>>>>>>>> 
>>>>>>>>> As I have stated on this functionality many times before, I do
>>>>>>>>> not see why this is necessary at all.
>>>>>>>>> 
>>>>>>>>> Why is this an add-on?
>>>>>>>>> 
>>>>>>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>>>>>>> of code instead of adding many hundreds of lines?
>>>>>>>>> 
>>>>>>>>> Please can someone elaborate on this more?
>>>>>>>>> 
>>>>>>>>> -Michael
>>>>>>>>> 
>>>>>>>>>> Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
>>>>>>>>>> ---
>>>>>>>>>> config/portredir/EX-portredir.menu    |   6 +
>>>>>>>>>> config/portredir/lang/portredir.de.pl |  19 +
>>>>>>>>>> config/portredir/lang/portredir.en.pl |  19 +
>>>>>>>>>> config/portredir/portredir-backup     |   1 +
>>>>>>>>>> config/portredir/portredir.cgi        | 525
>>>>>>>>>> ++++++++++++++++++++++++++
>>>>>>>>>> config/rootfiles/common/misc-progs    |   1 +
>>>>>>>>>> config/rootfiles/packages/portredir   |  11 +
>>>>>>>>>> lfs/portredir                         |  85 +++++
>>>>>>>>>> make.sh                               |   1 +
>>>>>>>>>> src/initscripts/packages/portredir    | 191 ++++++++++
>>>>>>>>>> src/misc-progs/Makefile               |   2 +-
>>>>>>>>>> src/misc-progs/portredirctrl.c        |  47 +++
>>>>>>>>>> src/paks/portredir/install.sh         |  32 ++
>>>>>>>>>> src/paks/portredir/uninstall.sh       |  28 ++
>>>>>>>>>> src/paks/portredir/update.sh          |  26 ++
>>>>>>>>>> 15 files changed, 993 insertions(+), 1 deletion(-)
>>>>>>>>>> create mode 100644 config/portredir/EX-portredir.menu
>>>>>>>>>> create mode 100644 config/portredir/lang/portredir.de.pl
>>>>>>>>>> create mode 100644 config/portredir/lang/portredir.en.pl
>>>>>>>>>> create mode 100644 config/portredir/portredir-backup
>>>>>>>>>> create mode 100644 config/portredir/portredir.cgi
>>>>>>>>>> create mode 100644 config/rootfiles/packages/portredir
>>>>>>>>>> create mode 100644 lfs/portredir
>>>>>>>>>> create mode 100644 src/initscripts/packages/portredir
>>>>>>>>>> create mode 100644 src/misc-progs/portredirctrl.c
>>>>>>>>>> create mode 100644 src/paks/portredir/install.sh
>>>>>>>>>> create mode 100644 src/paks/portredir/uninstall.sh
>>>>>>>>>> create mode 100644 src/paks/portredir/update.sh
>>>>>>>>>> 
>>>>>>>>>> diff --git a/config/portredir/EX-portredir.menu
>>>>>>>>>> b/config/portredir/EX-portredir.menu
>>>>>>>>>> new file mode 100644
>>>>>>>>>> index 000000000..8376e8053
>>>>>>>>>> --- /dev/null
>>>>>>>>>> +++ b/config/portredir/EX-portredir.menu
>>>>>>>>>> @@ -0,0 +1,6 @@
>>>>>>>>>> +    $subfirewall->{'95.portredir'} = {
>>>>>>>>>> +                               'caption' =>
>>>>>>>>>> $Lang::tr{'portredir port redirections'},
>>>>>>>>>> +                               'uri' => '/cgi-
>>>>>>>>>> bin/portredir.cgi',
>>>>>>>>>> +                               'title' =>
>>>>>>>>>> "$Lang::tr{'portredir port redirections'}",
>>>>>>>>>> +                               'enabled' => 1
>>>>>>>>>> +                               };
>>>>>>>>>> diff --git a/config/portredir/lang/portredir.de.pl
>>>>>>>>>> b/config/portredir/lang/portredir.de.pl
>>>>>>>>>> new file mode 100644
>>>>>>>>>> index 000000000..b932d4a85
>>>>>>>>>> --- /dev/null
>>>>>>>>>> +++ b/config/portredir/lang/portredir.de.pl
>>>>>>>>>> @@ -0,0 +1,19 @@
>>>>>>>>>> +%tr = (
>>>>>>>>>> +%tr,
>>>>>>>>>> +'portredir enable addon' => 'Addon aktivieren',
>>>>>>>>>> +'portredir common settings' => 'Allgemeine Einstellungen',
>>>>>>>>>> +'portredir port redirections' => 'Portumleitungen',
>>>>>>>>>> +'portredir fw for interface' => 'Firewalloptionen für das
>>>>>>>>>> Interface',
>>>>>>>>>> +'portredir enable user redirections' => 'Aktiviere
>>>>>>>>>> benutzerdefinierte Portumleitungen',
>>>>>>>>>> +'portredir force local dns' => 'Erzwinge lokale DNS-Server',
>>>>>>>>>> +'portredir force local ntp' => 'Erzwinge lokale NTP-Server',
>>>>>>>>>> +'portredir custom redirections' => 'Benutzerdefinierte
>>>>>>>>>> Portumleitungen',
>>>>>>>>>> +'portredir remove rule' => 'Entferne Regel',
>>>>>>>>>> +'portredir add rule' => 'Hinzufügen',
>>>>>>>>>> +'portredir no entries' => 'Keine Einträge vorhanden.',
>>>>>>>>>> +'portredir invalid address' => 'Ungültige Host-Addresse.',
>>>>>>>>>> +'portredir empty input' => 'Fehlende Angabe: Bitte geben Sie
>>>>>>>>>> einen gültigen Host an.',
>>>>>>>>>> +'portredir save to activate' => 'Speichern, um Änderungen zu
>>>>>>>>>> aktivieren',
>>>>>>>>>> +);
>>>>>>>>>> +
>>>>>>>>>> +#EOF
>>>>>>>>>> diff --git a/config/portredir/lang/portredir.en.pl
>>>>>>>>>> b/config/portredir/lang/portredir.en.pl
>>>>>>>>>> new file mode 100644
>>>>>>>>>> index 000000000..f442f3eaa
>>>>>>>>>> --- /dev/null
>>>>>>>>>> +++ b/config/portredir/lang/portredir.en.pl
>>>>>>>>>> @@ -0,0 +1,19 @@
>>>>>>>>>> +%tr = (
>>>>>>>>>> +%tr,
>>>>>>>>>> +'portredir enable addon' => 'Enable addon',
>>>>>>>>>> +'portredir common settings' => 'Common settings',
>>>>>>>>>> +'portredir port redirections' => 'Port redirections',
>>>>>>>>>> +'portredir fw for interface' => 'Firewall options for
>>>>>>>>>> interface',
>>>>>>>>>> +'portredir enable user redirections' => 'Enable user port
>>>>>>>>>> redirections',
>>>>>>>>>> +'portredir force local dns' => 'Enforce local DNS servers',
>>>>>>>>>> +'portredir force local ntp' => 'Enforce local NTP servers',
>>>>>>>>>> +'portredir custom redirections' => 'Custom port redirections',
>>>>>>>>>> +'portredir remove rule' => 'Remove rule',
>>>>>>>>>> +'portredir add rule' => 'Add new',
>>>>>>>>>> +'portredir no entries' => 'No entries at the moment.',
>>>>>>>>>> +'portredir invalid address' => 'Invalid host address.',
>>>>>>>>>> +'portredir empty input' => 'Empty input: Please enter a valid
>>>>>>>>>> host.',
>>>>>>>>>> +'portredir save to activate' => 'Save to activate changes',
>>>>>>>>>> +);
>>>>>>>>>> +
>>>>>>>>>> +#EOF
>>>>>>>>>> diff --git a/config/portredir/portredir-backup
>>>>>>>>>> b/config/portredir/portredir-backup
>>>>>>>>>> new file mode 100644
>>>>>>>>>> index 000000000..bd2ada742
>>>>>>>>>> --- /dev/null
>>>>>>>>>> +++ b/config/portredir/portredir-backup
>>>>>>>>>> @@ -0,0 +1 @@
>>>>>>>>>> +/var/ipfire/portredir
>>>>>>>>>> diff --git a/config/portredir/portredir.cgi
>>>>>>>>>> b/config/portredir/portredir.cgi
>>>>>>>>>> new file mode 100644
>>>>>>>>>> index 000000000..4913dda3f
>>>>>>>>>> --- /dev/null
>>>>>>>>>> +++ b/config/portredir/portredir.cgi
>>>>>>>>>> @@ -0,0 +1,525 @@
>>>>>>>>>> +#!/usr/bin/perl
>>>>>>>>>> +##############################################################
>>>>>>>>>> #################
>>>>>>>>>> +#                                                             
>>>>>>>>>>               #
>>>>>>>>>> +# IPFire.org - A linux based
>>>>>>>>>> firewall                                         #
>>>>>>>>>> +# Copyright (C) 2021  IPFire Team 
>>>>>>>>>> <info@ipfire.org>                          #
>>>>>>>>>> +#                                                             
>>>>>>>>>>               #
>>>>>>>>>> +# This program is free software: you can redistribute it
>>>>>>>>>> and/or modify        #
>>>>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>>>>> published by        #
>>>>>>>>>> +# the Free Software Foundation, either version 3 of the
>>>>>>>>>> License, or           #
>>>>>>>>>> +# (at your option) any later
>>>>>>>>>> version.                                         #
>>>>>>>>>> +#                                                             
>>>>>>>>>>               #
>>>>>>>>>> +# This program is distributed in the hope that it will be
>>>>>>>>>> useful,             #
>>>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>>>>> of              #
>>>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>>>>> the               #
>>>>>>>>>> +# GNU General Public License for more
>>>>>>>>>> details.                                #
>>>>>>>>>> +#                                                             
>>>>>>>>>>               #
>>>>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>>>>> License           #
>>>>>>>>>> +# along with this program.  If not, see <
>>>>>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>>>>>> +#                                                             
>>>>>>>>>>               #
>>>>>>>>>> +##############################################################
>>>>>>>>>> #################
>>>>>>>>>> +
>>>>>>>>>> +use strict;
>>>>>>>>>> +
>>>>>>>>>> +# enable only the following on debugging purpose
>>>>>>>>>> +use warnings;
>>>>>>>>>> +use CGI::Carp 'fatalsToBrowser';
>>>>>>>>>> +
>>>>>>>>>> +require '/var/ipfire/general-functions.pl';
>>>>>>>>>> +require "${General::swroot}/lang.pl";
>>>>>>>>>> +require "${General::swroot}/header.pl";
>>>>>>>>>> +
>>>>>>>>>> +# File declarations
>>>>>>>>>> +my $settingsfile = "${General::swroot}/portredir/settings";
>>>>>>>>>> +my $redirectsfile = "${General::swroot}/portredir/redirects";
>>>>>>>>>> +
>>>>>>>>>> +# Create empty settingsfiles if they does not exist yet
>>>>>>>>>> +unless (-e "$settingsfile") { system ("touch $settingsfile");
>>>>>>>>>> }
>>>>>>>>>> +unless (-e "$redirectsfile") { system ("touch
>>>>>>>>>> $redirectsfile"); }
>>>>>>>>>> +
>>>>>>>>>> +# load ipfire settings
>>>>>>>>>> +our %netsettings = ();
>>>>>>>>>> +our %color = ();
>>>>>>>>>> +&General::readhash("${General::swroot}/ethernet/settings",
>>>>>>>>>> \%netsettings);
>>>>>>>>>> +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include
>>>>>>>>>> /colors.txt", \%color);
>>>>>>>>>> +
>>>>>>>>>> +my %settings=();
>>>>>>>>>> +my %portredirs=();
>>>>>>>>>> +my %checked=(); # Checkbox manipulations
>>>>>>>>>> +my $errormessage='';
>>>>>>>>>> +my %selected=();
>>>>>>>>>> +our %redirects=();
>>>>>>>>>> +
>>>>>>>>>> +$settings{'ACTION'} = '';
>>>>>>>>>> +$settings{'REDIR_ENABLE_ADDON'}="off";
>>>>>>>>>> +$settings{'REDIR_CUSTOM_GREEN'}="off";
>>>>>>>>>> +$settings{'REDIR_CUSTOM_BLUE'}="off";
>>>>>>>>>> +$settings{'REDIR_CUSTOM_ORANGE'}="off";
>>>>>>>>>> +$settings{'REDIR_DNS_GREEN'}="off";
>>>>>>>>>> +$settings{'REDIR_NTP_GREEN'}="off";
>>>>>>>>>> +$settings{'REDIR_DNS_BLUE'}="off";
>>>>>>>>>> +$settings{'REDIR_NTP_BLUE'}="off";
>>>>>>>>>> +$settings{'REDIR_DNS_ORANGE'}="off";
>>>>>>>>>> +$settings{'REDIR_NTP_ORANGE'}="off";
>>>>>>>>>> +
>>>>>>>>>> +&Header::showhttpheaders();
>>>>>>>>>> +
>>>>>>>>>> +# Get GUI values
>>>>>>>>>> +&Header::getcgihash(\%settings);
>>>>>>>>>> +
>>>>>>>>>> +# Save action
>>>>>>>>>> +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
>>>>>>>>>> +
>>>>>>>>>> +       # If custom rules enabled, deactivate default rules on
>>>>>>>>>> interface
>>>>>>>>>> +       if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
>>>>>>>>>> +               $settings{'REDIR_DNS_GREEN'}="off";
>>>>>>>>>> +               $settings{'REDIR_NTP_GREEN'}="off";
>>>>>>>>>> +       }
>>>>>>>>>> +
>>>>>>>>>> +       if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
>>>>>>>>>> +               $settings{'REDIR_DNS_BLUE'}="off";
>>>>>>>>>> +               $settings{'REDIR_NTP_BLUE'}="off";
>>>>>>>>>> +       }
>>>>>>>>>> +
>>>>>>>>>> +       if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
>>>>>>>>>> +               $settings{'REDIR_DNS_ORANGE'}="off";
>>>>>>>>>> +               $settings{'REDIR_NTP_ORANGE'}="off";
>>>>>>>>>> +       }
>>>>>>>>>> +
>>>>>>>>>> +       &General::writehash($settingsfile, \%settings);
>>>>>>>>>> +
>>>>>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
>>>>>>>>>> +               system ('/usr/local/bin/portredirctrl restart
>>>>>>>>>>> /dev/null 2>&1');
>>>>>>>>>> +               system ('/usr/local/bin/portredirctrl enable
>>>>>>>>>>> /dev/null 2>&1');
>>>>>>>>>> +               &General::log('portredir addon: port
>>>>>>>>>> redirections enabled');
>>>>>>>>>> +       }
>>>>>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
>>>>>>>>>> +               system ('/usr/local/bin/portredirctrl disable
>>>>>>>>>>> /dev/null 2>&1');
>>>>>>>>>> +               system ('/usr/local/bin/portredirctrl stop
>>>>>>>>>>> /dev/null 2>&1');
>>>>>>>>>> +               &General::log('portredir addon: port
>>>>>>>>>> redirections disabled');
>>>>>>>>>> +       }
>>>>>>>>>> +
>>>>>>>>>> +# Add/edit an entry to the redirectsfile.
>>>>>>>>>> +
>>>>>>>>>> +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) ||
>>>>>>>>>> ($settings{'ACTION'} eq $Lang::tr{'update'})) {
>>>>>>>>>> +
>>>>>>>>>> +       # Check if any input has been performed.
>>>>>>>>>> +       if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
>>>>>>>>>> +
>>>>>>>>>> +               # Check if the given input is no valid IP-
>>>>>>>>>> address, display an error message.
>>>>>>>>>> +               if
>>>>>>>>>> (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
>>>>>>>>>> +                       $errormessage = "$Lang::tr{'portredir
>>>>>>>>>> invalid address'}";
>>>>>>>>>> +               }
>>>>>>>>>> +       } else {
>>>>>>>>>> +               $errormessage = "$Lang::tr{'portredir empty
>>>>>>>>>> input'}";
>>>>>>>>>> +       }
>>>>>>>>>> +
>>>>>>>>>> +       # Go further if there was no error.
>>>>>>>>>> +       if ($errormessage eq '') {
>>>>>>>>>> +               my %redirects = ();
>>>>>>>>>> +               my $id;
>>>>>>>>>> +               my $status;
>>>>>>>>>> +
>>>>>>>>>> +               # Assign hash values.
>>>>>>>>>> +               my $new_entry_interface =
>>>>>>>>>> $settings{'REDIR_ENTRY_INTERFACE'};
>>>>>>>>>> +               my $new_entry_protocol =
>>>>>>>>>> $settings{'REDIR_ENTRY_PROTOCOL'};
>>>>>>>>>> +               my $new_entry_port =
>>>>>>>>>> $settings{'REDIR_ENTRY_PORT'};
>>>>>>>>>> +               my $new_entry_address =
>>>>>>>>>> $settings{'REDIR_ENTRY_ADDRESS'};
>>>>>>>>>> +               my $new_entry_remark =
>>>>>>>>>> $settings{'REDIR_ENTRY_REMARK'};
>>>>>>>>>> +
>>>>>>>>>> +               # Read-in redirectsfile.
>>>>>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>>>>>> \%redirects);
>>>>>>>>>> +
>>>>>>>>>> +               # Check if we should edit an existing entry and
>>>>>>>>>> got an ID.
>>>>>>>>>> +               if (($settings{'ACTION'} eq
>>>>>>>>>> $Lang::tr{'update'}) && ($settings{'ID'})) {
>>>>>>>>>> +                       # Assin the provided id.
>>>>>>>>>> +                       $id = $settings{'ID'};
>>>>>>>>>> +
>>>>>>>>>> +                       # Undef the given ID.
>>>>>>>>>> +                       undef($settings{'ID'});
>>>>>>>>>> +
>>>>>>>>>> +                       # Grab the configured status of the
>>>>>>>>>> corresponding entry.
>>>>>>>>>> +                       $status = $redirects{$id}[4];
>>>>>>>>>> +               } else {
>>>>>>>>>> +                       # Each newly added entry automatically
>>>>>>>>>> should be enabled.
>>>>>>>>>> +                       $status = "enabled";
>>>>>>>>>> +
>>>>>>>>>> +                       # Generate the ID for the new entry.
>>>>>>>>>> +                       #
>>>>>>>>>> +                       # Sort the keys by their ID and store
>>>>>>>>>> them in an array.
>>>>>>>>>> +                       my @keys = sort { $a <=> $b } keys
>>>>>>>>>> %redirects;
>>>>>>>>>> +
>>>>>>>>>> +                       # Reverse the key array.
>>>>>>>>>> +                       my @reversed = reverse(@keys);
>>>>>>>>>> +
>>>>>>>>>> +                       # Obtain the last used id.
>>>>>>>>>> +                       my $last_id = @reversed[0];
>>>>>>>>>> +
>>>>>>>>>> +                       # Increase the last id by one and use
>>>>>>>>>> it as id for the new entry.
>>>>>>>>>> +                       $id = ++$last_id;
>>>>>>>>>> +               }
>>>>>>>>>> +
>>>>>>>>>> +               # Add/Modify the entry to/in the redirects
>>>>>>>>>> hash.
>>>>>>>>>> +               $redirects{$id} = ["$new_entry_interface",
>>>>>>>>>> "$new_entry_protocol", "$new_entry_port",
>>>>>>>>>> "$new_entry_address","$status", "$new_entry_remark"];
>>>>>>>>>> +
>>>>>>>>>> +               # Write the changed redirects hash to the
>>>>>>>>>> redirects file.
>>>>>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>>>>>> \%redirects);
>>>>>>>>>> +       }
>>>>>>>>>> +
>>>>>>>>>> +# Toggle Enabled/Disabled for an existing entry on the
>>>>>>>>>> redirects list.
>>>>>>>>>> +
>>>>>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable
>>>>>>>>>> disable'}) {
>>>>>>>>>> +       my %redirects = ();
>>>>>>>>>> +
>>>>>>>>>> +       # Only go further, if an ID has been passed.
>>>>>>>>>> +       if ($settings{'ID'}) {
>>>>>>>>>> +               # Assign the given ID.
>>>>>>>>>> +               my $id = $settings{'ID'};
>>>>>>>>>> +
>>>>>>>>>> +               # Undef the given ID.
>>>>>>>>>> +               undef($settings{'ID'});
>>>>>>>>>> +
>>>>>>>>>> +               # Read-in ignoredfile.
>>>>>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>>>>>> \%redirects);
>>>>>>>>>> +
>>>>>>>>>> +               # Grab the configured status of the
>>>>>>>>>> corresponding entry.
>>>>>>>>>> +               my $status = $redirects{$id}[4];
>>>>>>>>>> +
>>>>>>>>>> +               # Switch the status.
>>>>>>>>>> +               if ($status eq "disabled") {
>>>>>>>>>> +                       $status = "enabled";
>>>>>>>>>> +               } else {
>>>>>>>>>> +                       $status = "disabled";
>>>>>>>>>> +               }
>>>>>>>>>> +
>>>>>>>>>> +               # Modify the status of the existing entry.
>>>>>>>>>> +               $redirects{$id} = ["$redirects{$id}[0]",
>>>>>>>>>> "$redirects{$id}[1]", "$redirects{$id}[2]",
>>>>>>>>>> "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
>>>>>>>>>> +
>>>>>>>>>> +               # Write the changed ignored hash to the
>>>>>>>>>> redirects file.
>>>>>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>>>>>> \%redirects);
>>>>>>>>>> +       }
>>>>>>>>>> +
>>>>>>>>>> +# Remove entry from redirects list.
>>>>>>>>>> +
>>>>>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
>>>>>>>>>> +       my %redirects = ();
>>>>>>>>>> +
>>>>>>>>>> +       # Read-in redirectsfile.
>>>>>>>>>> +       &General::readhasharray($redirectsfile, \%redirects);
>>>>>>>>>> +
>>>>>>>>>> +       # move data on key up
>>>>>>>>>> +       foreach my $key (sort keys %redirects) {
>>>>>>>>>> +               if ($key >= $settings{'ID'}) {
>>>>>>>>>> +                       my $next = $key + 1;
>>>>>>>>>> +                       if (exists $redirects{$next}) {
>>>>>>>>>> +                               foreach my $i (0 ..
>>>>>>>>>> $#{$redirects{$next}}) { $redirects{$key}[$i] =
>>>>>>>>>> $redirects{$next}[$i]; }
>>>>>>>>>> +                       }
>>>>>>>>>> +               }
>>>>>>>>>> +       }
>>>>>>>>>> +
>>>>>>>>>> +       my $last_key = (sort {$a <=> $b} keys %redirects)[-1];
>>>>>>>>>> +       delete $redirects{$last_key};
>>>>>>>>>> +
>>>>>>>>>> +       # Undef the given ID.
>>>>>>>>>> +       undef($settings{'ID'});
>>>>>>>>>> +
>>>>>>>>>> +       # Write the changed redirects hash to file.
>>>>>>>>>> +       &General::writehasharray($redirectsfile, \%redirects);
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +# Load settings from file
>>>>>>>>>> +&General::readhash($settingsfile, \%settings);
>>>>>>>>>> +&General::readhasharray($redirectsfile, \%redirects);
>>>>>>>>>> +
>>>>>>>>>> +# Call functions to generate whole page.
>>>>>>>>>> +&Header::openpage($Lang::tr{'portredir port redirections'}, 1,
>>>>>>>>>> '');
>>>>>>>>>> +&Header::openbigbox('100%', 'left', '', $errormessage);
>>>>>>>>>> +
>>>>>>>>>> +if ($errormessage) {
>>>>>>>>>> +        &Header::openbox('100%', 'left', $Lang::tr{'warning
>>>>>>>>>> messages'});
>>>>>>>>>> +        print "<font color='red'>$errormessage&nbsp;</font>";
>>>>>>>>>> +        &Header::closebox();
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
>>>>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
>>>>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_ADDON'}
>>>>>>>>>> } = "checked='checked'";
>>>>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
>>>>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
>>>>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_GREEN'}
>>>>>>>>>> } = "checked='checked'";
>>>>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
>>>>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
>>>>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BLUE'}}
>>>>>>>>>> = "checked='checked'";
>>>>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
>>>>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
>>>>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_ORANGE
>>>>>>>>>> '}} = "checked='checked'";
>>>>>>>>>> +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
>>>>>>>>>> +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
>>>>>>>>>> +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}} =
>>>>>>>>>> "checked='checked'";
>>>>>>>>>> +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
>>>>>>>>>> +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
>>>>>>>>>> +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}} =
>>>>>>>>>> "checked='checked'";
>>>>>>>>>> +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
>>>>>>>>>> +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
>>>>>>>>>> +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}} =
>>>>>>>>>> "checked='checked'";
>>>>>>>>>> +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
>>>>>>>>>> +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
>>>>>>>>>> +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}} =
>>>>>>>>>> "checked='checked'";
>>>>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
>>>>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
>>>>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE'}} =
>>>>>>>>>> "checked='checked'";
>>>>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
>>>>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
>>>>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE'}} =
>>>>>>>>>> "checked='checked'";
>>>>>>>>>> +
>>>>>>>>>> +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTRY_INTE
>>>>>>>>>> RFACE'}} = 'selected';
>>>>>>>>>> +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY_PROTO
>>>>>>>>>> COL'}} = 'selected';
>>>>>>>>>> +
>>>>>>>>>> +&showMainBox();
>>>>>>>>>> +&showRedirectsBox();
>>>>>>>>>> +
>>>>>>>>>> +&Header::closebigbox();
>>>>>>>>>> +&Header::closepage();
>>>>>>>>>> +
>>>>>>>>>> +# Function to show main settings and options.
>>>>>>>>>> +sub showMainBox() {
>>>>>>>>>> +
>>>>>>>>>> +       &Header::openbox('100%', 'center',
>>>>>>>>>> "$Lang::tr{'settings'}");
>>>>>>>>>> +       print "<form method='post'
>>>>>>>>>> action='$ENV{'SCRIPT_NAME'}'>";
>>>>>>>>>> +
>>>>>>>>>> +print <<END;
>>>>>>>>>> +<table width='80%' cellspacing='0' border='0'>
>>>>>>>>>> +       <tr><td colspan='2' class='base'
>>>>>>>>>> bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir common
>>>>>>>>>> settings'}</b></td></tr
>>>>>>>>>> +       <tr>
>>>>>>>>>> +               <td width='25%'
>>>>>>>>>> class='base'>$Lang::tr{'portredir enable addon'}:</td>
>>>>>>>>>> +               <td><input type='checkbox'
>>>>>>>>>> name='REDIR_ENABLE_ADDON' $checked{'REDIR_ENABLE_ADDON'}{'on'}
>>>>>>>>>> /></td>
>>>>>>>>>> +       </tr>
>>>>>>>>>> +       <tr><td colspan='2'></td></tr>
>>>>>>>>>> +       <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>>>>>> +END
>>>>>>>>>> +
>>>>>>>>>> +       # create html table with header line 1
>>>>>>>>>> +       print "<table width='80%' cellspacing='0'
>>>>>>>>>> border='0'><tr>";
>>>>>>>>>> +       print "<th class='base' width='40%'
>>>>>>>>>> align='left'><b>$Lang::tr{'portredir fw for interface'}</th>";
>>>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<th
>>>>>>>>>> class='base' width='10%'><b><font
>>>>>>>>>> color=green>$Lang::tr{'green'}</font></th>";
>>>>>>>>>> +               } else {                 print "<th
>>>>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<th
>>>>>>>>>> class='base' width='10%'><b><font
>>>>>>>>>> color=blue>$Lang::tr{'blue'}</font></th>";
>>>>>>>>>> +               } else {                 print "<th
>>>>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<th
>>>>>>>>>> class='base' width='10%'><b><font
>>>>>>>>>> color=orange>$Lang::tr{'orange'}</font></th>";
>>>>>>>>>> +               } else {                 print "<th
>>>>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>>>>> +
>>>>>>>>>> +       # the empty right row
>>>>>>>>>> +       print "<th class='base'
>>>>>>>>>> width='30%'><td></td></th></tr>";
>>>>>>>>>> +
>>>>>>>>>> +       # line 2
>>>>>>>>>> +       print "<tr><td>$Lang::tr{'portredir force local
>>>>>>>>>> dns'}</td>";
>>>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>>> name='REDIR_DNS_GREEN'
>>>>>>>>>> $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print
>>>>>>>>>> "<td></td>";}
>>>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>>> name='REDIR_DNS_BLUE' $checked{'REDIR_DNS_BLUE'}{'on'}></td>";}
>>>>>>>>>> else { print "<td></td>";}
>>>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>>> name='REDIR_DNS_ORANGE'
>>>>>>>>>> $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print
>>>>>>>>>> "<td></td>";}
>>>>>>>>>> +
>>>>>>>>>> +       # line 3
>>>>>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir force local
>>>>>>>>>> ntp'}</td>";
>>>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>>> name='REDIR_NTP_GREEN'
>>>>>>>>>> $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print
>>>>>>>>>> "<td></td>";}
>>>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>>> name='REDIR_NTP_BLUE' $checked{'REDIR_NTP_BLUE'}{'on'}></td>";}
>>>>>>>>>> else { print "<td></td>";}
>>>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>>> name='REDIR_NTP_ORANGE'
>>>>>>>>>> $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print
>>>>>>>>>> "<td></td>";}
>>>>>>>>>> +
>>>>>>>>>> +       # line 4
>>>>>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir enable user
>>>>>>>>>> redirections'}</td>";
>>>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>>> name='REDIR_CUSTOM_GREEN'
>>>>>>>>>> $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else { print
>>>>>>>>>> "<td></td>";}
>>>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>>> name='REDIR_CUSTOM_BLUE'
>>>>>>>>>> $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print
>>>>>>>>>> "<td></td>";}
>>>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>>>> name='REDIR_CUSTOM_ORANGE'
>>>>>>>>>> $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else { print
>>>>>>>>>> "<td></td>";}
>>>>>>>>>> +
>>>>>>>>>> +       print <<END;
>>>>>>>>>> +       </tr></table>
>>>>>>>>>> +       <table width='80%' cellspacing='0' border='0'>
>>>>>>>>>> +               <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>>>>>> +               <tr><td align='left'><b>$Lang::tr{'portredir
>>>>>>>>>> save to activate'}</b></td><td width='5%' align='center'><input
>>>>>>>>>> type='submit' name='ACTION' value='  $Lang::tr{'save'} 
>>>>>>>>>> '></td></tr>
>>>>>>>>>> +       </table></form>
>>>>>>>>>> +END
>>>>>>>>>> +
>>>>>>>>>> +&Header::closebox();
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +# Function to show elements of the redirects file and allow to
>>>>>>>>>> add or remove single members of it.
>>>>>>>>>> +sub showRedirectsBox() {
>>>>>>>>>> +        &Header::openbox('100%', 'center',
>>>>>>>>>> "$Lang::tr{'portredir custom redirections'}");
>>>>>>>>>> +
>>>>>>>>>> +       print <<END;
>>>>>>>>>> +               <table width='80%' cellspacing='1' border='0'>
>>>>>>>>>> +                       <tr>
>>>>>>>>>> +                               <td class='base'
>>>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>>>> align='center'><b>$Lang::tr{'interface'}</b></td>
>>>>>>>>>> +                               <td class='base'
>>>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>>>> align='center'><b>$Lang::tr{'protocol'}</b></td>
>>>>>>>>>> +                               <td class='base'
>>>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>>>> align='center'><b>$Lang::tr{'port'}</b></td>
>>>>>>>>>> +                               <td class='base'
>>>>>>>>>> bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'ip
>>>>>>>>>> address'}</b></td>
>>>>>>>>>> +                               <td class='base'
>>>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>>>> align='center'><b>$Lang::tr{'remark'}</b></td>
>>>>>>>>>> +                               <td class='base' colspan='3'
>>>>>>>>>> bgcolor='$color{'color20'}'></td>
>>>>>>>>>> +                       </tr>
>>>>>>>>>> +END
>>>>>>>>>> +                       # Check if some rules have been added
>>>>>>>>>> to be redirects.
>>>>>>>>>> +                       if (keys (%redirects)) {
>>>>>>>>>> +                               my $col = "";
>>>>>>>>>> +
>>>>>>>>>> +                               # List all entries of the hash.
>>>>>>>>>> +                               foreach my $key (sort keys
>>>>>>>>>> %redirects){
>>>>>>>>>> +
>>>>>>>>>> +                                       # Assign data array
>>>>>>>>>> positions to some nice variable names.
>>>>>>>>>> +                                       my $interface =
>>>>>>>>>> $redirects{$key}[0];
>>>>>>>>>> +                                       my $protocol =
>>>>>>>>>> $redirects{$key}[1];
>>>>>>>>>> +                                       my $port  =
>>>>>>>>>> $redirects{$key}[2];
>>>>>>>>>> +                                       my $address =
>>>>>>>>>> $redirects{$key}[3];
>>>>>>>>>> +                                       my $status  =
>>>>>>>>>> $redirects{$key}[4];
>>>>>>>>>> +                                       my $remark  =
>>>>>>>>>> $redirects{$key}[5];
>>>>>>>>>> +
>>>>>>>>>> +                                       # Check if the key (id)
>>>>>>>>>> number is even or not.
>>>>>>>>>> +                                       if ($settings{'ID'} eq
>>>>>>>>>> $key) {
>>>>>>>>>> +                                               $col="bgcolor='
>>>>>>>>>> ${Header::colouryellow}'";
>>>>>>>>>> +                                       } elsif ($key % 2) {
>>>>>>>>>> +                                               $col="bgcolor='
>>>>>>>>>> $color{'color22'}'";
>>>>>>>>>> +                                       } else {
>>>>>>>>>> +                                               $col="bgcolor='
>>>>>>>>>> $color{'color20'}'";
>>>>>>>>>> +                                       }
>>>>>>>>>> +
>>>>>>>>>> +                                       # Choose icon for the
>>>>>>>>>> checkbox.
>>>>>>>>>> +                                       my $gif;
>>>>>>>>>> +                                       my $gdesc;
>>>>>>>>>> +
>>>>>>>>>> +                                       # Check if the status
>>>>>>>>>> is enabled and select the correct image and description.
>>>>>>>>>> +                                       if ($status eq
>>>>>>>>>> 'enabled' ) {
>>>>>>>>>> +                                               $gif =
>>>>>>>>>> 'on.gif';
>>>>>>>>>> +                                               $gdesc =
>>>>>>>>>> $Lang::tr{'click to disable'};
>>>>>>>>>> +                                       } else {
>>>>>>>>>> +                                               $gif =
>>>>>>>>>> 'off.gif';
>>>>>>>>>> +                                               $gdesc =
>>>>>>>>>> $Lang::tr{'click to enable'};
>>>>>>>>>> +                                       }
>>>>>>>>>> +
>>>>>>>>>> +                                       print <<END;
>>>>>>>>>> +                                       <tr>
>>>>>>>>>> +                                               <td width='15%'
>>>>>>>>>> class='base' align='center' $col><b><font
>>>>>>>>>> color=$interface>$Lang::tr{$interface}</font></b></td>
>>>>>>>>>> +                                               <td width='10%'
>>>>>>>>>> class='base' align='center' $col>$protocol</td>
>>>>>>>>>> +                                               <td width='10%'
>>>>>>>>>> class='base' align='center' $col>$port</td>
>>>>>>>>>> +                                               <td width='15%'
>>>>>>>>>> class='base' align='center' $col>&nbsp;$address</td>
>>>>>>>>>> +                                               <td width='40%'
>>>>>>>>>> class='base' align='center' $col>&nbsp;$remark</td>
>>>>>>>>>> +                                               <td
>>>>>>>>>> align='center' $col>
>>>>>>>>>> +                                                       <form
>>>>>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>>>> +                                                              
>>>>>>>>>> <input type='hidden' name='ACTION' value='$Lang::tr{'toggle
>>>>>>>>>> enable disable'}' />
>>>>>>>>>> +                                                              
>>>>>>>>>> <input type='image' name='$Lang::tr{'toggle enable disable'}'
>>>>>>>>>> src='/images/$gif' alt='$gdesc' title='$gdesc' />
>>>>>>>>>> +                                                              
>>>>>>>>>> <input type='hidden' name='ID' value='$key' />
>>>>>>>>>> +                                                       </form>
>>>>>>>>>> +                                               </td>
>>>>>>>>>> +                                               <td
>>>>>>>>>> align='center' $col>
>>>>>>>>>> +                                                       <form
>>>>>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>>>> +                                                              
>>>>>>>>>> <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}'
>>>>>>>>>> />
>>>>>>>>>> +                                                              
>>>>>>>>>> <input type='image' name='$Lang::tr{'edit'}'
>>>>>>>>>> src='/images/edit.gif' alt='$Lang::tr{'edit'}'
>>>>>>>>>> title='$Lang::tr{'edit'}' />
>>>>>>>>>> +                                                              
>>>>>>>>>> <input type='hidden' name='ID' value='$key' />
>>>>>>>>>> +                                                       </form>
>>>>>>>>>> +                                               </td>
>>>>>>>>>> +                                               <td
>>>>>>>>>> align='center' $col>
>>>>>>>>>> +                                                       <form
>>>>>>>>>> method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>>>> +                                                              
>>>>>>>>>> <input type='image' name='$Lang::tr{'remove'}'
>>>>>>>>>> src='/images/delete.gif' title='$Lang::tr{'remove'}'
>>>>>>>>>> alt='$Lang::tr{'remove'}'>
>>>>>>>>>> +                                                              
>>>>>>>>>> <input type='hidden' name='ID' value='$key'>
>>>>>>>>>> +                                                              
>>>>>>>>>> <input type='hidden' name='ACTION'
>>>>>>>>>> value='$Lang::tr{'remove'}'>
>>>>>>>>>> +                                                       </form>
>>>>>>>>>> +                                               </td>
>>>>>>>>>> +                                       </tr>
>>>>>>>>>> +END
>>>>>>>>>> +                               }
>>>>>>>>>> +                       } else {
>>>>>>>>>> +                               # Print notice that currently
>>>>>>>>>> no ports are redirected.
>>>>>>>>>> +                               print "<tr>\n";
>>>>>>>>>> +                               print "<td class='base'
>>>>>>>>>> colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
>>>>>>>>>> +                               print "</tr>\n";
>>>>>>>>>> +                       }
>>>>>>>>>> +
>>>>>>>>>> +               print "</table>\n";
>>>>>>>>>> +
>>>>>>>>>> +       # Section to add new elements or edit existing ones.
>>>>>>>>>> +       print <<END;
>>>>>>>>>> +       <br>
>>>>>>>>>> +       <hr>
>>>>>>>>>> +       <br>
>>>>>>>>>> +       <div align='center'>
>>>>>>>>>> +               <table width='100%' cellspacing='0' border='0'>
>>>>>>>>>> +END
>>>>>>>>>> +
>>>>>>>>>> +       # Assign correct headline and button text.
>>>>>>>>>> +       my $buttontext;
>>>>>>>>>> +       my $entry_interface;
>>>>>>>>>> +       my $entry_protocol;
>>>>>>>>>> +       my $entry_port;
>>>>>>>>>> +       my $entry_address;
>>>>>>>>>> +       my $entry_remark;
>>>>>>>>>> +
>>>>>>>>>> +       # Check if an ID (key) has been given, in this case an
>>>>>>>>>> existing entry should be edited.
>>>>>>>>>> +       if ($settings{'ID'} ne '') {
>>>>>>>>>> +               $buttontext = $Lang::tr{'update'};
>>>>>>>>>> +               print "<tr><td class='boldbase'
>>>>>>>>>> colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
>>>>>>>>>> +
>>>>>>>>>> +               # Grab address and remark for the given key.
>>>>>>>>>> +               $entry_interface =
>>>>>>>>>> $redirects{$settings{'ID'}}[0];
>>>>>>>>>> +               $entry_protocol =
>>>>>>>>>> $redirects{$settings{'ID'}}[1];
>>>>>>>>>> +               $entry_port = $redirects{$settings{'ID'}}[2];
>>>>>>>>>> +               $entry_address =
>>>>>>>>>> $redirects{$settings{'ID'}}[3];
>>>>>>>>>> +               $entry_remark = $redirects{$settings{'ID'}}[5];
>>>>>>>>>> +
>>>>>>>>>> +       } else {
>>>>>>>>>> +               $buttontext = $Lang::tr{'add'};
>>>>>>>>>> +               print "<tr><td class='boldbase'
>>>>>>>>>> colspan='11'><b>$Lang::tr{'dnsforward add a new
>>>>>>>>>> entry'}</b></td></tr>\n";
>>>>>>>>>> +               print "<tr><td>&nbsp</td></tr>\n";
>>>>>>>>>> +       }
>>>>>>>>>> +
>>>>>>>>>> +       print <<END;
>>>>>>>>>> +                       <tr>
>>>>>>>>>> +                               <td class='base' width='1%' 
>>>>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>>>>> +                               <td class='base' width='15%'
>>>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>>>> align='left'><b>$Lang::tr{'interface'}</b></td>
>>>>>>>>>> +                               <td class='base' width='10%'
>>>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>>>> align='left'><b>$Lang::tr{'protocol'}</b></td>
>>>>>>>>>> +                               <td class='base' width='10%'
>>>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>>>> align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
>>>>>>>>>> +                               <td class='base' width='13%'
>>>>>>>>>> bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'ip
>>>>>>>>>> address'}</b></td>
>>>>>>>>>> +                               <td class='base' width='30%'
>>>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>>>> align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
>>>>>>>>>> +                               <td class='base' width='15%'
>>>>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>>>>> +                               <td class='base' width='1%' 
>>>>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>>>>> +                       </tr>
>>>>>>>>>> +
>>>>>>>>>> +                       <form method='post'
>>>>>>>>>> action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>>>> +                       <input type='hidden' name='ID'
>>>>>>>>>> value='$settings{'ID'}'>
>>>>>>>>>> +                       <tr>
>>>>>>>>>> +                               <td class='base'></td>
>>>>>>>>>> +                               <td><select style='width:90px;'
>>>>>>>>>> id='interface' name='REDIR_ENTRY_INTERFACE'>
>>>>>>>>>> +END
>>>>>>>>>> +                               if ($netsettings{'GREEN_DEV'})
>>>>>>>>>> {
>>>>>>>>>> +                                       if ($entry_interface eq
>>>>>>>>>> "green") {
>>>>>>>>>> +                                               print "<option
>>>>>>>>>> value='green' selected='selected'
>>>>>>>>>> {'green'}>$Lang::tr{'green'}</option>";
>>>>>>>>>> +                                       } else { print "<option
>>>>>>>>>> value='green' {'green'}>$Lang::tr{'green'}</option>";}
>>>>>>>>>> +                               }
>>>>>>>>>> +                               if ($netsettings{'BLUE_DEV'}) {
>>>>>>>>>> +                                       if ($entry_interface eq
>>>>>>>>>> "blue") { 
>>>>>>>>>> +                                               print "<option
>>>>>>>>>> value='blue' selected='selected'
>>>>>>>>>> {'blue'}>$Lang::tr{'blue'}</option>";
>>>>>>>>>> +                                       } else { print "<option
>>>>>>>>>> value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
>>>>>>>>>> +                               }
>>>>>>>>>> +                               if ($netsettings{'ORANGE_DEV'})
>>>>>>>>>> {
>>>>>>>>>> +                                       if ($entry_interface eq
>>>>>>>>>> "orange") { 
>>>>>>>>>> +                                               print "<option
>>>>>>>>>> value='orange' selected='selected'
>>>>>>>>>> {'orange'}>$Lang::tr{'orange'}</option>";
>>>>>>>>>> +                                       } else { print "<option
>>>>>>>>>> value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
>>>>>>>>>> +                               }
>>>>>>>>>> +
>>>>>>>>>> +                       print "</select><td><select
>>>>>>>>>> style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
>>>>>>>>>> +                       if ((!$entry_protocol) ||
>>>>>>>>>> ($entry_protocol eq "tcp")) {
>>>>>>>>>> +                               print "<option
>>>>>>>>>> selected='selected' id='protocol' value='tcp'
>>>>>>>>>> {'tcp'}>tcp</option>";
>>>>>>>>>> +                               print "<option value='udp'
>>>>>>>>>> {'udp'}>udp</option>";
>>>>>>>>>> +                       } elsif ($entry_protocol eq "udp") {
>>>>>>>>>> +                               print "<option value='tcp'
>>>>>>>>>> {'tcp'}>tcp</option>";
>>>>>>>>>> +                               print "<option
>>>>>>>>>> selected='selected' value='udp' {'udp'}>udp</option>";
>>>>>>>>>> +                       }
>>>>>>>>>> +       print <<END;
>>>>>>>>>> +                               </select></td>
>>>>>>>>>> +                               <td><input type='text'
>>>>>>>>>> name='REDIR_ENTRY_PORT'    value='$entry_port'   
>>>>>>>>>> size='4'></td>
>>>>>>>>>> +                               <td><input type='text'
>>>>>>>>>> name='REDIR_ENTRY_ADDRESS' value='$entry_address'
>>>>>>>>>> size='14'></td>
>>>>>>>>>> +                               <td><input type='text'
>>>>>>>>>> name='REDIR_ENTRY_REMARK'  value='$entry_remark' 
>>>>>>>>>> size='35'></td>
>>>>>>>>>> +                               <td width='10%'
>>>>>>>>>> align='center'><input type='submit' name='ACTION' value=' 
>>>>>>>>>> $buttontext  '></td>
>>>>>>>>>> +                               <td class='base'></td>
>>>>>>>>>> +                       </tr>
>>>>>>>>>> +                       </form>
>>>>>>>>>> +               </table>
>>>>>>>>>> +       </div>
>>>>>>>>>> +END
>>>>>>>>>> +       &Header::closebox();
>>>>>>>>>> +}
>>>>>>>>>> diff --git a/config/rootfiles/common/misc-progs
>>>>>>>>>> b/config/rootfiles/common/misc-progs
>>>>>>>>>> index d6594b3f8..fbad2af8b 100644
>>>>>>>>>> --- a/config/rootfiles/common/misc-progs
>>>>>>>>>> +++ b/config/rootfiles/common/misc-progs
>>>>>>>>>> @@ -17,6 +17,7 @@ usr/local/bin/logwatch
>>>>>>>>>> #usr/local/bin/mpfirectrl
>>>>>>>>>> usr/local/bin/openvpnctrl
>>>>>>>>>> usr/local/bin/pakfire
>>>>>>>>>> +#usr/local/bin/portredirctrl
>>>>>>>>>> usr/local/bin/qosctrl
>>>>>>>>>> usr/local/bin/rebuildhosts
>>>>>>>>>> usr/local/bin/rebuildroutes
>>>>>>>>>> diff --git a/config/rootfiles/packages/portredir
>>>>>>>>>> b/config/rootfiles/packages/portredir
>>>>>>>>>> new file mode 100644
>>>>>>>>>> index 000000000..4b4ba8366
>>>>>>>>>> --- /dev/null
>>>>>>>>>> +++ b/config/rootfiles/packages/portredir
>>>>>>>>>> @@ -0,0 +1,11 @@
>>>>>>>>>> +etc/rc.d/init.d/portredir
>>>>>>>>>> +etc/rc.d/rc0.d/K77portredir
>>>>>>>>>> +etc/rc.d/rc3.d/S23portredir
>>>>>>>>>> +etc/rc.d/rc6.d/K77portredir
>>>>>>>>>> +srv/web/ipfire/cgi-bin/portredir.cgi
>>>>>>>>>> +usr/local/bin/portredirctrl
>>>>>>>>>> +var/ipfire/addon-lang/portredir.de.pl
>>>>>>>>>> +var/ipfire/addon-lang/portredir.en.pl
>>>>>>>>>> +var/ipfire/backup/addons/includes/portredir
>>>>>>>>>> +var/ipfire/menu.d/EX-portredir.menu
>>>>>>>>>> +var/ipfire/portredir
>>>>>>>>>> diff --git a/lfs/portredir b/lfs/portredir
>>>>>>>>>> new file mode 100644
>>>>>>>>>> index 000000000..a4911f71f
>>>>>>>>>> --- /dev/null
>>>>>>>>>> +++ b/lfs/portredir
>>>>>>>>>> @@ -0,0 +1,85 @@
>>>>>>>>>> +##############################################################
>>>>>>>>>> #################
>>>>>>>>>> +#                                                             
>>>>>>>>>>               #
>>>>>>>>>> +# IPFire.org - A linux based
>>>>>>>>>> firewall                                         #
>>>>>>>>>> +# Copyright (C) 2007-2021  IPFire Team 
>>>>>>>>>> <info@ipfire.org>                     #
>>>>>>>>>> +#                                                             
>>>>>>>>>>               #
>>>>>>>>>> +# This program is free software: you can redistribute it
>>>>>>>>>> and/or modify        #
>>>>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>>>>> published by        #
>>>>>>>>>> +# the Free Software Foundation, either version 3 of the
>>>>>>>>>> License, or           #
>>>>>>>>>> +# (at your option) any later
>>>>>>>>>> version.                                         #
>>>>>>>>>> +#                                                             
>>>>>>>>>>               #
>>>>>>>>>> +# This program is distributed in the hope that it will be
>>>>>>>>>> useful,             #
>>>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>>>>> of              #
>>>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>>>>> the               #
>>>>>>>>>> +# GNU General Public License for more
>>>>>>>>>> details.                                #
>>>>>>>>>> +#                                                             
>>>>>>>>>>               #
>>>>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>>>>> License           #
>>>>>>>>>> +# along with this program.  If not, see <
>>>>>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>>>>>> +#                                                             
>>>>>>>>>>               #
>>>>>>>>>> +##############################################################
>>>>>>>>>> #################
>>>>>>>>>> +
>>>>>>>>>> +##############################################################
>>>>>>>>>> #################
>>>>>>>>>> +# Definitions
>>>>>>>>>> +##############################################################
>>>>>>>>>> #################
>>>>>>>>>> +
>>>>>>>>>> +include Config
>>>>>>>>>> +
>>>>>>>>>> +VER        = 1.0
>>>>>>>>>> +
>>>>>>>>>> +THISAPP    = portredir-$(VER)
>>>>>>>>>> +DIR_APP    = $(DIR_SRC)/$(THISAPP)
>>>>>>>>>> +TARGET     = $(DIR_INFO)/$(THISAPP)
>>>>>>>>>> +PROG       = portredir
>>>>>>>>>> +PAK_VER    = 1
>>>>>>>>>> +
>>>>>>>>>> +##############################################################
>>>>>>>>>> #################
>>>>>>>>>> +# Top-level Rules
>>>>>>>>>> +##############################################################
>>>>>>>>>> #################
>>>>>>>>>> +
>>>>>>>>>> +install : $(TARGET)
>>>>>>>>>> +
>>>>>>>>>> +check :
>>>>>>>>>> +
>>>>>>>>>> +download :
>>>>>>>>>> +
>>>>>>>>>> +md5 :
>>>>>>>>>> +
>>>>>>>>>> +dist: 
>>>>>>>>>> +       @$(PAK)
>>>>>>>>>> +
>>>>>>>>>> +##############################################################
>>>>>>>>>> #################
>>>>>>>>>> +# Installation Details
>>>>>>>>>> +##############################################################
>>>>>>>>>> #################
>>>>>>>>>> +
>>>>>>>>>> +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
>>>>>>>>>> +       @$(PREBUILD)
>>>>>>>>>> +       @rm -rf $(DIR_APP) && cd $(DIR_SRC)
>>>>>>>>>> +
>>>>>>>>>> +       #install cgi 
>>>>>>>>>> +       install -v -m 755 $(DIR_CONF)/portredir/portredir.cgi
>>>>>>>>>> /srv/web/ipfire/cgi-bin/
>>>>>>>>>> +
>>>>>>>>>> +       #create configuration dir 
>>>>>>>>>> +       -mkdir -pv /var/ipfire/portredir/
>>>>>>>>>> +       chown -R nobody:nobody /var/ipfire/portredir/
>>>>>>>>>> +
>>>>>>>>>> +       # Install include file for backup
>>>>>>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/portredir-
>>>>>>>>>> backup /var/ipfire/backup/addons/includes/portredir
>>>>>>>>>> +
>>>>>>>>>> +       # Install menu file
>>>>>>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/EX-
>>>>>>>>>> portredir.menu /var/ipfire/menu.d/
>>>>>>>>>> +       chown nobody:nobody /var/ipfire/menu.d/EX-
>>>>>>>>>> portredir.menu
>>>>>>>>>> +
>>>>>>>>>> +       # Install addon-specific language-files
>>>>>>>>>> +       install -v -m 644
>>>>>>>>>> $(DIR_CONF)/portredir/lang/portredir.*.pl /var/ipfire/addon-
>>>>>>>>>> lang/
>>>>>>>>>> +
>>>>>>>>>> +       #install initscripts
>>>>>>>>>> +       $(call INSTALL_INITSCRIPT,portredir)
>>>>>>>>>> +
>>>>>>>>>> +       # Create symlinks for runlevel interaction.
>>>>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>>>>> /etc/rc.d/rc3.d/S23portredir
>>>>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>>>>> /etc/rc.d/rc0.d/K77portredir
>>>>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>>>>> /etc/rc.d/rc6.d/K77portredir
>>>>>>>>>> +
>>>>>>>>>> +       @rm -rf $(DIR_APP)
>>>>>>>>>> +       @$(POSTBUILD)
>>>>>>>>>> +
>>>>>>>>>> diff --git a/make.sh b/make.sh
>>>>>>>>>> index fc03ebcd5..ab9fe881a 100755
>>>>>>>>>> --- a/make.sh
>>>>>>>>>> +++ b/make.sh
>>>>>>>>>> @@ -1623,6 +1623,7 @@ buildipfire() {
>>>>>>>>>> lfsmake2 socat
>>>>>>>>>> lfsmake2 libcdada
>>>>>>>>>> lfsmake2 pmacct
>>>>>>>>>> +  lfsmake2 portredir
>>>>>>>>>> }
>>>>>>>>>> 
>>>>>>>>>> buildinstaller() {
>>>>>>>>>> diff --git a/src/initscripts/packages/portredir
>>>>>>>>>> b/src/initscripts/packages/portredir
>>>>>>>>>> new file mode 100644
>>>>>>>>>> index 000000000..cc57fb9cc
>>>>>>>>>> --- /dev/null
>>>>>>>>>> +++ b/src/initscripts/packages/portredir
>>>>>>>>>> @@ -0,0 +1,191 @@
>>>>>>>>>> +#!/bin/sh
>>>>>>>>>> +##############################################################
>>>>>>>>>> ##########
>>>>>>>>>> +# Begin $rc_base/init.d/portredir
>>>>>>>>>> +#
>>>>>>>>>> +# Description : portredir init script for DNS/NTP and custom 
>>>>>>>>>> +#              port redirection rules
>>>>>>>>>> +#
>>>>>>>>>> +##############################################################
>>>>>>>>>> ##########
>>>>>>>>>> +
>>>>>>>>>> +. /etc/sysconfig/rc
>>>>>>>>>> +. ${rc_functions}
>>>>>>>>>> +
>>>>>>>>>> +IPT="/sbin/iptables";
>>>>>>>>>> +parent_chain="PREROUTING";
>>>>>>>>>> +chain="PORT_REDIRECT";
>>>>>>>>>> +
>>>>>>>>>> +confdir="/var/ipfire/portredir";
>>>>>>>>>> +settingsfile="${confdir}/settings";
>>>>>>>>>> +redirectsfile="${confdir}/redirects";
>>>>>>>>>> +SYSLOG="NO";
>>>>>>>>>> +VERBOSE="NO";
>>>>>>>>>> +
>>>>>>>>>> +eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings);
>>>>>>>>>> +eval $(/usr/local/bin/readhash ${settingsfile});
>>>>>>>>>> +
>>>>>>>>>> +logtext() {
>>>>>>>>>> +       if [ "${SYSLOG}" = "YES" ]; then logger -t "portredir"
>>>>>>>>>> ${1}; fi;
>>>>>>>>>> +       if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
>>>>>>>>>> +
>>>>>>>>>> +create_chain() {
>>>>>>>>>> +
>>>>>>>>>> +       local line=$(${IPT} -t nat -L ${parent_chain} --line-
>>>>>>>>>> numbers |grep "SQUID" |awk '{printf($1)}');
>>>>>>>>>> +
>>>>>>>>>> +       if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z
>>>>>>>>>> "${REDIR_ENABLE_ADDON}" ]]; then
>>>>>>>>>> +               logtext "addon not enabled in web
>>>>>>>>>> interface...";
>>>>>>>>>> +               echo "Portredir addon not enabled in web
>>>>>>>>>> interface...";
>>>>>>>>>> +               exit 0;
>>>>>>>>>> +       fi;
>>>>>>>>>> +
>>>>>>>>>> +       if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>>>>>>> ${chain})" ]; then
>>>>>>>>>> +               ${IPT} -t nat -N ${chain};
>>>>>>>>>> +
>>>>>>>>>> +               if [ ! -z "${line}" ]; then
>>>>>>>>>> +                       logtext "create chain ${chain} and link
>>>>>>>>>> in ${parent_chain} at position ${line}...";
>>>>>>>>>> +                       ${IPT} -t nat -I ${parent_chain}
>>>>>>>>>> ${line} -j ${chain};
>>>>>>>>>> +               else
>>>>>>>>>> +                       logtext "create chain ${chain} and link
>>>>>>>>>> in ${parent_chain} at last position...";
>>>>>>>>>> +                       ${IPT} -t nat -A ${parent_chain} -j
>>>>>>>>>> ${chain};
>>>>>>>>>> +               fi
>>>>>>>>>> +       else
>>>>>>>>>> +               return 1;
>>>>>>>>>> +       fi;
>>>>>>>>>> +       return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +remove_chain() {
>>>>>>>>>> +       if [ ! -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>>>>>>> ${chain})" ]; then
>>>>>>>>>> +               logtext "remove chain ${chain} and link in
>>>>>>>>>> ${parent_chain} from system...";
>>>>>>>>>> +               ${IPT} -t nat -D "${parent_chain}" -j ${chain};
>>>>>>>>>> +               ${IPT} -t nat -F ${chain};
>>>>>>>>>> +               ${IPT} -t nat -X ${chain};
>>>>>>>>>> +       else
>>>>>>>>>> +               return 1;
>>>>>>>>>> +       fi;
>>>>>>>>>> +       return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +activate_custom_redirections() {
>>>>>>>>>> +       
>>>>>>>>>> +       local array=();
>>>>>>>>>> +       local redirects=();
>>>>>>>>>> +       local i;
>>>>>>>>>> +       index=();
>>>>>>>>>> +       iface=();
>>>>>>>>>> +       protocol=();
>>>>>>>>>> +       port=();
>>>>>>>>>> +       targetip=();
>>>>>>>>>> +       enabled=();
>>>>>>>>>> +
>>>>>>>>>> +       IFS=$'\n' read -d '' -ra redirects < ${redirectsfile};
>>>>>>>>>> +
>>>>>>>>>> +       for i in "${!redirects[@]}"
>>>>>>>>>> +       do
>>>>>>>>>> +               IFS=$',' read -ra array <<< ${redirects[i]};
>>>>>>>>>> +               index[i]=${array[0]};
>>>>>>>>>> +               iface[i]=${array[1]};
>>>>>>>>>> +               protocol[i]=${array[2]};
>>>>>>>>>> +               port[i]=${array[3]};
>>>>>>>>>> +               targetip[i]=${array[4]};
>>>>>>>>>> +               enabled[i]=${array[5]};
>>>>>>>>>> +       done
>>>>>>>>>> +
>>>>>>>>>> +       for i in "${!index[@]}"
>>>>>>>>>> +       do
>>>>>>>>>> +               if [[ ! -z "${GREEN_DEV}" &&  "${iface[i]}" =
>>>>>>>>>> "green" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>>>>> +
>>>>>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>>>>>> ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>>>>>> ${port[i]} ";
>>>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>>>> ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>>>> ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>>>>> +               fi
>>>>>>>>>> +               if [[ ! -z "${BLUE_DEV}" &&  "${iface[i]}" =
>>>>>>>>>> "blue" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>>>>>> ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>>>>>> ${port[i]} ";
>>>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>>>> ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>>>> ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>>>>> +               fi
>>>>>>>>>> +               if [[ ! -z "${ORANGE_DEV}" &&  "${iface[i]}" =
>>>>>>>>>> "orange" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>>>>>> ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>>>>>> ${port[i]} ";
>>>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>>>> ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>>>> ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>>>>> +               fi
>>>>>>>>>> +       done
>>>>>>>>>> +       unset array redirects i index iface protocol port
>>>>>>>>>> targetip enabled;
>>>>>>>>>> +       return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +activate_redirections() {
>>>>>>>>>> +
>>>>>>>>>> +       if ! create_chain; then return 1; fi;
>>>>>>>>>> +       
>>>>>>>>>> +       # Force DNS REDIRECTs on GREEN (udp, tcp, 53)
>>>>>>>>>> +       if [[ "${REDIR_DNS_GREEN}" == "on" && 
>>>>>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>>>>>> udp -m udp --dport domain -j REDIRECT;
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>>>>>> ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>>>>>> tcp -m tcp --dport domain -j REDIRECT;
>>>>>>>>>> +       fi
>>>>>>>>>> +
>>>>>>>>>> +       # Force DNS REDIRECTs on BLUE (udp, tcp, 53)
>>>>>>>>>> +       if [[ "${REDIR_DNS_BLUE}" == "on" && 
>>>>>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>>>>>>> -m udp --dport domain -j REDIRECT
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>>>>>> ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p tcp
>>>>>>>>>> -m tcp --dport domain -j REDIRECT
>>>>>>>>>> +       fi
>>>>>>>>>> +
>>>>>>>>>> +       # Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
>>>>>>>>>> +       if [[ "${REDIR_DNS_ORANGE}" == "on" && 
>>>>>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>>>>>> udp -m udp --dport domain -j REDIRECT
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>>>>>> ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>>>>>> tcp -m tcp --dport domain -j REDIRECT
>>>>>>>>>> +       fi
>>>>>>>>>> +
>>>>>>>>>> +       # Force NTP REDIRECTs on GREEN (udp, 123)
>>>>>>>>>> +       if [[ "${REDIR_NTP_GREEN}" == "on" && 
>>>>>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>>>>>> +       fi
>>>>>>>>>> +
>>>>>>>>>> +       # Force NTP REDIRECTs on BLUE (udp, 123)
>>>>>>>>>> +       if [[ "${REDIR_NTP_BLUE}" == "on" && 
>>>>>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>>>>>>> -m udp --dport ntp -j REDIRECT
>>>>>>>>>> +       fi
>>>>>>>>>> +
>>>>>>>>>> +       # Force NTP REDIRECTs on ORANGE (udp, 123)
>>>>>>>>>> +       if [[ "${REDIR_NTP_ORANGE}" == "on" && 
>>>>>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>>>>>> +       fi
>>>>>>>>>> +
>>>>>>>>>> +       if ! activate_custom_redirections; then return 1; fi;
>>>>>>>>>> +
>>>>>>>>>> +       return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +case "${1}" in
>>>>>>>>>> +       start)
>>>>>>>>>> +               boot_mesg "Loading port redirections..."
>>>>>>>>>> +               activate_redirections;
>>>>>>>>>> +               evaluate_retval;
>>>>>>>>>> +               ;;
>>>>>>>>>> +
>>>>>>>>>> +       stop)   
>>>>>>>>>> +               boot_mesg "Removing port redirections..."
>>>>>>>>>> +               remove_chain;
>>>>>>>>>> +               evaluate_retval;
>>>>>>>>>> +               ;;
>>>>>>>>>> +
>>>>>>>>>> +       restart)
>>>>>>>>>> +               ${0} stop
>>>>>>>>>> +               ${0} start
>>>>>>>>>> +               ;;
>>>>>>>>>> +
>>>>>>>>>> +       *)
>>>>>>>>>> +               echo "Usage: ${0} {start|stop|restart}"
>>>>>>>>>> +               exit 1
>>>>>>>>>> +               ;;
>>>>>>>>>> +esac
>>>>>>>>>> +
>>>>>>>>>> +# End $rc_base/init.d/portredir
>>>>>>>>>> diff --git a/src/misc-progs/Makefile b/src/misc-progs/Makefile
>>>>>>>>>> index 7c3ef7529..850f8fdcc 100644
>>>>>>>>>> --- a/src/misc-progs/Makefile
>>>>>>>>>> +++ b/src/misc-progs/Makefile
>>>>>>>>>> @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
>>>>>>>>>>       wirelessctrl getipstat qosctrl \
>>>>>>>>>>       redctrl syslogdctrl extrahdctrl sambactrl \
>>>>>>>>>>       smartctrl clamavctrl addonctrl pakfire mpfirectrl
>>>>>>>>>> wlanapctrl \
>>>>>>>>>> -       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>>>>>>> rebuildroutes \
>>>>>>>>>> +       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>>>>>>> rebuildroutes portredirctrl \
>>>>>>>>>>       getconntracktable wirelessclient torctrl ddnsctrl
>>>>>>>>>> unboundctrl \
>>>>>>>>>>       captivectrl
>>>>>>>>>> 
>>>>>>>>>> diff --git a/src/misc-progs/portredirctrl.c b/src/misc-
>>>>>>>>>> progs/portredirctrl.c
>>>>>>>>>> new file mode 100644
>>>>>>>>>> index 000000000..7897d711c
>>>>>>>>>> --- /dev/null
>>>>>>>>>> +++ b/src/misc-progs/portredirctrl.c
>>>>>>>>>> @@ -0,0 +1,47 @@
>>>>>>>>>> +/* This file is part of the IPFire Firewall.
>>>>>>>>>> + *
>>>>>>>>>> + * This program is distributed under the terms of the GNU
>>>>>>>>>> General Public
>>>>>>>>>> + * Licence.  See the file COPYING for details.
>>>>>>>>>> + *
>>>>>>>>>> + */
>>>>>>>>>> +
>>>>>>>>>> +#include <stdlib.h>
>>>>>>>>>> +#include <stdio.h>
>>>>>>>>>> +#include <string.h>
>>>>>>>>>> +#include <unistd.h>
>>>>>>>>>> +#include <sys/types.h>
>>>>>>>>>> +#include <fcntl.h>
>>>>>>>>>> +#include "setuid.h"
>>>>>>>>>> +
>>>>>>>>>> +int main(int argc, char *argv[]) {
>>>>>>>>>> +       if (!(initsetuid()))
>>>>>>>>>> +               exit(1);
>>>>>>>>>> +
>>>>>>>>>> +       // Check what command is asked
>>>>>>>>>> +        if (argc < 2) {
>>>>>>>>>> +                fprintf(stderr, "\nNo argument
>>>>>>>>>> given.\n\nportredirctrl
>>>>>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>>>>>> +                exit(1);
>>>>>>>>>> +        }
>>>>>>>>>> +
>>>>>>>>>> +        if (strcmp(argv[1], "start") == 0) {
>>>>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>>>>> start");
>>>>>>>>>> +        } else if (strcmp(argv[1], "stop") == 0) {
>>>>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>>>>> stop");
>>>>>>>>>> +        } else if (strcmp(argv[1], "restart") == 0) {
>>>>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>>>>> restart");
>>>>>>>>>> +       } else if (strcmp(argv[1], "enable") == 0) {
>>>>>>>>>> +               safe_system("touch
>>>>>>>>>> /var/ipfire/portredir/enable");
>>>>>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>>>>>> /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
>>>>>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>>>>>> /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
>>>>>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>>>>>> /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
>>>>>>>>>> +       } else if (strcmp(argv[1], "disable") == 0) {
>>>>>>>>>> +               safe_system("/etc/rc.d/init.d/portredir stop");
>>>>>>>>>> +               safe_system("unlink
>>>>>>>>>> /var/ipfire/portredir/enable");
>>>>>>>>>> +               safe_system("rm -rf /etc/rc.d/rc*.d/*portredir
>>>>>>>>>>> /dev/null 2>&1");
>>>>>>>>>> +        } else {
>>>>>>>>>> +                fprintf(stderr, "\nBad argument
>>>>>>>>>> given.\n\nportredirctrl
>>>>>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>>>>>> +                exit(1);
>>>>>>>>>> +        }
>>>>>>>>>> +
>>>>>>>>>> +       return 0;
>>>>>>>>>> +}
>>>>>>>>>> diff --git a/src/paks/portredir/install.sh
>>>>>>>>>> b/src/paks/portredir/install.sh
>>>>>>>>>> new file mode 100644
>>>>>>>>>> index 000000000..9f69aeae2
>>>>>>>>>> --- /dev/null
>>>>>>>>>> +++ b/src/paks/portredir/install.sh
>>>>>>>>>> @@ -0,0 +1,32 @@
>>>>>>>>>> +#!/bin/bash
>>>>>>>>>> +##############################################################
>>>>>>>>>> ##############
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +# This file is part of the IPFire
>>>>>>>>>> Firewall.                                #
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>>>>>> modify           #
>>>>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>>>>> published by     #
>>>>>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>>>>>> License, or        #
>>>>>>>>>> +# (at your option) any later
>>>>>>>>>> version.                                      #
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>>>>>> useful,                #
>>>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>>>>> of           #
>>>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>>>>> the            #
>>>>>>>>>> +# GNU General Public License for more
>>>>>>>>>> details.                             #
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>>>>> License        #
>>>>>>>>>> +# along with IPFire; if not, write to the Free
>>>>>>>>>> Software                    #
>>>>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>>>>>> 02111-1307 USA #
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +# Copyright (C) 2021 IPFire-Team
>>>>>>>>>> <info@ipfire.org>.                        #
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +##############################################################
>>>>>>>>>> ##############
>>>>>>>>>> +#
>>>>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>>>>> +extract_files
>>>>>>>>>> +restore_backup ${NAME}
>>>>>>>>>> +
>>>>>>>>>> +/usr/local/bin/update-lang-cache
>>>>>>>>>> +
>>>>>>>>>> +chown root:nobody /usr/local/bin/portredirctrl
>>>>>>>>>> +chmod 4750 /usr/local/bin/portredirctrl
>>>>>>>>>> +chmod u+s /usr/local/bin/portredirctrl
>>>>>>>>>> diff --git a/src/paks/portredir/uninstall.sh
>>>>>>>>>> b/src/paks/portredir/uninstall.sh
>>>>>>>>>> new file mode 100644
>>>>>>>>>> index 000000000..df9270125
>>>>>>>>>> --- /dev/null
>>>>>>>>>> +++ b/src/paks/portredir/uninstall.sh
>>>>>>>>>> @@ -0,0 +1,28 @@
>>>>>>>>>> +#!/bin/bash
>>>>>>>>>> +##############################################################
>>>>>>>>>> ##############
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +# This file is part of the IPFire
>>>>>>>>>> Firewall.                                #
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>>>>>> modify           #
>>>>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>>>>> published by     #
>>>>>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>>>>>> License, or        #
>>>>>>>>>> +# (at your option) any later
>>>>>>>>>> version.                                      #
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>>>>>> useful,                #
>>>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>>>>> of           #
>>>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>>>>> the            #
>>>>>>>>>> +# GNU General Public License for more
>>>>>>>>>> details.                             #
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>>>>> License        #
>>>>>>>>>> +# along with IPFire; if not, write to the Free
>>>>>>>>>> Software                    #
>>>>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>>>>>> 02111-1307 USA #
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>>>>>> <info@ipfire.org>.                        #
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +##############################################################
>>>>>>>>>> ##############
>>>>>>>>>> +#
>>>>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>>>>> +make_backup ${NAME}
>>>>>>>>>> +remove_files
>>>>>>>>>> +
>>>>>>>>>> +/usr/local/bin/update-lang-cache
>>>>>>>>>> diff --git a/src/paks/portredir/update.sh
>>>>>>>>>> b/src/paks/portredir/update.sh
>>>>>>>>>> new file mode 100644
>>>>>>>>>> index 000000000..89c40d0d7
>>>>>>>>>> --- /dev/null
>>>>>>>>>> +++ b/src/paks/portredir/update.sh
>>>>>>>>>> @@ -0,0 +1,26 @@
>>>>>>>>>> +#!/bin/bash
>>>>>>>>>> +##############################################################
>>>>>>>>>> ##############
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +# This file is part of the IPFire
>>>>>>>>>> Firewall.                                #
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>>>>>> modify           #
>>>>>>>>>> +# it under the terms of the GNU General Public License as
>>>>>>>>>> published by     #
>>>>>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>>>>>> License, or        #
>>>>>>>>>> +# (at your option) any later
>>>>>>>>>> version.                                      #
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>>>>>> useful,                #
>>>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>>>>>> of           #
>>>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>>>>>> the            #
>>>>>>>>>> +# GNU General Public License for more
>>>>>>>>>> details.                             #
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +# You should have received a copy of the GNU General Public
>>>>>>>>>> License        #
>>>>>>>>>> +# along with IPFire; if not, write to the Free
>>>>>>>>>> Software                    #
>>>>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>>>>>> 02111-1307 USA #
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>>>>>> <info@ipfire.org>.                        #
>>>>>>>>>> +#                                                             
>>>>>>>>>>            #
>>>>>>>>>> +##############################################################
>>>>>>>>>> ##############
>>>>>>>>>> +#
>>>>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>>>>> +./uninstall.sh
>>>>>>>>>> +./install.sh
>>>>>>>>>> -- 
>>>>>>>>>> 2.18.0
>>>>>>>>>> 
>>>>>>>>> 
>>>>>>>> 
>>>>>>> 
>>>>>> 
>>>>> 
>>>> 
>>> 
>> 
>
  
Stefan Schantl July 5, 2021, 12:07 p.m. UTC | #11
Hello Jon,

sorry for the long delay, currently I'm very limited on spare time.

I've the same issue, when editing an redirect rule on the WUI.

The rule was created for all and works properly but the WUI shows BLUE
here.

I'll have a look on it.

Best regards,

-Stefan
> Hmmm.  I tried an ALL (a.k.a ANY) instead and I think it works.  But
> there is a bug in the UI.
> 
> 
> This is what I tried:
> 
> 
> 
> (Two images enclosed)
> 
> 
> But, when I go to edit this rule the Destination Firewall changes
> from ALL to GREEN.  So I thought it wasn’t accepted.
> 
> 
> (One more image enclosed)
> 
> 
> Jon
> 
> 
> 
> > On Jul 1, 2021, at 10:24 AM, Michael Tremer
> > <michael.tremer@ipfire.org> wrote:
> > 
> > @Stefan: There should be an exception in the UI that these rules
> > can be created when REDIRECT is being used.
> > 
> > > On 1 Jul 2021, at 16:04, Jon Murphy <jcmurphy26@gmail.com> wrote:
> > > 
> > > > You probably want “any” as destination.
> > > 
> > > Those are the only choices that allow a Save.  When I enter an IP
> > > address I get this error:
> > > 
> > > 
> > > Error messages
> > > 
> > > Source and destination IP addresses are from the same subnet.  
> > > 
> > > 
> > > 
> > > Jon
> > > 
> > > > On Jul 1, 2021, at 3:08 AM, Michael Tremer
> > > > <michael.tremer@ipfire.org> wrote:
> > > > 
> > > > Hey Jon,
> > > > 
> > > > You probably want “any” as destination.
> > > > 
> > > > -Michael
> > > > 
> > > > > On 1 Jul 2021, at 04:08, Jon Murphy <jcmurphy26@gmail.com>
> > > > > wrote:
> > > > > 
> > > > > Hi Stefan,
> > > > > 
> > > > > Thank you for taking this on!
> > > > > 
> > > > > I applied the patchwork.ipfire patch.
> > > > > 
> > > > > I think I entered something wrong since I cannot get things
> > > > > to work.  I tried both with Destination Firewall GREEN &
> > > > > Firewall RED.
> > > > > 
> > > > > 
> > > > > Does the Firewall Rule seem right?
> > > > > 
> > > > > Best regards,
> > > > > Jon
> > > > > 
> > > > > 
> > > > > Here is the rule I set up:
> > > > > 
> > > > > 
> > > > > <Screen Shot 2021-06-30 at 9.53.52 PM.png>
> > > > > 
> > > > > <Screen Shot 2021-06-30 at 9.53.10 PM.png>
> > > > > 
> > > > > 
> > > > > And this is what I see with conntrack:
> > > > > 
> > > > > conntrack -E -e NEW,UPDATE | grep -e "=53 "
> > > > > 
> > > > >    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4
> > > > > sport=51169 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10
> > > > > sport=53 dport=51169
> > > > >    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4
> > > > > sport=54168 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10
> > > > > sport=53 dport=54168
> > > > >    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4
> > > > > sport=56094 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10
> > > > > sport=53 dport=56094
> > > > >    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4
> > > > > sport=52964 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10
> > > > > sport=53 dport=52964
> > > > >    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4
> > > > > sport=53279 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10
> > > > > sport=53 dport=53279
> > > > >    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4
> > > > > sport=61657 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10
> > > > > sport=53 dport=61657
> > > > >    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4
> > > > > sport=57723 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10
> > > > > sport=53 dport=57723
> > > > > 
> > > > > 
> > > > > 
> > > > > 
> > > > > > On Jun 30, 2021, at 2:14 PM, Stefan Schantl
> > > > > > <stefan.schantl@ipfire.org> wrote:
> > > > > > 
> > > > > > Hello Matthias, Hello Michael, Hello Jon, Hello *,
> > > > > > 
> > > > > > I've followed the conversation on this list since the first
> > > > > > mail and
> > > > > > thoughts about forcing DNS traffic to use the local
> > > > > > resolver.
> > > > > > 
> > > > > > It was a very long journey and lot of time and work has
> > > > > > been spent to
> > > > > > get to the present point.
> > > > > > 
> > > > > > As Michael requested here, I've digged through the lines of
> > > > > > the perl
> > > > > > script which is responsible for creating the firewall rules
> > > > > > and
> > > > > > surprisingly found that everyting which is needed to create
> > > > > > generic
> > > > > > REDIRECT rules already was written in the past - it just
> > > > > > did not work
> > > > > > as designed/expected.
> > > > > > 
> > > > > > Finaly I was able to adjust these lines of code and to
> > > > > > repair that
> > > > > > feature.
> > > > > > 
> > > > > > A redirect rule can be created by picking a single host or
> > > > > > group of
> > > > > > hosts or entire network(s) as source, selecting NAT (DNAT)
> > > > > > and choosing
> > > > > > the Firewall itself as target.
> > > > > > 
> > > > > > The protocol or service or service group which should be
> > > > > > redirected has
> > > > > > to be selected afterwards. If you want to redirect a given
> > > > > > port to
> > > > > > another one it can be specified as "Target port".
> > > > > > 
> > > > > > All created redirect rules are displayed as "input rules".
> > > > > > 
> > > > > > 
> > > > > > The patch directly can be accessed here:
> > > > > > 
> > > > > > https://patchwork.ipfire.org/project/ipfire/patch/20210630184031.7726-1-stefan.schantl@ipfire.org/
> > > > > > 
> > > > > > Best regards,
> > > > > > 
> > > > > > -Stefan
> > > > > > 
> > > > > > > Hello,
> > > > > > > 
> > > > > > > > On 28 Jun 2021, at 18:53, Jon Murphy
> > > > > > > > <jcmurphy26@gmail.com> wrote:
> > > > > > > > 
> > > > > > > > Hi Michael!  Happy Monday!
> > > > > > > > 
> > > > > > > > 
> > > > > > > > > Why do we not extend the firewall UI probably by
> > > > > > > > > about 20 lines
> > > > > > > > > of code instead of adding many hundreds of lines?
> > > > > > > > > 
> > > > > > > > > Please can someone elaborate on this more?
> > > > > > > > 
> > > > > > > > Doing a DNS redirect, via the WegBUI, has been an issue
> > > > > > > > since
> > > > > > > > 2015.  I found this quote in the old forum:
> > > > > > > > 
> > > > > > > > "Having investigated a bit more I have concluded that
> > > > > > > > it's not
> > > > > > > > currently possible to create such rules through the
> > > > > > > > WUI.
> > > > > > > > 
> > > > > > > > There are a number of obstacles:
> > > > > > > > 1. It is not allowed to create a rule where source IP
> > > > > > > > and
> > > > > > > > destination nat IP is on the same subnetwork (e.g.
> > > > > > > > GREEN), WUI
> > > > > > > > error message: "Source and destination IP addresses are
> > > > > > > > from the
> > > > > > > > same subnet."
> > > > > > > > 
> > > > > > > > 2. WUI will not allow you to create a rule without a
> > > > > > > > destination
> > > > > > > > (the filtered packet must adhere to a destination, not
> > > > > > > > only a port)
> > > > > > > > and the destination MUST be an IP address of one of the
> > > > > > > > IPFire
> > > > > > > > interfaces, which limits whats possible a great deal." 
> > > > > > > 
> > > > > > > And these cannot be changed?
> > > > > > > 
> > > > > > > > And I found this from 2016:
> > > > > > > > https://bugzilla.ipfire.org/show_bug.cgi?id=11168
> > > > > > > > 
> > > > > > > > So I am guessing that no one has been able to determine
> > > > > > > > a way to
> > > > > > > > extend the WebGUI.  
> > > > > > > 
> > > > > > > Has anyone tried? I do not see any obvious reasons why
> > > > > > > this should
> > > > > > > not be possible.
> > > > > > > 
> > > > > > > > I am curious - Who created the 
> > > > > > > > https://ipfire:444/cgi-bin/firewall.cgi page?  And
> > > > > > > > could they help?
> > > > > > > 
> > > > > > > -Michael
> > > > > > > 
> > > > > > > > Jon
> > > > > > > > 
> > > > > > > > 
> > > > > > > > > On Jun 28, 2021, at 11:04 AM, Michael Tremer <
> > > > > > > > > michael.tremer@ipfire.org> wrote:
> > > > > > > > > 
> > > > > > > > > Hello Matthias,
> > > > > > > > > 
> > > > > > > > > > On 27 Jun 2021, at 14:48, Matthias Fischer <
> > > > > > > > > > matthias.fischer@ipfire.org> wrote:
> > > > > > > > > > 
> > > > > > > > > > From: Marcel Lorenz <marcel.lorenz@ipfire.org>
> > > > > > > > > 
> > > > > > > > > Thank you for sending this patch on Marcel’s behalf,
> > > > > > > > > but I would
> > > > > > > > > much more prefer if he would submit his patches on
> > > > > > > > > his own. I do
> > > > > > > > > not see why that isn’t possible.
> > > > > > > > > 
> > > > > > > > > > Please note:
> > > > > > > > > > This is a new addon written by Marcel Lorenz <
> > > > > > > > > > marcel.lorenz@ipfire.org>.
> > > > > > > > > > 
> > > > > > > > > > It adds a new GUI to IPFire for DNS/NTP *and* user
> > > > > > > > > > specific
> > > > > > > > > > port redirections.
> > > > > > > > > > 
> > > > > > > > > > How its working:
> > > > > > > > > > It has exactly the same functionalities as "Forcing
> > > > > > > > > > DNS/NTP..."  - and some more.
> > > > > > > > > > 
> > > > > > > > > > By setting switches, DNS/NTP requests are
> > > > > > > > > > automatically
> > > > > > > > > > redirected to the local IPFire DNS/NTP servers.
> > > > > > > > > > 
> > > > > > > > > > Additionally, the user can specify custom
> > > > > > > > > > redirections.
> > > > > > > > > > 
> > > > > > > > > > These rules are added to a new chain in PREROUTING
> > > > > > > > > > =>
> > > > > > > > > > PORT_REDIRECT.
> > > > > > > > > > 
> > > > > > > > > > To avoid problems with (e.g.) transparent 'squid'
> > > > > > > > > > configurations,
> > > > > > > > > > redirection rules are added automatically before
> > > > > > > > > > existing
> > > > > > > > > > 'squid' rules.
> > > > > > > > > 
> > > > > > > > > This message does unfortunately not say why this add-
> > > > > > > > > on would be
> > > > > > > > > useful. I am emphasising this again and again that it
> > > > > > > > > is not very
> > > > > > > > > important how something is done specially. That
> > > > > > > > > should be
> > > > > > > > > commented in the code and other implementation
> > > > > > > > > details should
> > > > > > > > > also be documented there.
> > > > > > > > > 
> > > > > > > > > As I have stated on this functionality many times
> > > > > > > > > before, I do
> > > > > > > > > not see why this is necessary at all.
> > > > > > > > > 
> > > > > > > > > Why is this an add-on?
> > > > > > > > > 
> > > > > > > > > Why do we not extend the firewall UI probably by
> > > > > > > > > about 20 lines
> > > > > > > > > of code instead of adding many hundreds of lines?
> > > > > > > > > 
> > > > > > > > > Please can someone elaborate on this more?
> > > > > > > > > 
> > > > > > > > > -Michael
> > > > > > > > > 
> > > > > > > > > > Signed-off-by: Matthias Fischer
> > > > > > > > > > <matthias.fischer@ipfire.org>
> > > > > > > > > > ---
> > > > > > > > > > config/portredir/EX-portredir.menu    |   6 +
> > > > > > > > > > config/portredir/lang/portredir.de.pl |  19 +
> > > > > > > > > > config/portredir/lang/portredir.en.pl |  19 +
> > > > > > > > > > config/portredir/portredir-backup     |   1 +
> > > > > > > > > > config/portredir/portredir.cgi        | 525
> > > > > > > > > > ++++++++++++++++++++++++++
> > > > > > > > > > config/rootfiles/common/misc-progs    |   1 +
> > > > > > > > > > config/rootfiles/packages/portredir   |  11 +
> > > > > > > > > > lfs/portredir                         |  85 +++++
> > > > > > > > > > make.sh                               |   1 +
> > > > > > > > > > src/initscripts/packages/portredir    | 191
> > > > > > > > > > ++++++++++
> > > > > > > > > > src/misc-progs/Makefile               |   2 +-
> > > > > > > > > > src/misc-progs/portredirctrl.c        |  47 +++
> > > > > > > > > > src/paks/portredir/install.sh         |  32 ++
> > > > > > > > > > src/paks/portredir/uninstall.sh       |  28 ++
> > > > > > > > > > src/paks/portredir/update.sh          |  26 ++
> > > > > > > > > > 15 files changed, 993 insertions(+), 1 deletion(-)
> > > > > > > > > > create mode 100644 config/portredir/EX-
> > > > > > > > > > portredir.menu
> > > > > > > > > > create mode 100644
> > > > > > > > > > config/portredir/lang/portredir.de.pl
> > > > > > > > > > create mode 100644
> > > > > > > > > > config/portredir/lang/portredir.en.pl
> > > > > > > > > > create mode 100644 config/portredir/portredir-
> > > > > > > > > > backup
> > > > > > > > > > create mode 100644 config/portredir/portredir.cgi
> > > > > > > > > > create mode 100644
> > > > > > > > > > config/rootfiles/packages/portredir
> > > > > > > > > > create mode 100644 lfs/portredir
> > > > > > > > > > create mode 100644
> > > > > > > > > > src/initscripts/packages/portredir
> > > > > > > > > > create mode 100644 src/misc-progs/portredirctrl.c
> > > > > > > > > > create mode 100644 src/paks/portredir/install.sh
> > > > > > > > > > create mode 100644 src/paks/portredir/uninstall.sh
> > > > > > > > > > create mode 100644 src/paks/portredir/update.sh
> > > > > > > > > > 
> > > > > > > > > > diff --git a/config/portredir/EX-portredir.menu
> > > > > > > > > > b/config/portredir/EX-portredir.menu
> > > > > > > > > > new file mode 100644
> > > > > > > > > > index 000000000..8376e8053
> > > > > > > > > > --- /dev/null
> > > > > > > > > > +++ b/config/portredir/EX-portredir.menu
> > > > > > > > > > @@ -0,0 +1,6 @@
> > > > > > > > > > +    $subfirewall->{'95.portredir'} = {
> > > > > > > > > > +                               'caption' =>
> > > > > > > > > > $Lang::tr{'portredir port redirections'},
> > > > > > > > > > +                               'uri' => '/cgi-
> > > > > > > > > > bin/portredir.cgi',
> > > > > > > > > > +                               'title' =>
> > > > > > > > > > "$Lang::tr{'portredir port redirections'}",
> > > > > > > > > > +                               'enabled' => 1
> > > > > > > > > > +                               };
> > > > > > > > > > diff --git a/config/portredir/lang/portredir.de.pl
> > > > > > > > > > b/config/portredir/lang/portredir.de.pl
> > > > > > > > > > new file mode 100644
> > > > > > > > > > index 000000000..b932d4a85
> > > > > > > > > > --- /dev/null
> > > > > > > > > > +++ b/config/portredir/lang/portredir.de.pl
> > > > > > > > > > @@ -0,0 +1,19 @@
> > > > > > > > > > +%tr = (
> > > > > > > > > > +%tr,
> > > > > > > > > > +'portredir enable addon' => 'Addon aktivieren',
> > > > > > > > > > +'portredir common settings' => 'Allgemeine
> > > > > > > > > > Einstellungen',
> > > > > > > > > > +'portredir port redirections' =>
> > > > > > > > > > 'Portumleitungen',
> > > > > > > > > > +'portredir fw for interface' => 'Firewalloptionen
> > > > > > > > > > für das
> > > > > > > > > > Interface',
> > > > > > > > > > +'portredir enable user redirections' => 'Aktiviere
> > > > > > > > > > benutzerdefinierte Portumleitungen',
> > > > > > > > > > +'portredir force local dns' => 'Erzwinge lokale
> > > > > > > > > > DNS-Server',
> > > > > > > > > > +'portredir force local ntp' => 'Erzwinge lokale
> > > > > > > > > > NTP-Server',
> > > > > > > > > > +'portredir custom redirections' =>
> > > > > > > > > > 'Benutzerdefinierte
> > > > > > > > > > Portumleitungen',
> > > > > > > > > > +'portredir remove rule' => 'Entferne Regel',
> > > > > > > > > > +'portredir add rule' => 'Hinzufügen',
> > > > > > > > > > +'portredir no entries' => 'Keine Einträge
> > > > > > > > > > vorhanden.',
> > > > > > > > > > +'portredir invalid address' => 'Ungültige Host-
> > > > > > > > > > Addresse.',
> > > > > > > > > > +'portredir empty input' => 'Fehlende Angabe: Bitte
> > > > > > > > > > geben Sie
> > > > > > > > > > einen gültigen Host an.',
> > > > > > > > > > +'portredir save to activate' => 'Speichern, um
> > > > > > > > > > Änderungen zu
> > > > > > > > > > aktivieren',
> > > > > > > > > > +);
> > > > > > > > > > +
> > > > > > > > > > +#EOF
> > > > > > > > > > diff --git a/config/portredir/lang/portredir.en.pl
> > > > > > > > > > b/config/portredir/lang/portredir.en.pl
> > > > > > > > > > new file mode 100644
> > > > > > > > > > index 000000000..f442f3eaa
> > > > > > > > > > --- /dev/null
> > > > > > > > > > +++ b/config/portredir/lang/portredir.en.pl
> > > > > > > > > > @@ -0,0 +1,19 @@
> > > > > > > > > > +%tr = (
> > > > > > > > > > +%tr,
> > > > > > > > > > +'portredir enable addon' => 'Enable addon',
> > > > > > > > > > +'portredir common settings' => 'Common settings',
> > > > > > > > > > +'portredir port redirections' => 'Port
> > > > > > > > > > redirections',
> > > > > > > > > > +'portredir fw for interface' => 'Firewall options
> > > > > > > > > > for
> > > > > > > > > > interface',
> > > > > > > > > > +'portredir enable user redirections' => 'Enable
> > > > > > > > > > user port
> > > > > > > > > > redirections',
> > > > > > > > > > +'portredir force local dns' => 'Enforce local DNS
> > > > > > > > > > servers',
> > > > > > > > > > +'portredir force local ntp' => 'Enforce local NTP
> > > > > > > > > > servers',
> > > > > > > > > > +'portredir custom redirections' => 'Custom port
> > > > > > > > > > redirections',
> > > > > > > > > > +'portredir remove rule' => 'Remove rule',
> > > > > > > > > > +'portredir add rule' => 'Add new',
> > > > > > > > > > +'portredir no entries' => 'No entries at the
> > > > > > > > > > moment.',
> > > > > > > > > > +'portredir invalid address' => 'Invalid host
> > > > > > > > > > address.',
> > > > > > > > > > +'portredir empty input' => 'Empty input: Please
> > > > > > > > > > enter a valid
> > > > > > > > > > host.',
> > > > > > > > > > +'portredir save to activate' => 'Save to activate
> > > > > > > > > > changes',
> > > > > > > > > > +);
> > > > > > > > > > +
> > > > > > > > > > +#EOF
> > > > > > > > > > diff --git a/config/portredir/portredir-backup
> > > > > > > > > > b/config/portredir/portredir-backup
> > > > > > > > > > new file mode 100644
> > > > > > > > > > index 000000000..bd2ada742
> > > > > > > > > > --- /dev/null
> > > > > > > > > > +++ b/config/portredir/portredir-backup
> > > > > > > > > > @@ -0,0 +1 @@
> > > > > > > > > > +/var/ipfire/portredir
> > > > > > > > > > diff --git a/config/portredir/portredir.cgi
> > > > > > > > > > b/config/portredir/portredir.cgi
> > > > > > > > > > new file mode 100644
> > > > > > > > > > index 000000000..4913dda3f
> > > > > > > > > > --- /dev/null
> > > > > > > > > > +++ b/config/portredir/portredir.cgi
> > > > > > > > > > @@ -0,0 +1,525 @@
> > > > > > > > > > +#!/usr/bin/perl
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > #################
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >                #
> > > > > > > > > > +# IPFire.org - A linux based
> > > > > > > > > > firewall                                         #
> > > > > > > > > > +# Copyright (C) 2021  IPFire Team 
> > > > > > > > > > <info@ipfire.org>                          #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >                #
> > > > > > > > > > +# This program is free software: you can
> > > > > > > > > > redistribute it
> > > > > > > > > > and/or modify        #
> > > > > > > > > > +# it under the terms of the GNU General Public
> > > > > > > > > > License as
> > > > > > > > > > published by        #
> > > > > > > > > > +# the Free Software Foundation, either version 3
> > > > > > > > > > of the
> > > > > > > > > > License, or           #
> > > > > > > > > > +# (at your option) any later
> > > > > > > > > > version.                                         #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >                #
> > > > > > > > > > +# This program is distributed in the hope that it
> > > > > > > > > > will be
> > > > > > > > > > useful,             #
> > > > > > > > > > +# but WITHOUT ANY WARRANTY; without even the
> > > > > > > > > > implied warranty
> > > > > > > > > > of              #
> > > > > > > > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR
> > > > > > > > > > PURPOSE.  See
> > > > > > > > > > the               #
> > > > > > > > > > +# GNU General Public License for more
> > > > > > > > > > details.                                #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >                #
> > > > > > > > > > +# You should have received a copy of the GNU
> > > > > > > > > > General Public
> > > > > > > > > > License           #
> > > > > > > > > > +# along with this program.  If not, see <
> > > > > > > > > > http://www.gnu.org/licenses/>.       #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >                #
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > #################
> > > > > > > > > > +
> > > > > > > > > > +use strict;
> > > > > > > > > > +
> > > > > > > > > > +# enable only the following on debugging purpose
> > > > > > > > > > +use warnings;
> > > > > > > > > > +use CGI::Carp 'fatalsToBrowser';
> > > > > > > > > > +
> > > > > > > > > > +require '/var/ipfire/general-functions.pl';
> > > > > > > > > > +require "${General::swroot}/lang.pl";
> > > > > > > > > > +require "${General::swroot}/header.pl";
> > > > > > > > > > +
> > > > > > > > > > +# File declarations
> > > > > > > > > > +my $settingsfile =
> > > > > > > > > > "${General::swroot}/portredir/settings";
> > > > > > > > > > +my $redirectsfile =
> > > > > > > > > > "${General::swroot}/portredir/redirects";
> > > > > > > > > > +
> > > > > > > > > > +# Create empty settingsfiles if they does not
> > > > > > > > > > exist yet
> > > > > > > > > > +unless (-e "$settingsfile") { system ("touch
> > > > > > > > > > $settingsfile");
> > > > > > > > > > }
> > > > > > > > > > +unless (-e "$redirectsfile") { system ("touch
> > > > > > > > > > $redirectsfile"); }
> > > > > > > > > > +
> > > > > > > > > > +# load ipfire settings
> > > > > > > > > > +our %netsettings = ();
> > > > > > > > > > +our %color = ();
> > > > > > > > > > +&General::readhash("${General::swroot}/ethernet/se
> > > > > > > > > > ttings",
> > > > > > > > > > \%netsettings);
> > > > > > > > > > +&General::readhash("/srv/web/ipfire/html/themes/ip
> > > > > > > > > > fire/include
> > > > > > > > > > /colors.txt", \%color);
> > > > > > > > > > +
> > > > > > > > > > +my %settings=();
> > > > > > > > > > +my %portredirs=();
> > > > > > > > > > +my %checked=(); # Checkbox manipulations
> > > > > > > > > > +my $errormessage='';
> > > > > > > > > > +my %selected=();
> > > > > > > > > > +our %redirects=();
> > > > > > > > > > +
> > > > > > > > > > +$settings{'ACTION'} = '';
> > > > > > > > > > +$settings{'REDIR_ENABLE_ADDON'}="off";
> > > > > > > > > > +$settings{'REDIR_CUSTOM_GREEN'}="off";
> > > > > > > > > > +$settings{'REDIR_CUSTOM_BLUE'}="off";
> > > > > > > > > > +$settings{'REDIR_CUSTOM_ORANGE'}="off";
> > > > > > > > > > +$settings{'REDIR_DNS_GREEN'}="off";
> > > > > > > > > > +$settings{'REDIR_NTP_GREEN'}="off";
> > > > > > > > > > +$settings{'REDIR_DNS_BLUE'}="off";
> > > > > > > > > > +$settings{'REDIR_NTP_BLUE'}="off";
> > > > > > > > > > +$settings{'REDIR_DNS_ORANGE'}="off";
> > > > > > > > > > +$settings{'REDIR_NTP_ORANGE'}="off";
> > > > > > > > > > +
> > > > > > > > > > +&Header::showhttpheaders();
> > > > > > > > > > +
> > > > > > > > > > +# Get GUI values
> > > > > > > > > > +&Header::getcgihash(\%settings);
> > > > > > > > > > +
> > > > > > > > > > +# Save action
> > > > > > > > > > +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
> > > > > > > > > > +
> > > > > > > > > > +       # If custom rules enabled, deactivate
> > > > > > > > > > default rules on
> > > > > > > > > > interface
> > > > > > > > > > +       if ($settings{'REDIR_CUSTOM_GREEN'} eq "on"
> > > > > > > > > > ) {
> > > > > > > > > > +               $settings{'REDIR_DNS_GREEN'}="off";
> > > > > > > > > > +               $settings{'REDIR_NTP_GREEN'}="off";
> > > > > > > > > > +       }
> > > > > > > > > > +
> > > > > > > > > > +       if ($settings{'REDIR_CUSTOM_BLUE'} eq "on"
> > > > > > > > > > ) {
> > > > > > > > > > +               $settings{'REDIR_DNS_BLUE'}="off";
> > > > > > > > > > +               $settings{'REDIR_NTP_BLUE'}="off";
> > > > > > > > > > +       }
> > > > > > > > > > +
> > > > > > > > > > +       if ($settings{'REDIR_CUSTOM_ORANGE'} eq
> > > > > > > > > > "on" ) {
> > > > > > > > > > +
> > > > > > > > > >               $settings{'REDIR_DNS_ORANGE'}="off";
> > > > > > > > > > +
> > > > > > > > > >               $settings{'REDIR_NTP_ORANGE'}="off";
> > > > > > > > > > +       }
> > > > > > > > > > +
> > > > > > > > > > +       &General::writehash($settingsfile,
> > > > > > > > > > \%settings);
> > > > > > > > > > +
> > > > > > > > > > +       if ($settings{'REDIR_ENABLE_ADDON'} eq
> > > > > > > > > > "on") {
> > > > > > > > > > +               system
> > > > > > > > > > ('/usr/local/bin/portredirctrl restart
> > > > > > > > > > > /dev/null 2>&1');
> > > > > > > > > > +               system
> > > > > > > > > > ('/usr/local/bin/portredirctrl enable
> > > > > > > > > > > /dev/null 2>&1');
> > > > > > > > > > +               &General::log('portredir addon:
> > > > > > > > > > port
> > > > > > > > > > redirections enabled');
> > > > > > > > > > +       }
> > > > > > > > > > +       if ($settings{'REDIR_ENABLE_ADDON'} eq
> > > > > > > > > > "off") {
> > > > > > > > > > +               system
> > > > > > > > > > ('/usr/local/bin/portredirctrl disable
> > > > > > > > > > > /dev/null 2>&1');
> > > > > > > > > > +               system
> > > > > > > > > > ('/usr/local/bin/portredirctrl stop
> > > > > > > > > > > /dev/null 2>&1');
> > > > > > > > > > +               &General::log('portredir addon:
> > > > > > > > > > port
> > > > > > > > > > redirections disabled');
> > > > > > > > > > +       }
> > > > > > > > > > +
> > > > > > > > > > +# Add/edit an entry to the redirectsfile.
> > > > > > > > > > +
> > > > > > > > > > +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'})
> > > > > > > > > > ||
> > > > > > > > > > ($settings{'ACTION'} eq $Lang::tr{'update'})) {
> > > > > > > > > > +
> > > > > > > > > > +       # Check if any input has been performed.
> > > > > > > > > > +       if ($settings{'REDIR_ENTRY_ADDRESS'} ne '')
> > > > > > > > > > {
> > > > > > > > > > +
> > > > > > > > > > +               # Check if the given input is no
> > > > > > > > > > valid IP-
> > > > > > > > > > address, display an error message.
> > > > > > > > > > +               if
> > > > > > > > > > (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'
> > > > > > > > > > }))  {
> > > > > > > > > > +                       $errormessage =
> > > > > > > > > > "$Lang::tr{'portredir
> > > > > > > > > > invalid address'}";
> > > > > > > > > > +               }
> > > > > > > > > > +       } else {
> > > > > > > > > > +               $errormessage =
> > > > > > > > > > "$Lang::tr{'portredir empty
> > > > > > > > > > input'}";
> > > > > > > > > > +       }
> > > > > > > > > > +
> > > > > > > > > > +       # Go further if there was no error.
> > > > > > > > > > +       if ($errormessage eq '') {
> > > > > > > > > > +               my %redirects = ();
> > > > > > > > > > +               my $id;
> > > > > > > > > > +               my $status;
> > > > > > > > > > +
> > > > > > > > > > +               # Assign hash values.
> > > > > > > > > > +               my $new_entry_interface =
> > > > > > > > > > $settings{'REDIR_ENTRY_INTERFACE'};
> > > > > > > > > > +               my $new_entry_protocol =
> > > > > > > > > > $settings{'REDIR_ENTRY_PROTOCOL'};
> > > > > > > > > > +               my $new_entry_port =
> > > > > > > > > > $settings{'REDIR_ENTRY_PORT'};
> > > > > > > > > > +               my $new_entry_address =
> > > > > > > > > > $settings{'REDIR_ENTRY_ADDRESS'};
> > > > > > > > > > +               my $new_entry_remark =
> > > > > > > > > > $settings{'REDIR_ENTRY_REMARK'};
> > > > > > > > > > +
> > > > > > > > > > +               # Read-in redirectsfile.
> > > > > > > > > > +
> > > > > > > > > >               &General::readhasharray($redirectsfil
> > > > > > > > > > e,
> > > > > > > > > > \%redirects);
> > > > > > > > > > +
> > > > > > > > > > +               # Check if we should edit an
> > > > > > > > > > existing entry and
> > > > > > > > > > got an ID.
> > > > > > > > > > +               if (($settings{'ACTION'} eq
> > > > > > > > > > $Lang::tr{'update'}) && ($settings{'ID'})) {
> > > > > > > > > > +                       # Assin the provided id.
> > > > > > > > > > +                       $id = $settings{'ID'};
> > > > > > > > > > +
> > > > > > > > > > +                       # Undef the given ID.
> > > > > > > > > > +                       undef($settings{'ID'});
> > > > > > > > > > +
> > > > > > > > > > +                       # Grab the configured
> > > > > > > > > > status of the
> > > > > > > > > > corresponding entry.
> > > > > > > > > > +                       $status =
> > > > > > > > > > $redirects{$id}[4];
> > > > > > > > > > +               } else {
> > > > > > > > > > +                       # Each newly added entry
> > > > > > > > > > automatically
> > > > > > > > > > should be enabled.
> > > > > > > > > > +                       $status = "enabled";
> > > > > > > > > > +
> > > > > > > > > > +                       # Generate the ID for the
> > > > > > > > > > new entry.
> > > > > > > > > > +                       #
> > > > > > > > > > +                       # Sort the keys by their ID
> > > > > > > > > > and store
> > > > > > > > > > them in an array.
> > > > > > > > > > +                       my @keys = sort { $a <=> $b
> > > > > > > > > > } keys
> > > > > > > > > > %redirects;
> > > > > > > > > > +
> > > > > > > > > > +                       # Reverse the key array.
> > > > > > > > > > +                       my @reversed =
> > > > > > > > > > reverse(@keys);
> > > > > > > > > > +
> > > > > > > > > > +                       # Obtain the last used id.
> > > > > > > > > > +                       my $last_id = @reversed[0];
> > > > > > > > > > +
> > > > > > > > > > +                       # Increase the last id by
> > > > > > > > > > one and use
> > > > > > > > > > it as id for the new entry.
> > > > > > > > > > +                       $id = ++$last_id;
> > > > > > > > > > +               }
> > > > > > > > > > +
> > > > > > > > > > +               # Add/Modify the entry to/in the
> > > > > > > > > > redirects
> > > > > > > > > > hash.
> > > > > > > > > > +               $redirects{$id} =
> > > > > > > > > > ["$new_entry_interface",
> > > > > > > > > > "$new_entry_protocol", "$new_entry_port",
> > > > > > > > > > "$new_entry_address","$status",
> > > > > > > > > > "$new_entry_remark"];
> > > > > > > > > > +
> > > > > > > > > > +               # Write the changed redirects hash
> > > > > > > > > > to the
> > > > > > > > > > redirects file.
> > > > > > > > > > +
> > > > > > > > > >               &General::writehasharray($redirectsfi
> > > > > > > > > > le,
> > > > > > > > > > \%redirects);
> > > > > > > > > > +       }
> > > > > > > > > > +
> > > > > > > > > > +# Toggle Enabled/Disabled for an existing entry on
> > > > > > > > > > the
> > > > > > > > > > redirects list.
> > > > > > > > > > +
> > > > > > > > > > +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle
> > > > > > > > > > enable
> > > > > > > > > > disable'}) {
> > > > > > > > > > +       my %redirects = ();
> > > > > > > > > > +
> > > > > > > > > > +       # Only go further, if an ID has been
> > > > > > > > > > passed.
> > > > > > > > > > +       if ($settings{'ID'}) {
> > > > > > > > > > +               # Assign the given ID.
> > > > > > > > > > +               my $id = $settings{'ID'};
> > > > > > > > > > +
> > > > > > > > > > +               # Undef the given ID.
> > > > > > > > > > +               undef($settings{'ID'});
> > > > > > > > > > +
> > > > > > > > > > +               # Read-in ignoredfile.
> > > > > > > > > > +
> > > > > > > > > >               &General::readhasharray($redirectsfil
> > > > > > > > > > e,
> > > > > > > > > > \%redirects);
> > > > > > > > > > +
> > > > > > > > > > +               # Grab the configured status of the
> > > > > > > > > > corresponding entry.
> > > > > > > > > > +               my $status = $redirects{$id}[4];
> > > > > > > > > > +
> > > > > > > > > > +               # Switch the status.
> > > > > > > > > > +               if ($status eq "disabled") {
> > > > > > > > > > +                       $status = "enabled";
> > > > > > > > > > +               } else {
> > > > > > > > > > +                       $status = "disabled";
> > > > > > > > > > +               }
> > > > > > > > > > +
> > > > > > > > > > +               # Modify the status of the existing
> > > > > > > > > > entry.
> > > > > > > > > > +               $redirects{$id} =
> > > > > > > > > > ["$redirects{$id}[0]",
> > > > > > > > > > "$redirects{$id}[1]", "$redirects{$id}[2]",
> > > > > > > > > > "$redirects{$id}[3]","$status",
> > > > > > > > > > "$redirects{$id}[5]"];
> > > > > > > > > > +
> > > > > > > > > > +               # Write the changed ignored hash to
> > > > > > > > > > the
> > > > > > > > > > redirects file.
> > > > > > > > > > +
> > > > > > > > > >               &General::writehasharray($redirectsfi
> > > > > > > > > > le,
> > > > > > > > > > \%redirects);
> > > > > > > > > > +       }
> > > > > > > > > > +
> > > > > > > > > > +# Remove entry from redirects list.
> > > > > > > > > > +
> > > > > > > > > > +} elsif ($settings{'ACTION'} eq
> > > > > > > > > > $Lang::tr{'remove'}) {
> > > > > > > > > > +       my %redirects = ();
> > > > > > > > > > +
> > > > > > > > > > +       # Read-in redirectsfile.
> > > > > > > > > > +       &General::readhasharray($redirectsfile,
> > > > > > > > > > \%redirects);
> > > > > > > > > > +
> > > > > > > > > > +       # move data on key up
> > > > > > > > > > +       foreach my $key (sort keys %redirects) {
> > > > > > > > > > +               if ($key >= $settings{'ID'}) {
> > > > > > > > > > +                       my $next = $key + 1;
> > > > > > > > > > +                       if (exists
> > > > > > > > > > $redirects{$next}) {
> > > > > > > > > > +                               foreach my $i (0 ..
> > > > > > > > > > $#{$redirects{$next}}) { $redirects{$key}[$i] =
> > > > > > > > > > $redirects{$next}[$i]; }
> > > > > > > > > > +                       }
> > > > > > > > > > +               }
> > > > > > > > > > +       }
> > > > > > > > > > +
> > > > > > > > > > +       my $last_key = (sort {$a <=> $b} keys
> > > > > > > > > > %redirects)[-1];
> > > > > > > > > > +       delete $redirects{$last_key};
> > > > > > > > > > +
> > > > > > > > > > +       # Undef the given ID.
> > > > > > > > > > +       undef($settings{'ID'});
> > > > > > > > > > +
> > > > > > > > > > +       # Write the changed redirects hash to file.
> > > > > > > > > > +       &General::writehasharray($redirectsfile,
> > > > > > > > > > \%redirects);
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +# Load settings from file
> > > > > > > > > > +&General::readhash($settingsfile, \%settings);
> > > > > > > > > > +&General::readhasharray($redirectsfile,
> > > > > > > > > > \%redirects);
> > > > > > > > > > +
> > > > > > > > > > +# Call functions to generate whole page.
> > > > > > > > > > +&Header::openpage($Lang::tr{'portredir port
> > > > > > > > > > redirections'}, 1,
> > > > > > > > > > '');
> > > > > > > > > > +&Header::openbigbox('100%', 'left', '',
> > > > > > > > > > $errormessage);
> > > > > > > > > > +
> > > > > > > > > > +if ($errormessage) {
> > > > > > > > > > +        &Header::openbox('100%', 'left',
> > > > > > > > > > $Lang::tr{'warning
> > > > > > > > > > messages'});
> > > > > > > > > > +        print "<font
> > > > > > > > > > color='red'>$errormessage&nbsp;</font>";
> > > > > > > > > > +        &Header::closebox();
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
> > > > > > > > > > +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
> > > > > > > > > > +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_EN
> > > > > > > > > > ABLE_ADDON'}
> > > > > > > > > > } = "checked='checked'";
> > > > > > > > > > +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
> > > > > > > > > > +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
> > > > > > > > > > +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CU
> > > > > > > > > > STOM_GREEN'}
> > > > > > > > > > } = "checked='checked'";
> > > > > > > > > > +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
> > > > > > > > > > +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
> > > > > > > > > > +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUS
> > > > > > > > > > TOM_BLUE'}}
> > > > > > > > > > = "checked='checked'";
> > > > > > > > > > +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
> > > > > > > > > > +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
> > > > > > > > > > +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_C
> > > > > > > > > > USTOM_ORANGE
> > > > > > > > > > '}} = "checked='checked'";
> > > > > > > > > > +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
> > > > > > > > > > +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
> > > > > > > > > > +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_G
> > > > > > > > > > REEN'}} =
> > > > > > > > > > "checked='checked'";
> > > > > > > > > > +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
> > > > > > > > > > +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
> > > > > > > > > > +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_G
> > > > > > > > > > REEN'}} =
> > > > > > > > > > "checked='checked'";
> > > > > > > > > > +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
> > > > > > > > > > +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
> > > > > > > > > > +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BL
> > > > > > > > > > UE'}} =
> > > > > > > > > > "checked='checked'";
> > > > > > > > > > +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
> > > > > > > > > > +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
> > > > > > > > > > +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BL
> > > > > > > > > > UE'}} =
> > > > > > > > > > "checked='checked'";
> > > > > > > > > > +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
> > > > > > > > > > +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
> > > > > > > > > > +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_
> > > > > > > > > > ORANGE'}} =
> > > > > > > > > > "checked='checked'";
> > > > > > > > > > +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
> > > > > > > > > > +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
> > > > > > > > > > +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_
> > > > > > > > > > ORANGE'}} =
> > > > > > > > > > "checked='checked'";
> > > > > > > > > > +
> > > > > > > > > > +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDI
> > > > > > > > > > R_ENTRY_INTE
> > > > > > > > > > RFACE'}} = 'selected';
> > > > > > > > > > +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR
> > > > > > > > > > _ENTRY_PROTO
> > > > > > > > > > COL'}} = 'selected';
> > > > > > > > > > +
> > > > > > > > > > +&showMainBox();
> > > > > > > > > > +&showRedirectsBox();
> > > > > > > > > > +
> > > > > > > > > > +&Header::closebigbox();
> > > > > > > > > > +&Header::closepage();
> > > > > > > > > > +
> > > > > > > > > > +# Function to show main settings and options.
> > > > > > > > > > +sub showMainBox() {
> > > > > > > > > > +
> > > > > > > > > > +       &Header::openbox('100%', 'center',
> > > > > > > > > > "$Lang::tr{'settings'}");
> > > > > > > > > > +       print "<form method='post'
> > > > > > > > > > action='$ENV{'SCRIPT_NAME'}'>";
> > > > > > > > > > +
> > > > > > > > > > +print <<END;
> > > > > > > > > > +<table width='80%' cellspacing='0' border='0'>
> > > > > > > > > > +       <tr><td colspan='2' class='base'
> > > > > > > > > > bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir
> > > > > > > > > > common
> > > > > > > > > > settings'}</b></td></tr
> > > > > > > > > > +       <tr>
> > > > > > > > > > +               <td width='25%'
> > > > > > > > > > class='base'>$Lang::tr{'portredir enable
> > > > > > > > > > addon'}:</td>
> > > > > > > > > > +               <td><input type='checkbox'
> > > > > > > > > > name='REDIR_ENABLE_ADDON'
> > > > > > > > > > $checked{'REDIR_ENABLE_ADDON'}{'on'}
> > > > > > > > > > /></td>
> > > > > > > > > > +       </tr>
> > > > > > > > > > +       <tr><td colspan='2'></td></tr>
> > > > > > > > > > +       <tr><td colspan='2'>&nbsp;</td></tr>
> > > > > > > > > > +END
> > > > > > > > > > +
> > > > > > > > > > +       # create html table with header line 1
> > > > > > > > > > +       print "<table width='80%' cellspacing='0'
> > > > > > > > > > border='0'><tr>";
> > > > > > > > > > +       print "<th class='base' width='40%'
> > > > > > > > > > align='left'><b>$Lang::tr{'portredir fw for
> > > > > > > > > > interface'}</th>";
> > > > > > > > > > +       if ($netsettings{'GREEN_DEV'})  {print "<th
> > > > > > > > > > class='base' width='10%'><b><font
> > > > > > > > > > color=green>$Lang::tr{'green'}</font></th>";
> > > > > > > > > > +               } else {                 print "<th
> > > > > > > > > > class='base' width='10%'></font></th>"; }
> > > > > > > > > > +       if ($netsettings{'BLUE_DEV'})   {print "<th
> > > > > > > > > > class='base' width='10%'><b><font
> > > > > > > > > > color=blue>$Lang::tr{'blue'}</font></th>";
> > > > > > > > > > +               } else {                 print "<th
> > > > > > > > > > class='base' width='10%'></font></th>"; }
> > > > > > > > > > +       if ($netsettings{'ORANGE_DEV'}) {print "<th
> > > > > > > > > > class='base' width='10%'><b><font
> > > > > > > > > > color=orange>$Lang::tr{'orange'}</font></th>";
> > > > > > > > > > +               } else {                 print "<th
> > > > > > > > > > class='base' width='10%'></font></th>"; }
> > > > > > > > > > +
> > > > > > > > > > +       # the empty right row
> > > > > > > > > > +       print "<th class='base'
> > > > > > > > > > width='30%'><td></td></th></tr>";
> > > > > > > > > > +
> > > > > > > > > > +       # line 2
> > > > > > > > > > +       print "<tr><td>$Lang::tr{'portredir force
> > > > > > > > > > local
> > > > > > > > > > dns'}</td>";
> > > > > > > > > > +       if ($netsettings{'GREEN_DEV'})  {print "<td
> > > > > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > > > > name='REDIR_DNS_GREEN'
> > > > > > > > > > $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else {
> > > > > > > > > > print
> > > > > > > > > > "<td></td>";}
> > > > > > > > > > +       if ($netsettings{'BLUE_DEV'})   {print "<td
> > > > > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > > > > name='REDIR_DNS_BLUE'
> > > > > > > > > > $checked{'REDIR_DNS_BLUE'}{'on'}></td>";}
> > > > > > > > > > else { print "<td></td>";}
> > > > > > > > > > +       if ($netsettings{'ORANGE_DEV'}) {print "<td
> > > > > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > > > > name='REDIR_DNS_ORANGE'
> > > > > > > > > > $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else {
> > > > > > > > > > print
> > > > > > > > > > "<td></td>";}
> > > > > > > > > > +
> > > > > > > > > > +       # line 3
> > > > > > > > > > +       print "</tr><tr><td>$Lang::tr{'portredir
> > > > > > > > > > force local
> > > > > > > > > > ntp'}</td>";
> > > > > > > > > > +       if ($netsettings{'GREEN_DEV'})  {print "<td
> > > > > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > > > > name='REDIR_NTP_GREEN'
> > > > > > > > > > $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else {
> > > > > > > > > > print
> > > > > > > > > > "<td></td>";}
> > > > > > > > > > +       if ($netsettings{'BLUE_DEV'})   {print "<td
> > > > > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > > > > name='REDIR_NTP_BLUE'
> > > > > > > > > > $checked{'REDIR_NTP_BLUE'}{'on'}></td>";}
> > > > > > > > > > else { print "<td></td>";}
> > > > > > > > > > +       if ($netsettings{'ORANGE_DEV'}) {print "<td
> > > > > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > > > > name='REDIR_NTP_ORANGE'
> > > > > > > > > > $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else {
> > > > > > > > > > print
> > > > > > > > > > "<td></td>";}
> > > > > > > > > > +
> > > > > > > > > > +       # line 4
> > > > > > > > > > +       print "</tr><tr><td>$Lang::tr{'portredir
> > > > > > > > > > enable user
> > > > > > > > > > redirections'}</td>";
> > > > > > > > > > +       if ($netsettings{'GREEN_DEV'})  {print "<td
> > > > > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > > > > name='REDIR_CUSTOM_GREEN'
> > > > > > > > > > $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else
> > > > > > > > > > { print
> > > > > > > > > > "<td></td>";}
> > > > > > > > > > +       if ($netsettings{'BLUE_DEV'})   {print "<td
> > > > > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > > > > name='REDIR_CUSTOM_BLUE'
> > > > > > > > > > $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else {
> > > > > > > > > > print
> > > > > > > > > > "<td></td>";}
> > > > > > > > > > +       if ($netsettings{'ORANGE_DEV'}) {print "<td
> > > > > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > > > > name='REDIR_CUSTOM_ORANGE'
> > > > > > > > > > $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else
> > > > > > > > > > { print
> > > > > > > > > > "<td></td>";}
> > > > > > > > > > +
> > > > > > > > > > +       print <<END;
> > > > > > > > > > +       </tr></table>
> > > > > > > > > > +       <table width='80%' cellspacing='0'
> > > > > > > > > > border='0'>
> > > > > > > > > > +               <tr><td
> > > > > > > > > > colspan='2'>&nbsp;</td></tr>
> > > > > > > > > > +               <tr><td
> > > > > > > > > > align='left'><b>$Lang::tr{'portredir
> > > > > > > > > > save to activate'}</b></td><td width='5%'
> > > > > > > > > > align='center'><input
> > > > > > > > > > type='submit' name='ACTION' value='
> > > > > > > > > >  $Lang::tr{'save'} 
> > > > > > > > > > '></td></tr>
> > > > > > > > > > +       </table></form>
> > > > > > > > > > +END
> > > > > > > > > > +
> > > > > > > > > > +&Header::closebox();
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +# Function to show elements of the redirects file
> > > > > > > > > > and allow to
> > > > > > > > > > add or remove single members of it.
> > > > > > > > > > +sub showRedirectsBox() {
> > > > > > > > > > +        &Header::openbox('100%', 'center',
> > > > > > > > > > "$Lang::tr{'portredir custom redirections'}");
> > > > > > > > > > +
> > > > > > > > > > +       print <<END;
> > > > > > > > > > +               <table width='80%' cellspacing='1'
> > > > > > > > > > border='0'>
> > > > > > > > > > +                       <tr>
> > > > > > > > > > +                               <td class='base'
> > > > > > > > > > bgcolor='$color{'color20'}'
> > > > > > > > > > align='center'><b>$Lang::tr{'interface'}</b></td>
> > > > > > > > > > +                               <td class='base'
> > > > > > > > > > bgcolor='$color{'color20'}'
> > > > > > > > > > align='center'><b>$Lang::tr{'protocol'}</b></td>
> > > > > > > > > > +                               <td class='base'
> > > > > > > > > > bgcolor='$color{'color20'}'
> > > > > > > > > > align='center'><b>$Lang::tr{'port'}</b></td>
> > > > > > > > > > +                               <td class='base'
> > > > > > > > > > bgcolor='$color{'color20'}'
> > > > > > > > > > align='center'><b>$Lang::tr{'ip
> > > > > > > > > > address'}</b></td>
> > > > > > > > > > +                               <td class='base'
> > > > > > > > > > bgcolor='$color{'color20'}'
> > > > > > > > > > align='center'><b>$Lang::tr{'remark'}</b></td>
> > > > > > > > > > +                               <td class='base'
> > > > > > > > > > colspan='3'
> > > > > > > > > > bgcolor='$color{'color20'}'></td>
> > > > > > > > > > +                       </tr>
> > > > > > > > > > +END
> > > > > > > > > > +                       # Check if some rules have
> > > > > > > > > > been added
> > > > > > > > > > to be redirects.
> > > > > > > > > > +                       if (keys (%redirects)) {
> > > > > > > > > > +                               my $col = "";
> > > > > > > > > > +
> > > > > > > > > > +                               # List all entries
> > > > > > > > > > of the hash.
> > > > > > > > > > +                               foreach my $key
> > > > > > > > > > (sort keys
> > > > > > > > > > %redirects){
> > > > > > > > > > +
> > > > > > > > > > +                                       # Assign
> > > > > > > > > > data array
> > > > > > > > > > positions to some nice variable names.
> > > > > > > > > > +                                       my
> > > > > > > > > > $interface =
> > > > > > > > > > $redirects{$key}[0];
> > > > > > > > > > +                                       my
> > > > > > > > > > $protocol =
> > > > > > > > > > $redirects{$key}[1];
> > > > > > > > > > +                                       my $port  =
> > > > > > > > > > $redirects{$key}[2];
> > > > > > > > > > +                                       my $address
> > > > > > > > > > =
> > > > > > > > > > $redirects{$key}[3];
> > > > > > > > > > +                                       my $status
> > > > > > > > > >  =
> > > > > > > > > > $redirects{$key}[4];
> > > > > > > > > > +                                       my $remark
> > > > > > > > > >  =
> > > > > > > > > > $redirects{$key}[5];
> > > > > > > > > > +
> > > > > > > > > > +                                       # Check if
> > > > > > > > > > the key (id)
> > > > > > > > > > number is even or not.
> > > > > > > > > > +                                       if
> > > > > > > > > > ($settings{'ID'} eq
> > > > > > > > > > $key) {
> > > > > > > > > > +
> > > > > > > > > >                                               $col=
> > > > > > > > > > "bgcolor='
> > > > > > > > > > ${Header::colouryellow}'";
> > > > > > > > > > +                                       } elsif
> > > > > > > > > > ($key % 2) {
> > > > > > > > > > +
> > > > > > > > > >                                               $col=
> > > > > > > > > > "bgcolor='
> > > > > > > > > > $color{'color22'}'";
> > > > > > > > > > +                                       } else {
> > > > > > > > > > +
> > > > > > > > > >                                               $col=
> > > > > > > > > > "bgcolor='
> > > > > > > > > > $color{'color20'}'";
> > > > > > > > > > +                                       }
> > > > > > > > > > +
> > > > > > > > > > +                                       # Choose
> > > > > > > > > > icon for the
> > > > > > > > > > checkbox.
> > > > > > > > > > +                                       my $gif;
> > > > > > > > > > +                                       my $gdesc;
> > > > > > > > > > +
> > > > > > > > > > +                                       # Check if
> > > > > > > > > > the status
> > > > > > > > > > is enabled and select the correct image and
> > > > > > > > > > description.
> > > > > > > > > > +                                       if ($status
> > > > > > > > > > eq
> > > > > > > > > > 'enabled' ) {
> > > > > > > > > > +
> > > > > > > > > >                                               $gif
> > > > > > > > > > =
> > > > > > > > > > 'on.gif';
> > > > > > > > > > +
> > > > > > > > > >                                               $gdes
> > > > > > > > > > c =
> > > > > > > > > > $Lang::tr{'click to disable'};
> > > > > > > > > > +                                       } else {
> > > > > > > > > > +
> > > > > > > > > >                                               $gif
> > > > > > > > > > =
> > > > > > > > > > 'off.gif';
> > > > > > > > > > +
> > > > > > > > > >                                               $gdes
> > > > > > > > > > c =
> > > > > > > > > > $Lang::tr{'click to enable'};
> > > > > > > > > > +                                       }
> > > > > > > > > > +
> > > > > > > > > > +                                       print
> > > > > > > > > > <<END;
> > > > > > > > > > +                                       <tr>
> > > > > > > > > > +                                               <td
> > > > > > > > > > width='15%'
> > > > > > > > > > class='base' align='center' $col><b><font
> > > > > > > > > > color=$interface>$Lang::tr{$interface}</font></b></
> > > > > > > > > > td>
> > > > > > > > > > +                                               <td
> > > > > > > > > > width='10%'
> > > > > > > > > > class='base' align='center' $col>$protocol</td>
> > > > > > > > > > +                                               <td
> > > > > > > > > > width='10%'
> > > > > > > > > > class='base' align='center' $col>$port</td>
> > > > > > > > > > +                                               <td
> > > > > > > > > > width='15%'
> > > > > > > > > > class='base' align='center'
> > > > > > > > > > $col>&nbsp;$address</td>
> > > > > > > > > > +                                               <td
> > > > > > > > > > width='40%'
> > > > > > > > > > class='base' align='center' $col>&nbsp;$remark</td>
> > > > > > > > > > +                                               <td
> > > > > > > > > > align='center' $col>
> > > > > > > > > > +
> > > > > > > > > >                                                    
> > > > > > > > > >    <form
> > > > > > > > > > method='post' action='$ENV{'SCRIPT_NAME'}'>
> > > > > > > > > > +
> > > > > > > > > >                                                    
> > > > > > > > > >           
> > > > > > > > > > <input type='hidden' name='ACTION'
> > > > > > > > > > value='$Lang::tr{'toggle
> > > > > > > > > > enable disable'}' />
> > > > > > > > > > +
> > > > > > > > > >                                                    
> > > > > > > > > >           
> > > > > > > > > > <input type='image' name='$Lang::tr{'toggle enable
> > > > > > > > > > disable'}'
> > > > > > > > > > src='/images/$gif' alt='$gdesc' title='$gdesc' />
> > > > > > > > > > +
> > > > > > > > > >                                                    
> > > > > > > > > >           
> > > > > > > > > > <input type='hidden' name='ID' value='$key' />
> > > > > > > > > > +
> > > > > > > > > >                                                    
> > > > > > > > > >    </form>
> > > > > > > > > > +
> > > > > > > > > >                                               </td>
> > > > > > > > > > 
> > > > > > > > > > +                                               <td
> > > > > > > > > > align='center' $col>
> > > > > > > > > > +
> > > > > > > > > >                                                    
> > > > > > > > > >    <form
> > > > > > > > > > method='post' action='$ENV{'SCRIPT_NAME'}'>
> > > > > > > > > > +
> > > > > > > > > >                                                    
> > > > > > > > > >           
> > > > > > > > > > <input type='hidden' name='ACTION'
> > > > > > > > > > value='$Lang::tr{'edit'}'
> > > > > > > > > > />
> > > > > > > > > > +
> > > > > > > > > >                                                    
> > > > > > > > > >           
> > > > > > > > > > <input type='image' name='$Lang::tr{'edit'}'
> > > > > > > > > > src='/images/edit.gif' alt='$Lang::tr{'edit'}'
> > > > > > > > > > title='$Lang::tr{'edit'}' />
> > > > > > > > > > +
> > > > > > > > > >                                                    
> > > > > > > > > >           
> > > > > > > > > > <input type='hidden' name='ID' value='$key' />
> > > > > > > > > > +
> > > > > > > > > >                                                    
> > > > > > > > > >    </form>
> > > > > > > > > > +
> > > > > > > > > >                                               </td>
> > > > > > > > > > 
> > > > > > > > > > +                                               <td
> > > > > > > > > > align='center' $col>
> > > > > > > > > > +
> > > > > > > > > >                                                    
> > > > > > > > > >    <form
> > > > > > > > > > method='post' name='$key'
> > > > > > > > > > action='$ENV{'SCRIPT_NAME'}'>
> > > > > > > > > > +
> > > > > > > > > >                                                    
> > > > > > > > > >           
> > > > > > > > > > <input type='image' name='$Lang::tr{'remove'}'
> > > > > > > > > > src='/images/delete.gif'
> > > > > > > > > > title='$Lang::tr{'remove'}'
> > > > > > > > > > alt='$Lang::tr{'remove'}'>
> > > > > > > > > > +
> > > > > > > > > >                                                    
> > > > > > > > > >           
> > > > > > > > > > <input type='hidden' name='ID' value='$key'>
> > > > > > > > > > +
> > > > > > > > > >                                                    
> > > > > > > > > >           
> > > > > > > > > > <input type='hidden' name='ACTION'
> > > > > > > > > > value='$Lang::tr{'remove'}'>
> > > > > > > > > > +
> > > > > > > > > >                                                    
> > > > > > > > > >    </form>
> > > > > > > > > > +
> > > > > > > > > >                                               </td>
> > > > > > > > > > 
> > > > > > > > > > +                                       </tr>
> > > > > > > > > > +END
> > > > > > > > > > +                               }
> > > > > > > > > > +                       } else {
> > > > > > > > > > +                               # Print notice that
> > > > > > > > > > currently
> > > > > > > > > > no ports are redirected.
> > > > > > > > > > +                               print "<tr>\n";
> > > > > > > > > > +                               print "<td
> > > > > > > > > > class='base'
> > > > > > > > > > colspan='2'>$Lang::tr{'portredir no
> > > > > > > > > > entries'}</td>\n";
> > > > > > > > > > +                               print "</tr>\n";
> > > > > > > > > > +                       }
> > > > > > > > > > +
> > > > > > > > > > +               print "</table>\n";
> > > > > > > > > > +
> > > > > > > > > > +       # Section to add new elements or edit
> > > > > > > > > > existing ones.
> > > > > > > > > > +       print <<END;
> > > > > > > > > > +       <br>
> > > > > > > > > > +       <hr>
> > > > > > > > > > +       <br>
> > > > > > > > > > +       <div align='center'>
> > > > > > > > > > +               <table width='100%' cellspacing='0'
> > > > > > > > > > border='0'>
> > > > > > > > > > +END
> > > > > > > > > > +
> > > > > > > > > > +       # Assign correct headline and button text.
> > > > > > > > > > +       my $buttontext;
> > > > > > > > > > +       my $entry_interface;
> > > > > > > > > > +       my $entry_protocol;
> > > > > > > > > > +       my $entry_port;
> > > > > > > > > > +       my $entry_address;
> > > > > > > > > > +       my $entry_remark;
> > > > > > > > > > +
> > > > > > > > > > +       # Check if an ID (key) has been given, in
> > > > > > > > > > this case an
> > > > > > > > > > existing entry should be edited.
> > > > > > > > > > +       if ($settings{'ID'} ne '') {
> > > > > > > > > > +               $buttontext = $Lang::tr{'update'};
> > > > > > > > > > +               print "<tr><td class='boldbase'
> > > > > > > > > > colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n"
> > > > > > > > > > ;
> > > > > > > > > > +
> > > > > > > > > > +               # Grab address and remark for the
> > > > > > > > > > given key.
> > > > > > > > > > +               $entry_interface =
> > > > > > > > > > $redirects{$settings{'ID'}}[0];
> > > > > > > > > > +               $entry_protocol =
> > > > > > > > > > $redirects{$settings{'ID'}}[1];
> > > > > > > > > > +               $entry_port =
> > > > > > > > > > $redirects{$settings{'ID'}}[2];
> > > > > > > > > > +               $entry_address =
> > > > > > > > > > $redirects{$settings{'ID'}}[3];
> > > > > > > > > > +               $entry_remark =
> > > > > > > > > > $redirects{$settings{'ID'}}[5];
> > > > > > > > > > +
> > > > > > > > > > +       } else {
> > > > > > > > > > +               $buttontext = $Lang::tr{'add'};
> > > > > > > > > > +               print "<tr><td class='boldbase'
> > > > > > > > > > colspan='11'><b>$Lang::tr{'dnsforward add a new
> > > > > > > > > > entry'}</b></td></tr>\n";
> > > > > > > > > > +               print "<tr><td>&nbsp</td></tr>\n";
> > > > > > > > > > +       }
> > > > > > > > > > +
> > > > > > > > > > +       print <<END;
> > > > > > > > > > +                       <tr>
> > > > > > > > > > +                               <td class='base'
> > > > > > > > > > width='1%' 
> > > > > > > > > > bgcolor='$color{'color22'}'></td>
> > > > > > > > > > +                               <td class='base'
> > > > > > > > > > width='15%'
> > > > > > > > > > bgcolor='$color{'color22'}'
> > > > > > > > > > align='left'><b>$Lang::tr{'interface'}</b></td>
> > > > > > > > > > +                               <td class='base'
> > > > > > > > > > width='10%'
> > > > > > > > > > bgcolor='$color{'color22'}'
> > > > > > > > > > align='left'><b>$Lang::tr{'protocol'}</b></td>
> > > > > > > > > > +                               <td class='base'
> > > > > > > > > > width='10%'
> > > > > > > > > > bgcolor='$color{'color22'}'
> > > > > > > > > > align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
> > > > > > > > > > +                               <td class='base'
> > > > > > > > > > width='13%'
> > > > > > > > > > bgcolor='$color{'color22'}'
> > > > > > > > > > align='left'>&nbsp;<b>$Lang::tr{'ip
> > > > > > > > > > address'}</b></td>
> > > > > > > > > > +                               <td class='base'
> > > > > > > > > > width='30%'
> > > > > > > > > > bgcolor='$color{'color22'}'
> > > > > > > > > > align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
> > > > > > > > > > +                               <td class='base'
> > > > > > > > > > width='15%'
> > > > > > > > > > bgcolor='$color{'color22'}'></td>
> > > > > > > > > > +                               <td class='base'
> > > > > > > > > > width='1%' 
> > > > > > > > > > bgcolor='$color{'color22'}'></td>
> > > > > > > > > > +                       </tr>
> > > > > > > > > > +
> > > > > > > > > > +                       <form method='post'
> > > > > > > > > > action='$ENV{'SCRIPT_NAME'}'>
> > > > > > > > > > +                       <input type='hidden'
> > > > > > > > > > name='ID'
> > > > > > > > > > value='$settings{'ID'}'>
> > > > > > > > > > +                       <tr>
> > > > > > > > > > +                               <td
> > > > > > > > > > class='base'></td>
> > > > > > > > > > +                               <td><select
> > > > > > > > > > style='width:90px;'
> > > > > > > > > > id='interface' name='REDIR_ENTRY_INTERFACE'>
> > > > > > > > > > +END
> > > > > > > > > > +                               if
> > > > > > > > > > ($netsettings{'GREEN_DEV'})
> > > > > > > > > > {
> > > > > > > > > > +                                       if
> > > > > > > > > > ($entry_interface eq
> > > > > > > > > > "green") {
> > > > > > > > > > +
> > > > > > > > > >                                               print
> > > > > > > > > > "<option
> > > > > > > > > > value='green' selected='selected'
> > > > > > > > > > {'green'}>$Lang::tr{'green'}</option>";
> > > > > > > > > > +                                       } else {
> > > > > > > > > > print "<option
> > > > > > > > > > value='green'
> > > > > > > > > > {'green'}>$Lang::tr{'green'}</option>";}
> > > > > > > > > > +                               }
> > > > > > > > > > +                               if
> > > > > > > > > > ($netsettings{'BLUE_DEV'}) {
> > > > > > > > > > +                                       if
> > > > > > > > > > ($entry_interface eq
> > > > > > > > > > "blue") { 
> > > > > > > > > > +
> > > > > > > > > >                                               print
> > > > > > > > > > "<option
> > > > > > > > > > value='blue' selected='selected'
> > > > > > > > > > {'blue'}>$Lang::tr{'blue'}</option>";
> > > > > > > > > > +                                       } else {
> > > > > > > > > > print "<option
> > > > > > > > > > value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
> > > > > > > > > > +                               }
> > > > > > > > > > +                               if
> > > > > > > > > > ($netsettings{'ORANGE_DEV'})
> > > > > > > > > > {
> > > > > > > > > > +                                       if
> > > > > > > > > > ($entry_interface eq
> > > > > > > > > > "orange") { 
> > > > > > > > > > +
> > > > > > > > > >                                               print
> > > > > > > > > > "<option
> > > > > > > > > > value='orange' selected='selected'
> > > > > > > > > > {'orange'}>$Lang::tr{'orange'}</option>";
> > > > > > > > > > +                                       } else {
> > > > > > > > > > print "<option
> > > > > > > > > > value='orange'
> > > > > > > > > > {'orange'}>$Lang::tr{'orange'}</option>";}
> > > > > > > > > > +                               }
> > > > > > > > > > +
> > > > > > > > > > +                       print "</select><td><select
> > > > > > > > > > style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
> > > > > > > > > > +                       if ((!$entry_protocol) ||
> > > > > > > > > > ($entry_protocol eq "tcp")) {
> > > > > > > > > > +                               print "<option
> > > > > > > > > > selected='selected' id='protocol' value='tcp'
> > > > > > > > > > {'tcp'}>tcp</option>";
> > > > > > > > > > +                               print "<option
> > > > > > > > > > value='udp'
> > > > > > > > > > {'udp'}>udp</option>";
> > > > > > > > > > +                       } elsif ($entry_protocol eq
> > > > > > > > > > "udp") {
> > > > > > > > > > +                               print "<option
> > > > > > > > > > value='tcp'
> > > > > > > > > > {'tcp'}>tcp</option>";
> > > > > > > > > > +                               print "<option
> > > > > > > > > > selected='selected' value='udp'
> > > > > > > > > > {'udp'}>udp</option>";
> > > > > > > > > > +                       }
> > > > > > > > > > +       print <<END;
> > > > > > > > > > +                               </select></td>
> > > > > > > > > > +                               <td><input
> > > > > > > > > > type='text'
> > > > > > > > > > name='REDIR_ENTRY_PORT'    value='$entry_port'   
> > > > > > > > > > size='4'></td>
> > > > > > > > > > +                               <td><input
> > > > > > > > > > type='text'
> > > > > > > > > > name='REDIR_ENTRY_ADDRESS' value='$entry_address'
> > > > > > > > > > size='14'></td>
> > > > > > > > > > +                               <td><input
> > > > > > > > > > type='text'
> > > > > > > > > > name='REDIR_ENTRY_REMARK'  value='$entry_remark' 
> > > > > > > > > > size='35'></td>
> > > > > > > > > > +                               <td width='10%'
> > > > > > > > > > align='center'><input type='submit' name='ACTION'
> > > > > > > > > > value=' 
> > > > > > > > > > $buttontext  '></td>
> > > > > > > > > > +                               <td
> > > > > > > > > > class='base'></td>
> > > > > > > > > > +                       </tr>
> > > > > > > > > > +                       </form>
> > > > > > > > > > +               </table>
> > > > > > > > > > +       </div>
> > > > > > > > > > +END
> > > > > > > > > > +       &Header::closebox();
> > > > > > > > > > +}
> > > > > > > > > > diff --git a/config/rootfiles/common/misc-progs
> > > > > > > > > > b/config/rootfiles/common/misc-progs
> > > > > > > > > > index d6594b3f8..fbad2af8b 100644
> > > > > > > > > > --- a/config/rootfiles/common/misc-progs
> > > > > > > > > > +++ b/config/rootfiles/common/misc-progs
> > > > > > > > > > @@ -17,6 +17,7 @@ usr/local/bin/logwatch
> > > > > > > > > > #usr/local/bin/mpfirectrl
> > > > > > > > > > usr/local/bin/openvpnctrl
> > > > > > > > > > usr/local/bin/pakfire
> > > > > > > > > > +#usr/local/bin/portredirctrl
> > > > > > > > > > usr/local/bin/qosctrl
> > > > > > > > > > usr/local/bin/rebuildhosts
> > > > > > > > > > usr/local/bin/rebuildroutes
> > > > > > > > > > diff --git a/config/rootfiles/packages/portredir
> > > > > > > > > > b/config/rootfiles/packages/portredir
> > > > > > > > > > new file mode 100644
> > > > > > > > > > index 000000000..4b4ba8366
> > > > > > > > > > --- /dev/null
> > > > > > > > > > +++ b/config/rootfiles/packages/portredir
> > > > > > > > > > @@ -0,0 +1,11 @@
> > > > > > > > > > +etc/rc.d/init.d/portredir
> > > > > > > > > > +etc/rc.d/rc0.d/K77portredir
> > > > > > > > > > +etc/rc.d/rc3.d/S23portredir
> > > > > > > > > > +etc/rc.d/rc6.d/K77portredir
> > > > > > > > > > +srv/web/ipfire/cgi-bin/portredir.cgi
> > > > > > > > > > +usr/local/bin/portredirctrl
> > > > > > > > > > +var/ipfire/addon-lang/portredir.de.pl
> > > > > > > > > > +var/ipfire/addon-lang/portredir.en.pl
> > > > > > > > > > +var/ipfire/backup/addons/includes/portredir
> > > > > > > > > > +var/ipfire/menu.d/EX-portredir.menu
> > > > > > > > > > +var/ipfire/portredir
> > > > > > > > > > diff --git a/lfs/portredir b/lfs/portredir
> > > > > > > > > > new file mode 100644
> > > > > > > > > > index 000000000..a4911f71f
> > > > > > > > > > --- /dev/null
> > > > > > > > > > +++ b/lfs/portredir
> > > > > > > > > > @@ -0,0 +1,85 @@
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > #################
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >                #
> > > > > > > > > > +# IPFire.org - A linux based
> > > > > > > > > > firewall                                         #
> > > > > > > > > > +# Copyright (C) 2007-2021  IPFire Team 
> > > > > > > > > > <info@ipfire.org>                     #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >                #
> > > > > > > > > > +# This program is free software: you can
> > > > > > > > > > redistribute it
> > > > > > > > > > and/or modify        #
> > > > > > > > > > +# it under the terms of the GNU General Public
> > > > > > > > > > License as
> > > > > > > > > > published by        #
> > > > > > > > > > +# the Free Software Foundation, either version 3
> > > > > > > > > > of the
> > > > > > > > > > License, or           #
> > > > > > > > > > +# (at your option) any later
> > > > > > > > > > version.                                         #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >                #
> > > > > > > > > > +# This program is distributed in the hope that it
> > > > > > > > > > will be
> > > > > > > > > > useful,             #
> > > > > > > > > > +# but WITHOUT ANY WARRANTY; without even the
> > > > > > > > > > implied warranty
> > > > > > > > > > of              #
> > > > > > > > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR
> > > > > > > > > > PURPOSE.  See
> > > > > > > > > > the               #
> > > > > > > > > > +# GNU General Public License for more
> > > > > > > > > > details.                                #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >                #
> > > > > > > > > > +# You should have received a copy of the GNU
> > > > > > > > > > General Public
> > > > > > > > > > License           #
> > > > > > > > > > +# along with this program.  If not, see <
> > > > > > > > > > http://www.gnu.org/licenses/>.       #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >                #
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > #################
> > > > > > > > > > +
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > #################
> > > > > > > > > > +# Definitions
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > #################
> > > > > > > > > > +
> > > > > > > > > > +include Config
> > > > > > > > > > +
> > > > > > > > > > +VER        = 1.0
> > > > > > > > > > +
> > > > > > > > > > +THISAPP    = portredir-$(VER)
> > > > > > > > > > +DIR_APP    = $(DIR_SRC)/$(THISAPP)
> > > > > > > > > > +TARGET     = $(DIR_INFO)/$(THISAPP)
> > > > > > > > > > +PROG       = portredir
> > > > > > > > > > +PAK_VER    = 1
> > > > > > > > > > +
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > #################
> > > > > > > > > > +# Top-level Rules
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > #################
> > > > > > > > > > +
> > > > > > > > > > +install : $(TARGET)
> > > > > > > > > > +
> > > > > > > > > > +check :
> > > > > > > > > > +
> > > > > > > > > > +download :
> > > > > > > > > > +
> > > > > > > > > > +md5 :
> > > > > > > > > > +
> > > > > > > > > > +dist: 
> > > > > > > > > > +       @$(PAK)
> > > > > > > > > > +
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > #################
> > > > > > > > > > +# Installation Details
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > #################
> > > > > > > > > > +
> > > > > > > > > > +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
> > > > > > > > > > +       @$(PREBUILD)
> > > > > > > > > > +       @rm -rf $(DIR_APP) && cd $(DIR_SRC)
> > > > > > > > > > +
> > > > > > > > > > +       #install cgi 
> > > > > > > > > > +       install -v -m 755
> > > > > > > > > > $(DIR_CONF)/portredir/portredir.cgi
> > > > > > > > > > /srv/web/ipfire/cgi-bin/
> > > > > > > > > > +
> > > > > > > > > > +       #create configuration dir 
> > > > > > > > > > +       -mkdir -pv /var/ipfire/portredir/
> > > > > > > > > > +       chown -R nobody:nobody
> > > > > > > > > > /var/ipfire/portredir/
> > > > > > > > > > +
> > > > > > > > > > +       # Install include file for backup
> > > > > > > > > > +       install -v -m 644
> > > > > > > > > > $(DIR_CONF)/portredir/portredir-
> > > > > > > > > > backup /var/ipfire/backup/addons/includes/portredir
> > > > > > > > > > +
> > > > > > > > > > +       # Install menu file
> > > > > > > > > > +       install -v -m 644 $(DIR_CONF)/portredir/EX-
> > > > > > > > > > portredir.menu /var/ipfire/menu.d/
> > > > > > > > > > +       chown nobody:nobody /var/ipfire/menu.d/EX-
> > > > > > > > > > portredir.menu
> > > > > > > > > > +
> > > > > > > > > > +       # Install addon-specific language-files
> > > > > > > > > > +       install -v -m 644
> > > > > > > > > > $(DIR_CONF)/portredir/lang/portredir.*.pl
> > > > > > > > > > /var/ipfire/addon-
> > > > > > > > > > lang/
> > > > > > > > > > +
> > > > > > > > > > +       #install initscripts
> > > > > > > > > > +       $(call INSTALL_INITSCRIPT,portredir)
> > > > > > > > > > +
> > > > > > > > > > +       # Create symlinks for runlevel interaction.
> > > > > > > > > > +       ln -svf /etc/rc.d/init.d/portredir
> > > > > > > > > > /etc/rc.d/rc3.d/S23portredir
> > > > > > > > > > +       ln -svf /etc/rc.d/init.d/portredir
> > > > > > > > > > /etc/rc.d/rc0.d/K77portredir
> > > > > > > > > > +       ln -svf /etc/rc.d/init.d/portredir
> > > > > > > > > > /etc/rc.d/rc6.d/K77portredir
> > > > > > > > > > +
> > > > > > > > > > +       @rm -rf $(DIR_APP)
> > > > > > > > > > +       @$(POSTBUILD)
> > > > > > > > > > +
> > > > > > > > > > diff --git a/make.sh b/make.sh
> > > > > > > > > > index fc03ebcd5..ab9fe881a 100755
> > > > > > > > > > --- a/make.sh
> > > > > > > > > > +++ b/make.sh
> > > > > > > > > > @@ -1623,6 +1623,7 @@ buildipfire() {
> > > > > > > > > > lfsmake2 socat
> > > > > > > > > > lfsmake2 libcdada
> > > > > > > > > > lfsmake2 pmacct
> > > > > > > > > > +  lfsmake2 portredir
> > > > > > > > > > }
> > > > > > > > > > 
> > > > > > > > > > buildinstaller() {
> > > > > > > > > > diff --git a/src/initscripts/packages/portredir
> > > > > > > > > > b/src/initscripts/packages/portredir
> > > > > > > > > > new file mode 100644
> > > > > > > > > > index 000000000..cc57fb9cc
> > > > > > > > > > --- /dev/null
> > > > > > > > > > +++ b/src/initscripts/packages/portredir
> > > > > > > > > > @@ -0,0 +1,191 @@
> > > > > > > > > > +#!/bin/sh
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > ##########
> > > > > > > > > > +# Begin $rc_base/init.d/portredir
> > > > > > > > > > +#
> > > > > > > > > > +# Description : portredir init script for DNS/NTP
> > > > > > > > > > and custom 
> > > > > > > > > > +#              port redirection rules
> > > > > > > > > > +#
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > ##########
> > > > > > > > > > +
> > > > > > > > > > +. /etc/sysconfig/rc
> > > > > > > > > > +. ${rc_functions}
> > > > > > > > > > +
> > > > > > > > > > +IPT="/sbin/iptables";
> > > > > > > > > > +parent_chain="PREROUTING";
> > > > > > > > > > +chain="PORT_REDIRECT";
> > > > > > > > > > +
> > > > > > > > > > +confdir="/var/ipfire/portredir";
> > > > > > > > > > +settingsfile="${confdir}/settings";
> > > > > > > > > > +redirectsfile="${confdir}/redirects";
> > > > > > > > > > +SYSLOG="NO";
> > > > > > > > > > +VERBOSE="NO";
> > > > > > > > > > +
> > > > > > > > > > +eval $(/usr/local/bin/readhash
> > > > > > > > > > /var/ipfire/ethernet/settings);
> > > > > > > > > > +eval $(/usr/local/bin/readhash ${settingsfile});
> > > > > > > > > > +
> > > > > > > > > > +logtext() {
> > > > > > > > > > +       if [ "${SYSLOG}" = "YES" ]; then logger -t
> > > > > > > > > > "portredir"
> > > > > > > > > > ${1}; fi;
> > > > > > > > > > +       if [ "${VERBOSE}" = "YES" ]; then echo
> > > > > > > > > > ${1}; fi;}
> > > > > > > > > > +
> > > > > > > > > > +create_chain() {
> > > > > > > > > > +
> > > > > > > > > > +       local line=$(${IPT} -t nat -L
> > > > > > > > > > ${parent_chain} --line-
> > > > > > > > > > numbers |grep "SQUID" |awk '{printf($1)}');
> > > > > > > > > > +
> > > > > > > > > > +       if [[ "${REDIR_ENABLE_ADDON}" == "off" || -
> > > > > > > > > > z
> > > > > > > > > > "${REDIR_ENABLE_ADDON}" ]]; then
> > > > > > > > > > +               logtext "addon not enabled in web
> > > > > > > > > > interface...";
> > > > > > > > > > +               echo "Portredir addon not enabled
> > > > > > > > > > in web
> > > > > > > > > > interface...";
> > > > > > > > > > +               exit 0;
> > > > > > > > > > +       fi;
> > > > > > > > > > +
> > > > > > > > > > +       if [ -z "$(${IPT} -t nat -L ${parent_chain}
> > > > > > > > > > |grep
> > > > > > > > > > ${chain})" ]; then
> > > > > > > > > > +               ${IPT} -t nat -N ${chain};
> > > > > > > > > > +
> > > > > > > > > > +               if [ ! -z "${line}" ]; then
> > > > > > > > > > +                       logtext "create chain
> > > > > > > > > > ${chain} and link
> > > > > > > > > > in ${parent_chain} at position ${line}...";
> > > > > > > > > > +                       ${IPT} -t nat -I
> > > > > > > > > > ${parent_chain}
> > > > > > > > > > ${line} -j ${chain};
> > > > > > > > > > +               else
> > > > > > > > > > +                       logtext "create chain
> > > > > > > > > > ${chain} and link
> > > > > > > > > > in ${parent_chain} at last position...";
> > > > > > > > > > +                       ${IPT} -t nat -A
> > > > > > > > > > ${parent_chain} -j
> > > > > > > > > > ${chain};
> > > > > > > > > > +               fi
> > > > > > > > > > +       else
> > > > > > > > > > +               return 1;
> > > > > > > > > > +       fi;
> > > > > > > > > > +       return 0;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +remove_chain() {
> > > > > > > > > > +       if [ ! -z "$(${IPT} -t nat -L
> > > > > > > > > > ${parent_chain} |grep
> > > > > > > > > > ${chain})" ]; then
> > > > > > > > > > +               logtext "remove chain ${chain} and
> > > > > > > > > > link in
> > > > > > > > > > ${parent_chain} from system...";
> > > > > > > > > > +               ${IPT} -t nat -D "${parent_chain}"
> > > > > > > > > > -j ${chain};
> > > > > > > > > > +               ${IPT} -t nat -F ${chain};
> > > > > > > > > > +               ${IPT} -t nat -X ${chain};
> > > > > > > > > > +       else
> > > > > > > > > > +               return 1;
> > > > > > > > > > +       fi;
> > > > > > > > > > +       return 0;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +activate_custom_redirections() {
> > > > > > > > > > +       
> > > > > > > > > > +       local array=();
> > > > > > > > > > +       local redirects=();
> > > > > > > > > > +       local i;
> > > > > > > > > > +       index=();
> > > > > > > > > > +       iface=();
> > > > > > > > > > +       protocol=();
> > > > > > > > > > +       port=();
> > > > > > > > > > +       targetip=();
> > > > > > > > > > +       enabled=();
> > > > > > > > > > +
> > > > > > > > > > +       IFS=$'\n' read -d '' -ra redirects <
> > > > > > > > > > ${redirectsfile};
> > > > > > > > > > +
> > > > > > > > > > +       for i in "${!redirects[@]}"
> > > > > > > > > > +       do
> > > > > > > > > > +               IFS=$',' read -ra array <<<
> > > > > > > > > > ${redirects[i]};
> > > > > > > > > > +               index[i]=${array[0]};
> > > > > > > > > > +               iface[i]=${array[1]};
> > > > > > > > > > +               protocol[i]=${array[2]};
> > > > > > > > > > +               port[i]=${array[3]};
> > > > > > > > > > +               targetip[i]=${array[4]};
> > > > > > > > > > +               enabled[i]=${array[5]};
> > > > > > > > > > +       done
> > > > > > > > > > +
> > > > > > > > > > +       for i in "${!index[@]}"
> > > > > > > > > > +       do
> > > > > > > > > > +               if [[ ! -z "${GREEN_DEV}" &&
> > > > > > > > > >  "${iface[i]}" =
> > > > > > > > > > "green" && "${enabled[i]}" = "enabled" ]]; then
> > > > > > > > > > +
> > > > > > > > > > +                       logtext "add redirect in
> > > > > > > > > > ${chain} on
> > > > > > > > > > ${GREEN_DEV} ip ${targetip[i]} protocol
> > > > > > > > > > ${protocol[i]} port
> > > > > > > > > > ${port[i]} ";
> > > > > > > > > > +                       ${IPT} -t nat -A ${chain} -
> > > > > > > > > > i
> > > > > > > > > > ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
> > > > > > > > > > ${protocol[i]} --dport ${port[i]} -j RETURN;
> > > > > > > > > > +                       ${IPT} -t nat -A ${chain} -
> > > > > > > > > > i
> > > > > > > > > > ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --
> > > > > > > > > > dport
> > > > > > > > > > ${port[i]} -j REDIRECT;
> > > > > > > > > > +               fi
> > > > > > > > > > +               if [[ ! -z "${BLUE_DEV}" &&
> > > > > > > > > >  "${iface[i]}" =
> > > > > > > > > > "blue" && "${enabled[i]}" = "enabled" ]]; then
> > > > > > > > > > +                       logtext "add redirect in
> > > > > > > > > > ${chain} on
> > > > > > > > > > ${BLUE_DEV} ip ${targetip[i]} protocol
> > > > > > > > > > ${protocol[i]} port
> > > > > > > > > > ${port[i]} ";
> > > > > > > > > > +                       ${IPT} -t nat -A ${chain} -
> > > > > > > > > > i
> > > > > > > > > > ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
> > > > > > > > > > ${protocol[i]} --dport ${port[i]} -j RETURN;
> > > > > > > > > > +                       ${IPT} -t nat -A ${chain} -
> > > > > > > > > > i
> > > > > > > > > > ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --
> > > > > > > > > > dport
> > > > > > > > > > ${port[i]} -j REDIRECT;
> > > > > > > > > > +               fi
> > > > > > > > > > +               if [[ ! -z "${ORANGE_DEV}" &&
> > > > > > > > > >  "${iface[i]}" =
> > > > > > > > > > "orange" && "${enabled[i]}" = "enabled" ]]; then
> > > > > > > > > > +                       logtext "add redirect in
> > > > > > > > > > ${chain} on
> > > > > > > > > > ${ORANGE_DEV} ip ${targetip[i]} protocol
> > > > > > > > > > ${protocol[i]} port
> > > > > > > > > > ${port[i]} ";
> > > > > > > > > > +                       ${IPT} -t nat -A ${chain} -
> > > > > > > > > > i
> > > > > > > > > > ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -
> > > > > > > > > > m
> > > > > > > > > > ${protocol[i]} --dport ${port[i]} -j RETURN;
> > > > > > > > > > +                       ${IPT} -t nat -A ${chain} -
> > > > > > > > > > i
> > > > > > > > > > ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} -
> > > > > > > > > > -dport
> > > > > > > > > > ${port[i]} -j REDIRECT;
> > > > > > > > > > +               fi
> > > > > > > > > > +       done
> > > > > > > > > > +       unset array redirects i index iface
> > > > > > > > > > protocol port
> > > > > > > > > > targetip enabled;
> > > > > > > > > > +       return 0;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +activate_redirections() {
> > > > > > > > > > +
> > > > > > > > > > +       if ! create_chain; then return 1; fi;
> > > > > > > > > > +       
> > > > > > > > > > +       # Force DNS REDIRECTs on GREEN (udp, tcp,
> > > > > > > > > > 53)
> > > > > > > > > > +       if [[ "${REDIR_DNS_GREEN}" == "on" && 
> > > > > > > > > > "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${GREEN_DEV} -d
> > > > > > > > > > ${GREEN_ADDRESS} -p udp -m udp --dport domain -j
> > > > > > > > > > RETURN;
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${GREEN_DEV} -p
> > > > > > > > > > udp -m udp --dport domain -j REDIRECT;
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${GREEN_DEV} -d
> > > > > > > > > > ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j
> > > > > > > > > > RETURN;
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${GREEN_DEV} -p
> > > > > > > > > > tcp -m tcp --dport domain -j REDIRECT;
> > > > > > > > > > +       fi
> > > > > > > > > > +
> > > > > > > > > > +       # Force DNS REDIRECTs on BLUE (udp, tcp,
> > > > > > > > > > 53)
> > > > > > > > > > +       if [[ "${REDIR_DNS_BLUE}" == "on" && 
> > > > > > > > > > "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${BLUE_DEV} -d
> > > > > > > > > > ${BLUE_ADDRESS} -p udp -m udp --dport domain -j
> > > > > > > > > > RETURN
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${BLUE_DEV} -p udp
> > > > > > > > > > -m udp --dport domain -j REDIRECT
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${BLUE_DEV} -d
> > > > > > > > > > ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j
> > > > > > > > > > RETURN
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${BLUE_DEV} -p tcp
> > > > > > > > > > -m tcp --dport domain -j REDIRECT
> > > > > > > > > > +       fi
> > > > > > > > > > +
> > > > > > > > > > +       # Force DNS REDIRECTs on ORANGE (udp, tcp,
> > > > > > > > > > 53)
> > > > > > > > > > +       if [[ "${REDIR_DNS_ORANGE}" == "on" && 
> > > > > > > > > > "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${ORANGE_DEV} -d
> > > > > > > > > > ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j
> > > > > > > > > > RETURN
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${ORANGE_DEV} -p
> > > > > > > > > > udp -m udp --dport domain -j REDIRECT
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${ORANGE_DEV} -d
> > > > > > > > > > ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j
> > > > > > > > > > RETURN
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${ORANGE_DEV} -p
> > > > > > > > > > tcp -m tcp --dport domain -j REDIRECT
> > > > > > > > > > +       fi
> > > > > > > > > > +
> > > > > > > > > > +       # Force NTP REDIRECTs on GREEN (udp, 123)
> > > > > > > > > > +       if [[ "${REDIR_NTP_GREEN}" == "on" && 
> > > > > > > > > > "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${GREEN_DEV} -d
> > > > > > > > > > ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j
> > > > > > > > > > RETURN
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${GREEN_DEV} -p
> > > > > > > > > > udp -m udp --dport ntp -j REDIRECT
> > > > > > > > > > +       fi
> > > > > > > > > > +
> > > > > > > > > > +       # Force NTP REDIRECTs on BLUE (udp, 123)
> > > > > > > > > > +       if [[ "${REDIR_NTP_BLUE}" == "on" && 
> > > > > > > > > > "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${BLUE_DEV} -d
> > > > > > > > > > ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${BLUE_DEV} -p udp
> > > > > > > > > > -m udp --dport ntp -j REDIRECT
> > > > > > > > > > +       fi
> > > > > > > > > > +
> > > > > > > > > > +       # Force NTP REDIRECTs on ORANGE (udp, 123)
> > > > > > > > > > +       if [[ "${REDIR_NTP_ORANGE}" == "on" && 
> > > > > > > > > > "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${ORANGE_DEV} -d
> > > > > > > > > > ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j
> > > > > > > > > > RETURN
> > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > ${ORANGE_DEV} -p
> > > > > > > > > > udp -m udp --dport ntp -j REDIRECT
> > > > > > > > > > +       fi
> > > > > > > > > > +
> > > > > > > > > > +       if ! activate_custom_redirections; then
> > > > > > > > > > return 1; fi;
> > > > > > > > > > +
> > > > > > > > > > +       return 0;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +case "${1}" in
> > > > > > > > > > +       start)
> > > > > > > > > > +               boot_mesg "Loading port
> > > > > > > > > > redirections..."
> > > > > > > > > > +               activate_redirections;
> > > > > > > > > > +               evaluate_retval;
> > > > > > > > > > +               ;;
> > > > > > > > > > +
> > > > > > > > > > +       stop)   
> > > > > > > > > > +               boot_mesg "Removing port
> > > > > > > > > > redirections..."
> > > > > > > > > > +               remove_chain;
> > > > > > > > > > +               evaluate_retval;
> > > > > > > > > > +               ;;
> > > > > > > > > > +
> > > > > > > > > > +       restart)
> > > > > > > > > > +               ${0} stop
> > > > > > > > > > +               ${0} start
> > > > > > > > > > +               ;;
> > > > > > > > > > +
> > > > > > > > > > +       *)
> > > > > > > > > > +               echo "Usage: ${0}
> > > > > > > > > > {start|stop|restart}"
> > > > > > > > > > +               exit 1
> > > > > > > > > > +               ;;
> > > > > > > > > > +esac
> > > > > > > > > > +
> > > > > > > > > > +# End $rc_base/init.d/portredir
> > > > > > > > > > diff --git a/src/misc-progs/Makefile b/src/misc-
> > > > > > > > > > progs/Makefile
> > > > > > > > > > index 7c3ef7529..850f8fdcc 100644
> > > > > > > > > > --- a/src/misc-progs/Makefile
> > > > > > > > > > +++ b/src/misc-progs/Makefile
> > > > > > > > > > @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl
> > > > > > > > > > ipfirereboot \
> > > > > > > > > >        wirelessctrl getipstat qosctrl \
> > > > > > > > > >        redctrl syslogdctrl extrahdctrl sambactrl \
> > > > > > > > > >        smartctrl clamavctrl addonctrl pakfire
> > > > > > > > > > mpfirectrl
> > > > > > > > > > wlanapctrl \
> > > > > > > > > > -       setaliases urlfilterctrl updxlratorctrl
> > > > > > > > > > fireinfoctrl
> > > > > > > > > > rebuildroutes \
> > > > > > > > > > +       setaliases urlfilterctrl updxlratorctrl
> > > > > > > > > > fireinfoctrl
> > > > > > > > > > rebuildroutes portredirctrl \
> > > > > > > > > >        getconntracktable wirelessclient torctrl
> > > > > > > > > > ddnsctrl
> > > > > > > > > > unboundctrl \
> > > > > > > > > >        captivectrl
> > > > > > > > > > 
> > > > > > > > > > diff --git a/src/misc-progs/portredirctrl.c
> > > > > > > > > > b/src/misc-
> > > > > > > > > > progs/portredirctrl.c
> > > > > > > > > > new file mode 100644
> > > > > > > > > > index 000000000..7897d711c
> > > > > > > > > > --- /dev/null
> > > > > > > > > > +++ b/src/misc-progs/portredirctrl.c
> > > > > > > > > > @@ -0,0 +1,47 @@
> > > > > > > > > > +/* This file is part of the IPFire Firewall.
> > > > > > > > > > + *
> > > > > > > > > > + * This program is distributed under the terms of
> > > > > > > > > > the GNU
> > > > > > > > > > General Public
> > > > > > > > > > + * Licence.  See the file COPYING for details.
> > > > > > > > > > + *
> > > > > > > > > > + */
> > > > > > > > > > +
> > > > > > > > > > +#include <stdlib.h>
> > > > > > > > > > +#include <stdio.h>
> > > > > > > > > > +#include <string.h>
> > > > > > > > > > +#include <unistd.h>
> > > > > > > > > > +#include <sys/types.h>
> > > > > > > > > > +#include <fcntl.h>
> > > > > > > > > > +#include "setuid.h"
> > > > > > > > > > +
> > > > > > > > > > +int main(int argc, char *argv[]) {
> > > > > > > > > > +       if (!(initsetuid()))
> > > > > > > > > > +               exit(1);
> > > > > > > > > > +
> > > > > > > > > > +       // Check what command is asked
> > > > > > > > > > +        if (argc < 2) {
> > > > > > > > > > +                fprintf(stderr, "\nNo argument
> > > > > > > > > > given.\n\nportredirctrl
> > > > > > > > > > (start|stop|restart|enable|disable)\n\n");
> > > > > > > > > > +                exit(1);
> > > > > > > > > > +        }
> > > > > > > > > > +
> > > > > > > > > > +        if (strcmp(argv[1], "start") == 0) {
> > > > > > > > > > +
> > > > > > > > > >                safe_system("/etc/rc.d/init.d/portre
> > > > > > > > > > dir
> > > > > > > > > > start");
> > > > > > > > > > +        } else if (strcmp(argv[1], "stop") == 0) {
> > > > > > > > > > +
> > > > > > > > > >                safe_system("/etc/rc.d/init.d/portre
> > > > > > > > > > dir
> > > > > > > > > > stop");
> > > > > > > > > > +        } else if (strcmp(argv[1], "restart") ==
> > > > > > > > > > 0) {
> > > > > > > > > > +
> > > > > > > > > >                safe_system("/etc/rc.d/init.d/portre
> > > > > > > > > > dir
> > > > > > > > > > restart");
> > > > > > > > > > +       } else if (strcmp(argv[1], "enable") == 0)
> > > > > > > > > > {
> > > > > > > > > > +               safe_system("touch
> > > > > > > > > > /var/ipfire/portredir/enable");
> > > > > > > > > > +               safe_system("ln -snf
> > > > > > > > > > /etc/rc.d/init.d/portredir
> > > > > > > > > > /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
> > > > > > > > > > +               safe_system("ln -snf
> > > > > > > > > > /etc/rc.d/init.d/portredir
> > > > > > > > > > /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
> > > > > > > > > > +               safe_system("ln -snf
> > > > > > > > > > /etc/rc.d/init.d/portredir
> > > > > > > > > > /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
> > > > > > > > > > +       } else if (strcmp(argv[1], "disable") == 0)
> > > > > > > > > > {
> > > > > > > > > > +
> > > > > > > > > >               safe_system("/etc/rc.d/init.d/portred
> > > > > > > > > > ir stop");
> > > > > > > > > > +               safe_system("unlink
> > > > > > > > > > /var/ipfire/portredir/enable");
> > > > > > > > > > +               safe_system("rm -rf
> > > > > > > > > > /etc/rc.d/rc*.d/*portredir
> > > > > > > > > > > /dev/null 2>&1");
> > > > > > > > > > +        } else {
> > > > > > > > > > +                fprintf(stderr, "\nBad argument
> > > > > > > > > > given.\n\nportredirctrl
> > > > > > > > > > (start|stop|restart|enable|disable)\n\n");
> > > > > > > > > > +                exit(1);
> > > > > > > > > > +        }
> > > > > > > > > > +
> > > > > > > > > > +       return 0;
> > > > > > > > > > +}
> > > > > > > > > > diff --git a/src/paks/portredir/install.sh
> > > > > > > > > > b/src/paks/portredir/install.sh
> > > > > > > > > > new file mode 100644
> > > > > > > > > > index 000000000..9f69aeae2
> > > > > > > > > > --- /dev/null
> > > > > > > > > > +++ b/src/paks/portredir/install.sh
> > > > > > > > > > @@ -0,0 +1,32 @@
> > > > > > > > > > +#!/bin/bash
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > ##############
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +# This file is part of the IPFire
> > > > > > > > > > Firewall.                                #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +# IPFire is free software; you can redistribute it
> > > > > > > > > > and/or
> > > > > > > > > > modify           #
> > > > > > > > > > +# it under the terms of the GNU General Public
> > > > > > > > > > License as
> > > > > > > > > > published by     #
> > > > > > > > > > +# the Free Software Foundation; either version 2
> > > > > > > > > > of the
> > > > > > > > > > License, or        #
> > > > > > > > > > +# (at your option) any later
> > > > > > > > > > version.                                      #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +# IPFire is distributed in the hope that it will
> > > > > > > > > > be
> > > > > > > > > > useful,                #
> > > > > > > > > > +# but WITHOUT ANY WARRANTY; without even the
> > > > > > > > > > implied warranty
> > > > > > > > > > of           #
> > > > > > > > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR
> > > > > > > > > > PURPOSE.  See
> > > > > > > > > > the            #
> > > > > > > > > > +# GNU General Public License for more
> > > > > > > > > > details.                             #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +# You should have received a copy of the GNU
> > > > > > > > > > General Public
> > > > > > > > > > License        #
> > > > > > > > > > +# along with IPFire; if not, write to the Free
> > > > > > > > > > Software                    #
> > > > > > > > > > +# Foundation, Inc., 59 Temple Place, Suite 330,
> > > > > > > > > > Boston, MA 
> > > > > > > > > > 02111-1307 USA #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +# Copyright (C) 2021 IPFire-Team
> > > > > > > > > > <info@ipfire.org>.                        #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > ##############
> > > > > > > > > > +#
> > > > > > > > > > +. /opt/pakfire/lib/functions.sh
> > > > > > > > > > +extract_files
> > > > > > > > > > +restore_backup ${NAME}
> > > > > > > > > > +
> > > > > > > > > > +/usr/local/bin/update-lang-cache
> > > > > > > > > > +
> > > > > > > > > > +chown root:nobody /usr/local/bin/portredirctrl
> > > > > > > > > > +chmod 4750 /usr/local/bin/portredirctrl
> > > > > > > > > > +chmod u+s /usr/local/bin/portredirctrl
> > > > > > > > > > diff --git a/src/paks/portredir/uninstall.sh
> > > > > > > > > > b/src/paks/portredir/uninstall.sh
> > > > > > > > > > new file mode 100644
> > > > > > > > > > index 000000000..df9270125
> > > > > > > > > > --- /dev/null
> > > > > > > > > > +++ b/src/paks/portredir/uninstall.sh
> > > > > > > > > > @@ -0,0 +1,28 @@
> > > > > > > > > > +#!/bin/bash
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > ##############
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +# This file is part of the IPFire
> > > > > > > > > > Firewall.                                #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +# IPFire is free software; you can redistribute it
> > > > > > > > > > and/or
> > > > > > > > > > modify           #
> > > > > > > > > > +# it under the terms of the GNU General Public
> > > > > > > > > > License as
> > > > > > > > > > published by     #
> > > > > > > > > > +# the Free Software Foundation; either version 2
> > > > > > > > > > of the
> > > > > > > > > > License, or        #
> > > > > > > > > > +# (at your option) any later
> > > > > > > > > > version.                                      #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +# IPFire is distributed in the hope that it will
> > > > > > > > > > be
> > > > > > > > > > useful,                #
> > > > > > > > > > +# but WITHOUT ANY WARRANTY; without even the
> > > > > > > > > > implied warranty
> > > > > > > > > > of           #
> > > > > > > > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR
> > > > > > > > > > PURPOSE.  See
> > > > > > > > > > the            #
> > > > > > > > > > +# GNU General Public License for more
> > > > > > > > > > details.                             #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +# You should have received a copy of the GNU
> > > > > > > > > > General Public
> > > > > > > > > > License        #
> > > > > > > > > > +# along with IPFire; if not, write to the Free
> > > > > > > > > > Software                    #
> > > > > > > > > > +# Foundation, Inc., 59 Temple Place, Suite 330,
> > > > > > > > > > Boston, MA 
> > > > > > > > > > 02111-1307 USA #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +# Copyright (C) 2007 IPFire-Team
> > > > > > > > > > <info@ipfire.org>.                        #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > ##############
> > > > > > > > > > +#
> > > > > > > > > > +. /opt/pakfire/lib/functions.sh
> > > > > > > > > > +make_backup ${NAME}
> > > > > > > > > > +remove_files
> > > > > > > > > > +
> > > > > > > > > > +/usr/local/bin/update-lang-cache
> > > > > > > > > > diff --git a/src/paks/portredir/update.sh
> > > > > > > > > > b/src/paks/portredir/update.sh
> > > > > > > > > > new file mode 100644
> > > > > > > > > > index 000000000..89c40d0d7
> > > > > > > > > > --- /dev/null
> > > > > > > > > > +++ b/src/paks/portredir/update.sh
> > > > > > > > > > @@ -0,0 +1,26 @@
> > > > > > > > > > +#!/bin/bash
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > ##############
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +# This file is part of the IPFire
> > > > > > > > > > Firewall.                                #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +# IPFire is free software; you can redistribute it
> > > > > > > > > > and/or
> > > > > > > > > > modify           #
> > > > > > > > > > +# it under the terms of the GNU General Public
> > > > > > > > > > License as
> > > > > > > > > > published by     #
> > > > > > > > > > +# the Free Software Foundation; either version 2
> > > > > > > > > > of the
> > > > > > > > > > License, or        #
> > > > > > > > > > +# (at your option) any later
> > > > > > > > > > version.                                      #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +# IPFire is distributed in the hope that it will
> > > > > > > > > > be
> > > > > > > > > > useful,                #
> > > > > > > > > > +# but WITHOUT ANY WARRANTY; without even the
> > > > > > > > > > implied warranty
> > > > > > > > > > of           #
> > > > > > > > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR
> > > > > > > > > > PURPOSE.  See
> > > > > > > > > > the            #
> > > > > > > > > > +# GNU General Public License for more
> > > > > > > > > > details.                             #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +# You should have received a copy of the GNU
> > > > > > > > > > General Public
> > > > > > > > > > License        #
> > > > > > > > > > +# along with IPFire; if not, write to the Free
> > > > > > > > > > Software                    #
> > > > > > > > > > +# Foundation, Inc., 59 Temple Place, Suite 330,
> > > > > > > > > > Boston, MA 
> > > > > > > > > > 02111-1307 USA #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +# Copyright (C) 2007 IPFire-Team
> > > > > > > > > > <info@ipfire.org>.                        #
> > > > > > > > > > +#
> > > > > > > > > >                                                    
> > > > > > > > > >          
> > > > > > > > > >             #
> > > > > > > > > > +##################################################
> > > > > > > > > > ############
> > > > > > > > > > ##############
> > > > > > > > > > +#
> > > > > > > > > > +. /opt/pakfire/lib/functions.sh
> > > > > > > > > > +./uninstall.sh
> > > > > > > > > > +./install.sh
> > > > > > > > > > -- 
> > > > > > > > > > 2.18.0
> > > > > > > > > > 
> > > > > > > > > 
> > > > > > > > 
> > > > > > > 
> > > > > > 
> > > > > 
> > > > 
> > > 
> > 
>
  
Stefan Schantl July 5, 2021, 12:08 p.m. UTC | #12
Hello Jon,

yes please file both as bugs on bugzilla.

Thanks in advance,

-Stefan
> Stefan,
> 
> Please let me know if I should enter this (and the one below) in
> bugzilla.
> 
> On other issue (small).  If I disable logging (and click apply
> changes) the logging does not stop. 
> 
> 
> (One image enclosed)
> 
> So when the DNS redirect firewall rule is on, then there are
> bazillions of log entries.  Not sure how to disable.  
> 
> Also, it looks like the iptables are correct.  Looking at the 2nd
> image: the left side - logging disabled.  Right is logging enabled.
> 
> 
> (One more image enclosed)
> 
> 
> Jon
> 
> 
> 
> 
> > On Jul 1, 2021, at 10:55 AM, Jon Murphy <jcmurphy26@gmail.com>
> > wrote:
> > 
> > 
> > Hmmm.  I tried an ALL (a.k.a ANY) instead and I think it works.
> >  But there is a bug in the UI.
> > 
> > 
> > This is what I tried:
> > 
> > <Screen Shot 2021-07-01 at 10.48.15 AM.png>
> > 
> > <Screen Shot 2021-07-01 at 10.50.43 AM.png>
> > 
> > (Two images enclosed)
> > 
> > 
> > But, when I go to edit this rule the Destination Firewall changes
> > from ALL to GREEN.  So I thought it wasn’t accepted.
> > 
> > <Screen Shot 2021-07-01 at 10.52.05 AM.png>
> > 
> > (One more image enclosed)
> > 
> > 
> > Jon
> > 
> > 
> > 
> > > On Jul 1, 2021, at 10:24 AM, Michael Tremer
> > > <michael.tremer@ipfire.org> wrote:
> > > 
> > > @Stefan: There should be an exception in the UI that these rules
> > > can be created when REDIRECT is being used.
> > > 
> > > > On 1 Jul 2021, at 16:04, Jon Murphy <jcmurphy26@gmail.com>
> > > > wrote:
> > > > 
> > > > > You probably want “any” as destination.
> > > > 
> > > > Those are the only choices that allow a Save.  When I enter an
> > > > IP address I get this error:
> > > > 
> > > > 
> > > > Error messages
> > > > 
> > > > Source and destination IP addresses are from the same subnet.  
> > > > 
> > > > 
> > > > 
> > > > Jon
> > > > 
> > > > > On Jul 1, 2021, at 3:08 AM, Michael Tremer
> > > > > <michael.tremer@ipfire.org> wrote:
> > > > > 
> > > > > Hey Jon,
> > > > > 
> > > > > You probably want “any” as destination.
> > > > > 
> > > > > -Michael
> > > > > 
> > > > > > On 1 Jul 2021, at 04:08, Jon Murphy <jcmurphy26@gmail.com>
> > > > > > wrote:
> > > > > > 
> > > > > > Hi Stefan,
> > > > > > 
> > > > > > Thank you for taking this on!
> > > > > > 
> > > > > > I applied the patchwork.ipfire patch.
> > > > > > 
> > > > > > I think I entered something wrong since I cannot get things
> > > > > > to work.  I tried both with Destination Firewall GREEN &
> > > > > > Firewall RED.
> > > > > > 
> > > > > > 
> > > > > > Does the Firewall Rule seem right?
> > > > > > 
> > > > > > Best regards,
> > > > > > Jon
> > > > > > 
> > > > > > 
> > > > > > Here is the rule I set up:
> > > > > > 
> > > > > > 
> > > > > > <Screen Shot 2021-06-30 at 9.53.52 PM.png>
> > > > > > 
> > > > > > <Screen Shot 2021-06-30 at 9.53.10 PM.png>
> > > > > > 
> > > > > > 
> > > > > > And this is what I see with conntrack:
> > > > > > 
> > > > > > conntrack -E -e NEW,UPDATE | grep -e "=53 "
> > > > > > 
> > > > > >    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4
> > > > > > sport=51169 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10
> > > > > > sport=53 dport=51169
> > > > > >    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4
> > > > > > sport=54168 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10
> > > > > > sport=53 dport=54168
> > > > > >    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4
> > > > > > sport=56094 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10
> > > > > > sport=53 dport=56094
> > > > > >    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4
> > > > > > sport=52964 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10
> > > > > > sport=53 dport=52964
> > > > > >    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4
> > > > > > sport=53279 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10
> > > > > > sport=53 dport=53279
> > > > > >    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4
> > > > > > sport=61657 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10
> > > > > > sport=53 dport=61657
> > > > > >    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4
> > > > > > sport=57723 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10
> > > > > > sport=53 dport=57723
> > > > > > 
> > > > > > 
> > > > > > 
> > > > > > 
> > > > > > > On Jun 30, 2021, at 2:14 PM, Stefan Schantl
> > > > > > > <stefan.schantl@ipfire.org> wrote:
> > > > > > > 
> > > > > > > Hello Matthias, Hello Michael, Hello Jon, Hello *,
> > > > > > > 
> > > > > > > I've followed the conversation on this list since the
> > > > > > > first mail and
> > > > > > > thoughts about forcing DNS traffic to use the local
> > > > > > > resolver.
> > > > > > > 
> > > > > > > It was a very long journey and lot of time and work has
> > > > > > > been spent to
> > > > > > > get to the present point.
> > > > > > > 
> > > > > > > As Michael requested here, I've digged through the lines
> > > > > > > of the perl
> > > > > > > script which is responsible for creating the firewall
> > > > > > > rules and
> > > > > > > surprisingly found that everyting which is needed to
> > > > > > > create generic
> > > > > > > REDIRECT rules already was written in the past - it just
> > > > > > > did not work
> > > > > > > as designed/expected.
> > > > > > > 
> > > > > > > Finaly I was able to adjust these lines of code and to
> > > > > > > repair that
> > > > > > > feature.
> > > > > > > 
> > > > > > > A redirect rule can be created by picking a single host
> > > > > > > or group of
> > > > > > > hosts or entire network(s) as source, selecting NAT
> > > > > > > (DNAT) and choosing
> > > > > > > the Firewall itself as target.
> > > > > > > 
> > > > > > > The protocol or service or service group which should be
> > > > > > > redirected has
> > > > > > > to be selected afterwards. If you want to redirect a
> > > > > > > given port to
> > > > > > > another one it can be specified as "Target port".
> > > > > > > 
> > > > > > > All created redirect rules are displayed as "input
> > > > > > > rules".
> > > > > > > 
> > > > > > > 
> > > > > > > The patch directly can be accessed here:
> > > > > > > 
> > > > > > > https://patchwork.ipfire.org/project/ipfire/patch/20210630184031.7726-1-stefan.schantl@ipfire.org/
> > > > > > > 
> > > > > > > Best regards,
> > > > > > > 
> > > > > > > -Stefan
> > > > > > > 
> > > > > > > > Hello,
> > > > > > > > 
> > > > > > > > > On 28 Jun 2021, at 18:53, Jon Murphy
> > > > > > > > > <jcmurphy26@gmail.com> wrote:
> > > > > > > > > 
> > > > > > > > > Hi Michael!  Happy Monday!
> > > > > > > > > 
> > > > > > > > > 
> > > > > > > > > > Why do we not extend the firewall UI probably by
> > > > > > > > > > about 20 lines
> > > > > > > > > > of code instead of adding many hundreds of lines?
> > > > > > > > > > 
> > > > > > > > > > Please can someone elaborate on this more?
> > > > > > > > > 
> > > > > > > > > Doing a DNS redirect, via the WegBUI, has been an
> > > > > > > > > issue since
> > > > > > > > > 2015.  I found this quote in the old forum:
> > > > > > > > > 
> > > > > > > > > "Having investigated a bit more I have concluded that
> > > > > > > > > it's not
> > > > > > > > > currently possible to create such rules through the
> > > > > > > > > WUI.
> > > > > > > > > 
> > > > > > > > > There are a number of obstacles:
> > > > > > > > > 1. It is not allowed to create a rule where source IP
> > > > > > > > > and
> > > > > > > > > destination nat IP is on the same subnetwork (e.g.
> > > > > > > > > GREEN), WUI
> > > > > > > > > error message: "Source and destination IP addresses
> > > > > > > > > are from the
> > > > > > > > > same subnet."
> > > > > > > > > 
> > > > > > > > > 2. WUI will not allow you to create a rule without a
> > > > > > > > > destination
> > > > > > > > > (the filtered packet must adhere to a destination,
> > > > > > > > > not only a port)
> > > > > > > > > and the destination MUST be an IP address of one of
> > > > > > > > > the IPFire
> > > > > > > > > interfaces, which limits whats possible a great
> > > > > > > > > deal." 
> > > > > > > > 
> > > > > > > > And these cannot be changed?
> > > > > > > > 
> > > > > > > > > And I found this from 2016:
> > > > > > > > > https://bugzilla.ipfire.org/show_bug.cgi?id=11168
> > > > > > > > > 
> > > > > > > > > So I am guessing that no one has been able to
> > > > > > > > > determine a way to
> > > > > > > > > extend the WebGUI.  
> > > > > > > > 
> > > > > > > > Has anyone tried? I do not see any obvious reasons why
> > > > > > > > this should
> > > > > > > > not be possible.
> > > > > > > > 
> > > > > > > > > I am curious - Who created the 
> > > > > > > > > https://ipfire:444/cgi-bin/firewall.cgi page?  And
> > > > > > > > > could they help?
> > > > > > > > 
> > > > > > > > -Michael
> > > > > > > > 
> > > > > > > > > Jon
> > > > > > > > > 
> > > > > > > > > 
> > > > > > > > > > On Jun 28, 2021, at 11:04 AM, Michael Tremer <
> > > > > > > > > > michael.tremer@ipfire.org> wrote:
> > > > > > > > > > 
> > > > > > > > > > Hello Matthias,
> > > > > > > > > > 
> > > > > > > > > > > On 27 Jun 2021, at 14:48, Matthias Fischer <
> > > > > > > > > > > matthias.fischer@ipfire.org> wrote:
> > > > > > > > > > > 
> > > > > > > > > > > From: Marcel Lorenz <marcel.lorenz@ipfire.org>
> > > > > > > > > > 
> > > > > > > > > > Thank you for sending this patch on Marcel’s
> > > > > > > > > > behalf, but I would
> > > > > > > > > > much more prefer if he would submit his patches on
> > > > > > > > > > his own. I do
> > > > > > > > > > not see why that isn’t possible.
> > > > > > > > > > 
> > > > > > > > > > > Please note:
> > > > > > > > > > > This is a new addon written by Marcel Lorenz <
> > > > > > > > > > > marcel.lorenz@ipfire.org>.
> > > > > > > > > > > 
> > > > > > > > > > > It adds a new GUI to IPFire for DNS/NTP *and*
> > > > > > > > > > > user specific
> > > > > > > > > > > port redirections.
> > > > > > > > > > > 
> > > > > > > > > > > How its working:
> > > > > > > > > > > It has exactly the same functionalities as
> > > > > > > > > > > "Forcing
> > > > > > > > > > > DNS/NTP..."  - and some more.
> > > > > > > > > > > 
> > > > > > > > > > > By setting switches, DNS/NTP requests are
> > > > > > > > > > > automatically
> > > > > > > > > > > redirected to the local IPFire DNS/NTP servers.
> > > > > > > > > > > 
> > > > > > > > > > > Additionally, the user can specify custom
> > > > > > > > > > > redirections.
> > > > > > > > > > > 
> > > > > > > > > > > These rules are added to a new chain in
> > > > > > > > > > > PREROUTING =>
> > > > > > > > > > > PORT_REDIRECT.
> > > > > > > > > > > 
> > > > > > > > > > > To avoid problems with (e.g.) transparent 'squid'
> > > > > > > > > > > configurations,
> > > > > > > > > > > redirection rules are added automatically before
> > > > > > > > > > > existing
> > > > > > > > > > > 'squid' rules.
> > > > > > > > > > 
> > > > > > > > > > This message does unfortunately not say why this
> > > > > > > > > > add-on would be
> > > > > > > > > > useful. I am emphasising this again and again that
> > > > > > > > > > it is not very
> > > > > > > > > > important how something is done specially. That
> > > > > > > > > > should be
> > > > > > > > > > commented in the code and other implementation
> > > > > > > > > > details should
> > > > > > > > > > also be documented there.
> > > > > > > > > > 
> > > > > > > > > > As I have stated on this functionality many times
> > > > > > > > > > before, I do
> > > > > > > > > > not see why this is necessary at all.
> > > > > > > > > > 
> > > > > > > > > > Why is this an add-on?
> > > > > > > > > > 
> > > > > > > > > > Why do we not extend the firewall UI probably by
> > > > > > > > > > about 20 lines
> > > > > > > > > > of code instead of adding many hundreds of lines?
> > > > > > > > > > 
> > > > > > > > > > Please can someone elaborate on this more?
> > > > > > > > > > 
> > > > > > > > > > -Michael
> > > > > > > > > > 
> > > > > > > > > > > Signed-off-by: Matthias Fischer
> > > > > > > > > > > <matthias.fischer@ipfire.org>
> > > > > > > > > > > ---
> > > > > > > > > > > config/portredir/EX-portredir.menu    |   6 +
> > > > > > > > > > > config/portredir/lang/portredir.de.pl |  19 +
> > > > > > > > > > > config/portredir/lang/portredir.en.pl |  19 +
> > > > > > > > > > > config/portredir/portredir-backup     |   1 +
> > > > > > > > > > > config/portredir/portredir.cgi        | 525
> > > > > > > > > > > ++++++++++++++++++++++++++
> > > > > > > > > > > config/rootfiles/common/misc-progs    |   1 +
> > > > > > > > > > > config/rootfiles/packages/portredir   |  11 +
> > > > > > > > > > > lfs/portredir                         |  85 +++++
> > > > > > > > > > > make.sh                               |   1 +
> > > > > > > > > > > src/initscripts/packages/portredir    | 191
> > > > > > > > > > > ++++++++++
> > > > > > > > > > > src/misc-progs/Makefile               |   2 +-
> > > > > > > > > > > src/misc-progs/portredirctrl.c        |  47 +++
> > > > > > > > > > > src/paks/portredir/install.sh         |  32 ++
> > > > > > > > > > > src/paks/portredir/uninstall.sh       |  28 ++
> > > > > > > > > > > src/paks/portredir/update.sh          |  26 ++
> > > > > > > > > > > 15 files changed, 993 insertions(+), 1 deletion(-
> > > > > > > > > > > )
> > > > > > > > > > > create mode 100644 config/portredir/EX-
> > > > > > > > > > > portredir.menu
> > > > > > > > > > > create mode 100644
> > > > > > > > > > > config/portredir/lang/portredir.de.pl
> > > > > > > > > > > create mode 100644
> > > > > > > > > > > config/portredir/lang/portredir.en.pl
> > > > > > > > > > > create mode 100644 config/portredir/portredir-
> > > > > > > > > > > backup
> > > > > > > > > > > create mode 100644 config/portredir/portredir.cgi
> > > > > > > > > > > create mode 100644
> > > > > > > > > > > config/rootfiles/packages/portredir
> > > > > > > > > > > create mode 100644 lfs/portredir
> > > > > > > > > > > create mode 100644
> > > > > > > > > > > src/initscripts/packages/portredir
> > > > > > > > > > > create mode 100644 src/misc-progs/portredirctrl.c
> > > > > > > > > > > create mode 100644 src/paks/portredir/install.sh
> > > > > > > > > > > create mode 100644
> > > > > > > > > > > src/paks/portredir/uninstall.sh
> > > > > > > > > > > create mode 100644 src/paks/portredir/update.sh
> > > > > > > > > > > 
> > > > > > > > > > > diff --git a/config/portredir/EX-portredir.menu
> > > > > > > > > > > b/config/portredir/EX-portredir.menu
> > > > > > > > > > > new file mode 100644
> > > > > > > > > > > index 000000000..8376e8053
> > > > > > > > > > > --- /dev/null
> > > > > > > > > > > +++ b/config/portredir/EX-portredir.menu
> > > > > > > > > > > @@ -0,0 +1,6 @@
> > > > > > > > > > > +    $subfirewall->{'95.portredir'} = {
> > > > > > > > > > > +                               'caption' =>
> > > > > > > > > > > $Lang::tr{'portredir port redirections'},
> > > > > > > > > > > +                               'uri' => '/cgi-
> > > > > > > > > > > bin/portredir.cgi',
> > > > > > > > > > > +                               'title' =>
> > > > > > > > > > > "$Lang::tr{'portredir port redirections'}",
> > > > > > > > > > > +                               'enabled' => 1
> > > > > > > > > > > +                               };
> > > > > > > > > > > diff --git
> > > > > > > > > > > a/config/portredir/lang/portredir.de.pl
> > > > > > > > > > > b/config/portredir/lang/portredir.de.pl
> > > > > > > > > > > new file mode 100644
> > > > > > > > > > > index 000000000..b932d4a85
> > > > > > > > > > > --- /dev/null
> > > > > > > > > > > +++ b/config/portredir/lang/portredir.de.pl
> > > > > > > > > > > @@ -0,0 +1,19 @@
> > > > > > > > > > > +%tr = (
> > > > > > > > > > > +%tr,
> > > > > > > > > > > +'portredir enable addon' => 'Addon aktivieren',
> > > > > > > > > > > +'portredir common settings' => 'Allgemeine
> > > > > > > > > > > Einstellungen',
> > > > > > > > > > > +'portredir port redirections' =>
> > > > > > > > > > > 'Portumleitungen',
> > > > > > > > > > > +'portredir fw for interface' =>
> > > > > > > > > > > 'Firewalloptionen für das
> > > > > > > > > > > Interface',
> > > > > > > > > > > +'portredir enable user redirections' =>
> > > > > > > > > > > 'Aktiviere
> > > > > > > > > > > benutzerdefinierte Portumleitungen',
> > > > > > > > > > > +'portredir force local dns' => 'Erzwinge lokale
> > > > > > > > > > > DNS-Server',
> > > > > > > > > > > +'portredir force local ntp' => 'Erzwinge lokale
> > > > > > > > > > > NTP-Server',
> > > > > > > > > > > +'portredir custom redirections' =>
> > > > > > > > > > > 'Benutzerdefinierte
> > > > > > > > > > > Portumleitungen',
> > > > > > > > > > > +'portredir remove rule' => 'Entferne Regel',
> > > > > > > > > > > +'portredir add rule' => 'Hinzufügen',
> > > > > > > > > > > +'portredir no entries' => 'Keine Einträge
> > > > > > > > > > > vorhanden.',
> > > > > > > > > > > +'portredir invalid address' => 'Ungültige Host-
> > > > > > > > > > > Addresse.',
> > > > > > > > > > > +'portredir empty input' => 'Fehlende Angabe:
> > > > > > > > > > > Bitte geben Sie
> > > > > > > > > > > einen gültigen Host an.',
> > > > > > > > > > > +'portredir save to activate' => 'Speichern, um
> > > > > > > > > > > Änderungen zu
> > > > > > > > > > > aktivieren',
> > > > > > > > > > > +);
> > > > > > > > > > > +
> > > > > > > > > > > +#EOF
> > > > > > > > > > > diff --git
> > > > > > > > > > > a/config/portredir/lang/portredir.en.pl
> > > > > > > > > > > b/config/portredir/lang/portredir.en.pl
> > > > > > > > > > > new file mode 100644
> > > > > > > > > > > index 000000000..f442f3eaa
> > > > > > > > > > > --- /dev/null
> > > > > > > > > > > +++ b/config/portredir/lang/portredir.en.pl
> > > > > > > > > > > @@ -0,0 +1,19 @@
> > > > > > > > > > > +%tr = (
> > > > > > > > > > > +%tr,
> > > > > > > > > > > +'portredir enable addon' => 'Enable addon',
> > > > > > > > > > > +'portredir common settings' => 'Common
> > > > > > > > > > > settings',
> > > > > > > > > > > +'portredir port redirections' => 'Port
> > > > > > > > > > > redirections',
> > > > > > > > > > > +'portredir fw for interface' => 'Firewall
> > > > > > > > > > > options for
> > > > > > > > > > > interface',
> > > > > > > > > > > +'portredir enable user redirections' => 'Enable
> > > > > > > > > > > user port
> > > > > > > > > > > redirections',
> > > > > > > > > > > +'portredir force local dns' => 'Enforce local
> > > > > > > > > > > DNS servers',
> > > > > > > > > > > +'portredir force local ntp' => 'Enforce local
> > > > > > > > > > > NTP servers',
> > > > > > > > > > > +'portredir custom redirections' => 'Custom port
> > > > > > > > > > > redirections',
> > > > > > > > > > > +'portredir remove rule' => 'Remove rule',
> > > > > > > > > > > +'portredir add rule' => 'Add new',
> > > > > > > > > > > +'portredir no entries' => 'No entries at the
> > > > > > > > > > > moment.',
> > > > > > > > > > > +'portredir invalid address' => 'Invalid host
> > > > > > > > > > > address.',
> > > > > > > > > > > +'portredir empty input' => 'Empty input: Please
> > > > > > > > > > > enter a valid
> > > > > > > > > > > host.',
> > > > > > > > > > > +'portredir save to activate' => 'Save to
> > > > > > > > > > > activate changes',
> > > > > > > > > > > +);
> > > > > > > > > > > +
> > > > > > > > > > > +#EOF
> > > > > > > > > > > diff --git a/config/portredir/portredir-backup
> > > > > > > > > > > b/config/portredir/portredir-backup
> > > > > > > > > > > new file mode 100644
> > > > > > > > > > > index 000000000..bd2ada742
> > > > > > > > > > > --- /dev/null
> > > > > > > > > > > +++ b/config/portredir/portredir-backup
> > > > > > > > > > > @@ -0,0 +1 @@
> > > > > > > > > > > +/var/ipfire/portredir
> > > > > > > > > > > diff --git a/config/portredir/portredir.cgi
> > > > > > > > > > > b/config/portredir/portredir.cgi
> > > > > > > > > > > new file mode 100644
> > > > > > > > > > > index 000000000..4913dda3f
> > > > > > > > > > > --- /dev/null
> > > > > > > > > > > +++ b/config/portredir/portredir.cgi
> > > > > > > > > > > @@ -0,0 +1,525 @@
> > > > > > > > > > > +#!/usr/bin/perl
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > #################
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >                #
> > > > > > > > > > > +# IPFire.org - A linux based
> > > > > > > > > > > firewall
> > > > > > > > > > >                                         #
> > > > > > > > > > > +# Copyright (C) 2021  IPFire Team 
> > > > > > > > > > > <info@ipfire.org>                          #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >                #
> > > > > > > > > > > +# This program is free software: you can
> > > > > > > > > > > redistribute it
> > > > > > > > > > > and/or modify        #
> > > > > > > > > > > +# it under the terms of the GNU General Public
> > > > > > > > > > > License as
> > > > > > > > > > > published by        #
> > > > > > > > > > > +# the Free Software Foundation, either version 3
> > > > > > > > > > > of the
> > > > > > > > > > > License, or           #
> > > > > > > > > > > +# (at your option) any later
> > > > > > > > > > > version.
> > > > > > > > > > >                                         #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >                #
> > > > > > > > > > > +# This program is distributed in the hope that
> > > > > > > > > > > it will be
> > > > > > > > > > > useful,             #
> > > > > > > > > > > +# but WITHOUT ANY WARRANTY; without even the
> > > > > > > > > > > implied warranty
> > > > > > > > > > > of              #
> > > > > > > > > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR
> > > > > > > > > > > PURPOSE.  See
> > > > > > > > > > > the               #
> > > > > > > > > > > +# GNU General Public License for more
> > > > > > > > > > > details.                                #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >                #
> > > > > > > > > > > +# You should have received a copy of the GNU
> > > > > > > > > > > General Public
> > > > > > > > > > > License           #
> > > > > > > > > > > +# along with this program.  If not, see <
> > > > > > > > > > > http://www.gnu.org/licenses/>.       #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >                #
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > #################
> > > > > > > > > > > +
> > > > > > > > > > > +use strict;
> > > > > > > > > > > +
> > > > > > > > > > > +# enable only the following on debugging purpose
> > > > > > > > > > > +use warnings;
> > > > > > > > > > > +use CGI::Carp 'fatalsToBrowser';
> > > > > > > > > > > +
> > > > > > > > > > > +require '/var/ipfire/general-functions.pl';
> > > > > > > > > > > +require "${General::swroot}/lang.pl";
> > > > > > > > > > > +require "${General::swroot}/header.pl";
> > > > > > > > > > > +
> > > > > > > > > > > +# File declarations
> > > > > > > > > > > +my $settingsfile =
> > > > > > > > > > > "${General::swroot}/portredir/settings";
> > > > > > > > > > > +my $redirectsfile =
> > > > > > > > > > > "${General::swroot}/portredir/redirects";
> > > > > > > > > > > +
> > > > > > > > > > > +# Create empty settingsfiles if they does not
> > > > > > > > > > > exist yet
> > > > > > > > > > > +unless (-e "$settingsfile") { system ("touch
> > > > > > > > > > > $settingsfile");
> > > > > > > > > > > }
> > > > > > > > > > > +unless (-e "$redirectsfile") { system ("touch
> > > > > > > > > > > $redirectsfile"); }
> > > > > > > > > > > +
> > > > > > > > > > > +# load ipfire settings
> > > > > > > > > > > +our %netsettings = ();
> > > > > > > > > > > +our %color = ();
> > > > > > > > > > > +&General::readhash("${General::swroot}/ethernet/
> > > > > > > > > > > settings",
> > > > > > > > > > > \%netsettings);
> > > > > > > > > > > +&General::readhash("/srv/web/ipfire/html/themes/
> > > > > > > > > > > ipfire/include
> > > > > > > > > > > /colors.txt", \%color);
> > > > > > > > > > > +
> > > > > > > > > > > +my %settings=();
> > > > > > > > > > > +my %portredirs=();
> > > > > > > > > > > +my %checked=(); # Checkbox manipulations
> > > > > > > > > > > +my $errormessage='';
> > > > > > > > > > > +my %selected=();
> > > > > > > > > > > +our %redirects=();
> > > > > > > > > > > +
> > > > > > > > > > > +$settings{'ACTION'} = '';
> > > > > > > > > > > +$settings{'REDIR_ENABLE_ADDON'}="off";
> > > > > > > > > > > +$settings{'REDIR_CUSTOM_GREEN'}="off";
> > > > > > > > > > > +$settings{'REDIR_CUSTOM_BLUE'}="off";
> > > > > > > > > > > +$settings{'REDIR_CUSTOM_ORANGE'}="off";
> > > > > > > > > > > +$settings{'REDIR_DNS_GREEN'}="off";
> > > > > > > > > > > +$settings{'REDIR_NTP_GREEN'}="off";
> > > > > > > > > > > +$settings{'REDIR_DNS_BLUE'}="off";
> > > > > > > > > > > +$settings{'REDIR_NTP_BLUE'}="off";
> > > > > > > > > > > +$settings{'REDIR_DNS_ORANGE'}="off";
> > > > > > > > > > > +$settings{'REDIR_NTP_ORANGE'}="off";
> > > > > > > > > > > +
> > > > > > > > > > > +&Header::showhttpheaders();
> > > > > > > > > > > +
> > > > > > > > > > > +# Get GUI values
> > > > > > > > > > > +&Header::getcgihash(\%settings);
> > > > > > > > > > > +
> > > > > > > > > > > +# Save action
> > > > > > > > > > > +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
> > > > > > > > > > > +
> > > > > > > > > > > +       # If custom rules enabled, deactivate
> > > > > > > > > > > default rules on
> > > > > > > > > > > interface
> > > > > > > > > > > +       if ($settings{'REDIR_CUSTOM_GREEN'} eq
> > > > > > > > > > > "on" ) {
> > > > > > > > > > > +
> > > > > > > > > > >               $settings{'REDIR_DNS_GREEN'}="off";
> > > > > > > > > > > 
> > > > > > > > > > > +
> > > > > > > > > > >               $settings{'REDIR_NTP_GREEN'}="off";
> > > > > > > > > > > 
> > > > > > > > > > > +       }
> > > > > > > > > > > +
> > > > > > > > > > > +       if ($settings{'REDIR_CUSTOM_BLUE'} eq
> > > > > > > > > > > "on" ) {
> > > > > > > > > > > +
> > > > > > > > > > >               $settings{'REDIR_DNS_BLUE'}="off";
> > > > > > > > > > > +
> > > > > > > > > > >               $settings{'REDIR_NTP_BLUE'}="off";
> > > > > > > > > > > +       }
> > > > > > > > > > > +
> > > > > > > > > > > +       if ($settings{'REDIR_CUSTOM_ORANGE'} eq
> > > > > > > > > > > "on" ) {
> > > > > > > > > > > +
> > > > > > > > > > >               $settings{'REDIR_DNS_ORANGE'}="off"
> > > > > > > > > > > ;
> > > > > > > > > > > +
> > > > > > > > > > >               $settings{'REDIR_NTP_ORANGE'}="off"
> > > > > > > > > > > ;
> > > > > > > > > > > +       }
> > > > > > > > > > > +
> > > > > > > > > > > +       &General::writehash($settingsfile,
> > > > > > > > > > > \%settings);
> > > > > > > > > > > +
> > > > > > > > > > > +       if ($settings{'REDIR_ENABLE_ADDON'} eq
> > > > > > > > > > > "on") {
> > > > > > > > > > > +               system
> > > > > > > > > > > ('/usr/local/bin/portredirctrl restart
> > > > > > > > > > > > /dev/null 2>&1');
> > > > > > > > > > > +               system
> > > > > > > > > > > ('/usr/local/bin/portredirctrl enable
> > > > > > > > > > > > /dev/null 2>&1');
> > > > > > > > > > > +               &General::log('portredir addon:
> > > > > > > > > > > port
> > > > > > > > > > > redirections enabled');
> > > > > > > > > > > +       }
> > > > > > > > > > > +       if ($settings{'REDIR_ENABLE_ADDON'} eq
> > > > > > > > > > > "off") {
> > > > > > > > > > > +               system
> > > > > > > > > > > ('/usr/local/bin/portredirctrl disable
> > > > > > > > > > > > /dev/null 2>&1');
> > > > > > > > > > > +               system
> > > > > > > > > > > ('/usr/local/bin/portredirctrl stop
> > > > > > > > > > > > /dev/null 2>&1');
> > > > > > > > > > > +               &General::log('portredir addon:
> > > > > > > > > > > port
> > > > > > > > > > > redirections disabled');
> > > > > > > > > > > +       }
> > > > > > > > > > > +
> > > > > > > > > > > +# Add/edit an entry to the redirectsfile.
> > > > > > > > > > > +
> > > > > > > > > > > +} elsif (($settings{'ACTION'} eq
> > > > > > > > > > > $Lang::tr{'add'}) ||
> > > > > > > > > > > ($settings{'ACTION'} eq $Lang::tr{'update'})) {
> > > > > > > > > > > +
> > > > > > > > > > > +       # Check if any input has been performed.
> > > > > > > > > > > +       if ($settings{'REDIR_ENTRY_ADDRESS'} ne
> > > > > > > > > > > '') {
> > > > > > > > > > > +
> > > > > > > > > > > +               # Check if the given input is no
> > > > > > > > > > > valid IP-
> > > > > > > > > > > address, display an error message.
> > > > > > > > > > > +               if
> > > > > > > > > > > (!&General::validip($settings{'REDIR_ENTRY_ADDRES
> > > > > > > > > > > S'}))  {
> > > > > > > > > > > +                       $errormessage =
> > > > > > > > > > > "$Lang::tr{'portredir
> > > > > > > > > > > invalid address'}";
> > > > > > > > > > > +               }
> > > > > > > > > > > +       } else {
> > > > > > > > > > > +               $errormessage =
> > > > > > > > > > > "$Lang::tr{'portredir empty
> > > > > > > > > > > input'}";
> > > > > > > > > > > +       }
> > > > > > > > > > > +
> > > > > > > > > > > +       # Go further if there was no error.
> > > > > > > > > > > +       if ($errormessage eq '') {
> > > > > > > > > > > +               my %redirects = ();
> > > > > > > > > > > +               my $id;
> > > > > > > > > > > +               my $status;
> > > > > > > > > > > +
> > > > > > > > > > > +               # Assign hash values.
> > > > > > > > > > > +               my $new_entry_interface =
> > > > > > > > > > > $settings{'REDIR_ENTRY_INTERFACE'};
> > > > > > > > > > > +               my $new_entry_protocol =
> > > > > > > > > > > $settings{'REDIR_ENTRY_PROTOCOL'};
> > > > > > > > > > > +               my $new_entry_port =
> > > > > > > > > > > $settings{'REDIR_ENTRY_PORT'};
> > > > > > > > > > > +               my $new_entry_address =
> > > > > > > > > > > $settings{'REDIR_ENTRY_ADDRESS'};
> > > > > > > > > > > +               my $new_entry_remark =
> > > > > > > > > > > $settings{'REDIR_ENTRY_REMARK'};
> > > > > > > > > > > +
> > > > > > > > > > > +               # Read-in redirectsfile.
> > > > > > > > > > > +
> > > > > > > > > > >               &General::readhasharray($redirectsf
> > > > > > > > > > > ile,
> > > > > > > > > > > \%redirects);
> > > > > > > > > > > +
> > > > > > > > > > > +               # Check if we should edit an
> > > > > > > > > > > existing entry and
> > > > > > > > > > > got an ID.
> > > > > > > > > > > +               if (($settings{'ACTION'} eq
> > > > > > > > > > > $Lang::tr{'update'}) && ($settings{'ID'})) {
> > > > > > > > > > > +                       # Assin the provided id.
> > > > > > > > > > > +                       $id = $settings{'ID'};
> > > > > > > > > > > +
> > > > > > > > > > > +                       # Undef the given ID.
> > > > > > > > > > > +                       undef($settings{'ID'});
> > > > > > > > > > > +
> > > > > > > > > > > +                       # Grab the configured
> > > > > > > > > > > status of the
> > > > > > > > > > > corresponding entry.
> > > > > > > > > > > +                       $status =
> > > > > > > > > > > $redirects{$id}[4];
> > > > > > > > > > > +               } else {
> > > > > > > > > > > +                       # Each newly added entry
> > > > > > > > > > > automatically
> > > > > > > > > > > should be enabled.
> > > > > > > > > > > +                       $status = "enabled";
> > > > > > > > > > > +
> > > > > > > > > > > +                       # Generate the ID for the
> > > > > > > > > > > new entry.
> > > > > > > > > > > +                       #
> > > > > > > > > > > +                       # Sort the keys by their
> > > > > > > > > > > ID and store
> > > > > > > > > > > them in an array.
> > > > > > > > > > > +                       my @keys = sort { $a <=>
> > > > > > > > > > > $b } keys
> > > > > > > > > > > %redirects;
> > > > > > > > > > > +
> > > > > > > > > > > +                       # Reverse the key array.
> > > > > > > > > > > +                       my @reversed =
> > > > > > > > > > > reverse(@keys);
> > > > > > > > > > > +
> > > > > > > > > > > +                       # Obtain the last used
> > > > > > > > > > > id.
> > > > > > > > > > > +                       my $last_id =
> > > > > > > > > > > @reversed[0];
> > > > > > > > > > > +
> > > > > > > > > > > +                       # Increase the last id by
> > > > > > > > > > > one and use
> > > > > > > > > > > it as id for the new entry.
> > > > > > > > > > > +                       $id = ++$last_id;
> > > > > > > > > > > +               }
> > > > > > > > > > > +
> > > > > > > > > > > +               # Add/Modify the entry to/in the
> > > > > > > > > > > redirects
> > > > > > > > > > > hash.
> > > > > > > > > > > +               $redirects{$id} =
> > > > > > > > > > > ["$new_entry_interface",
> > > > > > > > > > > "$new_entry_protocol", "$new_entry_port",
> > > > > > > > > > > "$new_entry_address","$status",
> > > > > > > > > > > "$new_entry_remark"];
> > > > > > > > > > > +
> > > > > > > > > > > +               # Write the changed redirects
> > > > > > > > > > > hash to the
> > > > > > > > > > > redirects file.
> > > > > > > > > > > +
> > > > > > > > > > >               &General::writehasharray($redirects
> > > > > > > > > > > file,
> > > > > > > > > > > \%redirects);
> > > > > > > > > > > +       }
> > > > > > > > > > > +
> > > > > > > > > > > +# Toggle Enabled/Disabled for an existing entry
> > > > > > > > > > > on the
> > > > > > > > > > > redirects list.
> > > > > > > > > > > +
> > > > > > > > > > > +} elsif ($settings{'ACTION'} eq
> > > > > > > > > > > $Lang::tr{'toggle enable
> > > > > > > > > > > disable'}) {
> > > > > > > > > > > +       my %redirects = ();
> > > > > > > > > > > +
> > > > > > > > > > > +       # Only go further, if an ID has been
> > > > > > > > > > > passed.
> > > > > > > > > > > +       if ($settings{'ID'}) {
> > > > > > > > > > > +               # Assign the given ID.
> > > > > > > > > > > +               my $id = $settings{'ID'};
> > > > > > > > > > > +
> > > > > > > > > > > +               # Undef the given ID.
> > > > > > > > > > > +               undef($settings{'ID'});
> > > > > > > > > > > +
> > > > > > > > > > > +               # Read-in ignoredfile.
> > > > > > > > > > > +
> > > > > > > > > > >               &General::readhasharray($redirectsf
> > > > > > > > > > > ile,
> > > > > > > > > > > \%redirects);
> > > > > > > > > > > +
> > > > > > > > > > > +               # Grab the configured status of
> > > > > > > > > > > the
> > > > > > > > > > > corresponding entry.
> > > > > > > > > > > +               my $status = $redirects{$id}[4];
> > > > > > > > > > > +
> > > > > > > > > > > +               # Switch the status.
> > > > > > > > > > > +               if ($status eq "disabled") {
> > > > > > > > > > > +                       $status = "enabled";
> > > > > > > > > > > +               } else {
> > > > > > > > > > > +                       $status = "disabled";
> > > > > > > > > > > +               }
> > > > > > > > > > > +
> > > > > > > > > > > +               # Modify the status of the
> > > > > > > > > > > existing entry.
> > > > > > > > > > > +               $redirects{$id} =
> > > > > > > > > > > ["$redirects{$id}[0]",
> > > > > > > > > > > "$redirects{$id}[1]", "$redirects{$id}[2]",
> > > > > > > > > > > "$redirects{$id}[3]","$status",
> > > > > > > > > > > "$redirects{$id}[5]"];
> > > > > > > > > > > +
> > > > > > > > > > > +               # Write the changed ignored hash
> > > > > > > > > > > to the
> > > > > > > > > > > redirects file.
> > > > > > > > > > > +
> > > > > > > > > > >               &General::writehasharray($redirects
> > > > > > > > > > > file,
> > > > > > > > > > > \%redirects);
> > > > > > > > > > > +       }
> > > > > > > > > > > +
> > > > > > > > > > > +# Remove entry from redirects list.
> > > > > > > > > > > +
> > > > > > > > > > > +} elsif ($settings{'ACTION'} eq
> > > > > > > > > > > $Lang::tr{'remove'}) {
> > > > > > > > > > > +       my %redirects = ();
> > > > > > > > > > > +
> > > > > > > > > > > +       # Read-in redirectsfile.
> > > > > > > > > > > +       &General::readhasharray($redirectsfile,
> > > > > > > > > > > \%redirects);
> > > > > > > > > > > +
> > > > > > > > > > > +       # move data on key up
> > > > > > > > > > > +       foreach my $key (sort keys %redirects) {
> > > > > > > > > > > +               if ($key >= $settings{'ID'}) {
> > > > > > > > > > > +                       my $next = $key + 1;
> > > > > > > > > > > +                       if (exists
> > > > > > > > > > > $redirects{$next}) {
> > > > > > > > > > > +                               foreach my $i (0
> > > > > > > > > > > ..
> > > > > > > > > > > $#{$redirects{$next}}) { $redirects{$key}[$i] =
> > > > > > > > > > > $redirects{$next}[$i]; }
> > > > > > > > > > > +                       }
> > > > > > > > > > > +               }
> > > > > > > > > > > +       }
> > > > > > > > > > > +
> > > > > > > > > > > +       my $last_key = (sort {$a <=> $b} keys
> > > > > > > > > > > %redirects)[-1];
> > > > > > > > > > > +       delete $redirects{$last_key};
> > > > > > > > > > > +
> > > > > > > > > > > +       # Undef the given ID.
> > > > > > > > > > > +       undef($settings{'ID'});
> > > > > > > > > > > +
> > > > > > > > > > > +       # Write the changed redirects hash to
> > > > > > > > > > > file.
> > > > > > > > > > > +       &General::writehasharray($redirectsfile,
> > > > > > > > > > > \%redirects);
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +# Load settings from file
> > > > > > > > > > > +&General::readhash($settingsfile, \%settings);
> > > > > > > > > > > +&General::readhasharray($redirectsfile,
> > > > > > > > > > > \%redirects);
> > > > > > > > > > > +
> > > > > > > > > > > +# Call functions to generate whole page.
> > > > > > > > > > > +&Header::openpage($Lang::tr{'portredir port
> > > > > > > > > > > redirections'}, 1,
> > > > > > > > > > > '');
> > > > > > > > > > > +&Header::openbigbox('100%', 'left', '',
> > > > > > > > > > > $errormessage);
> > > > > > > > > > > +
> > > > > > > > > > > +if ($errormessage) {
> > > > > > > > > > > +        &Header::openbox('100%', 'left',
> > > > > > > > > > > $Lang::tr{'warning
> > > > > > > > > > > messages'});
> > > > > > > > > > > +        print "<font
> > > > > > > > > > > color='red'>$errormessage&nbsp;</font>";
> > > > > > > > > > > +        &Header::closebox();
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
> > > > > > > > > > > +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
> > > > > > > > > > > +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_
> > > > > > > > > > > ENABLE_ADDON'}
> > > > > > > > > > > } = "checked='checked'";
> > > > > > > > > > > +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
> > > > > > > > > > > +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
> > > > > > > > > > > +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_
> > > > > > > > > > > CUSTOM_GREEN'}
> > > > > > > > > > > } = "checked='checked'";
> > > > > > > > > > > +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
> > > > > > > > > > > +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
> > > > > > > > > > > +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_C
> > > > > > > > > > > USTOM_BLUE'}}
> > > > > > > > > > > = "checked='checked'";
> > > > > > > > > > > +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
> > > > > > > > > > > +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
> > > > > > > > > > > +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR
> > > > > > > > > > > _CUSTOM_ORANGE
> > > > > > > > > > > '}} = "checked='checked'";
> > > > > > > > > > > +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
> > > > > > > > > > > +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
> > > > > > > > > > > +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS
> > > > > > > > > > > _GREEN'}} =
> > > > > > > > > > > "checked='checked'";
> > > > > > > > > > > +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
> > > > > > > > > > > +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
> > > > > > > > > > > +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP
> > > > > > > > > > > _GREEN'}} =
> > > > > > > > > > > "checked='checked'";
> > > > > > > > > > > +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
> > > > > > > > > > > +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
> > > > > > > > > > > +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_
> > > > > > > > > > > BLUE'}} =
> > > > > > > > > > > "checked='checked'";
> > > > > > > > > > > +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
> > > > > > > > > > > +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
> > > > > > > > > > > +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_
> > > > > > > > > > > BLUE'}} =
> > > > > > > > > > > "checked='checked'";
> > > > > > > > > > > +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
> > > > > > > > > > > +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
> > > > > > > > > > > +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DN
> > > > > > > > > > > S_ORANGE'}} =
> > > > > > > > > > > "checked='checked'";
> > > > > > > > > > > +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
> > > > > > > > > > > +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
> > > > > > > > > > > +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NT
> > > > > > > > > > > P_ORANGE'}} =
> > > > > > > > > > > "checked='checked'";
> > > > > > > > > > > +
> > > > > > > > > > > +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'RE
> > > > > > > > > > > DIR_ENTRY_INTE
> > > > > > > > > > > RFACE'}} = 'selected';
> > > > > > > > > > > +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'RED
> > > > > > > > > > > IR_ENTRY_PROTO
> > > > > > > > > > > COL'}} = 'selected';
> > > > > > > > > > > +
> > > > > > > > > > > +&showMainBox();
> > > > > > > > > > > +&showRedirectsBox();
> > > > > > > > > > > +
> > > > > > > > > > > +&Header::closebigbox();
> > > > > > > > > > > +&Header::closepage();
> > > > > > > > > > > +
> > > > > > > > > > > +# Function to show main settings and options.
> > > > > > > > > > > +sub showMainBox() {
> > > > > > > > > > > +
> > > > > > > > > > > +       &Header::openbox('100%', 'center',
> > > > > > > > > > > "$Lang::tr{'settings'}");
> > > > > > > > > > > +       print "<form method='post'
> > > > > > > > > > > action='$ENV{'SCRIPT_NAME'}'>";
> > > > > > > > > > > +
> > > > > > > > > > > +print <<END;
> > > > > > > > > > > +<table width='80%' cellspacing='0' border='0'>
> > > > > > > > > > > +       <tr><td colspan='2' class='base'
> > > > > > > > > > > bgcolor='$color{'color20'}'><b>$Lang::tr{'portred
> > > > > > > > > > > ir common
> > > > > > > > > > > settings'}</b></td></tr
> > > > > > > > > > > +       <tr>
> > > > > > > > > > > +               <td width='25%'
> > > > > > > > > > > class='base'>$Lang::tr{'portredir enable
> > > > > > > > > > > addon'}:</td>
> > > > > > > > > > > +               <td><input type='checkbox'
> > > > > > > > > > > name='REDIR_ENABLE_ADDON'
> > > > > > > > > > > $checked{'REDIR_ENABLE_ADDON'}{'on'}
> > > > > > > > > > > /></td>
> > > > > > > > > > > +       </tr>
> > > > > > > > > > > +       <tr><td colspan='2'></td></tr>
> > > > > > > > > > > +       <tr><td colspan='2'>&nbsp;</td></tr>
> > > > > > > > > > > +END
> > > > > > > > > > > +
> > > > > > > > > > > +       # create html table with header line 1
> > > > > > > > > > > +       print "<table width='80%' cellspacing='0'
> > > > > > > > > > > border='0'><tr>";
> > > > > > > > > > > +       print "<th class='base' width='40%'
> > > > > > > > > > > align='left'><b>$Lang::tr{'portredir fw for
> > > > > > > > > > > interface'}</th>";
> > > > > > > > > > > +       if ($netsettings{'GREEN_DEV'})  {print
> > > > > > > > > > > "<th
> > > > > > > > > > > class='base' width='10%'><b><font
> > > > > > > > > > > color=green>$Lang::tr{'green'}</font></th>";
> > > > > > > > > > > +               } else {                 print
> > > > > > > > > > > "<th
> > > > > > > > > > > class='base' width='10%'></font></th>"; }
> > > > > > > > > > > +       if ($netsettings{'BLUE_DEV'})   {print
> > > > > > > > > > > "<th
> > > > > > > > > > > class='base' width='10%'><b><font
> > > > > > > > > > > color=blue>$Lang::tr{'blue'}</font></th>";
> > > > > > > > > > > +               } else {                 print
> > > > > > > > > > > "<th
> > > > > > > > > > > class='base' width='10%'></font></th>"; }
> > > > > > > > > > > +       if ($netsettings{'ORANGE_DEV'}) {print
> > > > > > > > > > > "<th
> > > > > > > > > > > class='base' width='10%'><b><font
> > > > > > > > > > > color=orange>$Lang::tr{'orange'}</font></th>";
> > > > > > > > > > > +               } else {                 print
> > > > > > > > > > > "<th
> > > > > > > > > > > class='base' width='10%'></font></th>"; }
> > > > > > > > > > > +
> > > > > > > > > > > +       # the empty right row
> > > > > > > > > > > +       print "<th class='base'
> > > > > > > > > > > width='30%'><td></td></th></tr>";
> > > > > > > > > > > +
> > > > > > > > > > > +       # line 2
> > > > > > > > > > > +       print "<tr><td>$Lang::tr{'portredir force
> > > > > > > > > > > local
> > > > > > > > > > > dns'}</td>";
> > > > > > > > > > > +       if ($netsettings{'GREEN_DEV'})  {print
> > > > > > > > > > > "<td
> > > > > > > > > > > class='base' align='center'><input
> > > > > > > > > > > type='checkbox'
> > > > > > > > > > > name='REDIR_DNS_GREEN'
> > > > > > > > > > > $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else {
> > > > > > > > > > > print
> > > > > > > > > > > "<td></td>";}
> > > > > > > > > > > +       if ($netsettings{'BLUE_DEV'})   {print
> > > > > > > > > > > "<td
> > > > > > > > > > > class='base' align='center'><input
> > > > > > > > > > > type='checkbox'
> > > > > > > > > > > name='REDIR_DNS_BLUE'
> > > > > > > > > > > $checked{'REDIR_DNS_BLUE'}{'on'}></td>";}
> > > > > > > > > > > else { print "<td></td>";}
> > > > > > > > > > > +       if ($netsettings{'ORANGE_DEV'}) {print
> > > > > > > > > > > "<td
> > > > > > > > > > > class='base' align='center'><input
> > > > > > > > > > > type='checkbox'
> > > > > > > > > > > name='REDIR_DNS_ORANGE'
> > > > > > > > > > > $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else
> > > > > > > > > > > { print
> > > > > > > > > > > "<td></td>";}
> > > > > > > > > > > +
> > > > > > > > > > > +       # line 3
> > > > > > > > > > > +       print "</tr><tr><td>$Lang::tr{'portredir
> > > > > > > > > > > force local
> > > > > > > > > > > ntp'}</td>";
> > > > > > > > > > > +       if ($netsettings{'GREEN_DEV'})  {print
> > > > > > > > > > > "<td
> > > > > > > > > > > class='base' align='center'><input
> > > > > > > > > > > type='checkbox'
> > > > > > > > > > > name='REDIR_NTP_GREEN'
> > > > > > > > > > > $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else {
> > > > > > > > > > > print
> > > > > > > > > > > "<td></td>";}
> > > > > > > > > > > +       if ($netsettings{'BLUE_DEV'})   {print
> > > > > > > > > > > "<td
> > > > > > > > > > > class='base' align='center'><input
> > > > > > > > > > > type='checkbox'
> > > > > > > > > > > name='REDIR_NTP_BLUE'
> > > > > > > > > > > $checked{'REDIR_NTP_BLUE'}{'on'}></td>";}
> > > > > > > > > > > else { print "<td></td>";}
> > > > > > > > > > > +       if ($netsettings{'ORANGE_DEV'}) {print
> > > > > > > > > > > "<td
> > > > > > > > > > > class='base' align='center'><input
> > > > > > > > > > > type='checkbox'
> > > > > > > > > > > name='REDIR_NTP_ORANGE'
> > > > > > > > > > > $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else
> > > > > > > > > > > { print
> > > > > > > > > > > "<td></td>";}
> > > > > > > > > > > +
> > > > > > > > > > > +       # line 4
> > > > > > > > > > > +       print "</tr><tr><td>$Lang::tr{'portredir
> > > > > > > > > > > enable user
> > > > > > > > > > > redirections'}</td>";
> > > > > > > > > > > +       if ($netsettings{'GREEN_DEV'})  {print
> > > > > > > > > > > "<td
> > > > > > > > > > > class='base' align='center'><input
> > > > > > > > > > > type='checkbox'
> > > > > > > > > > > name='REDIR_CUSTOM_GREEN'
> > > > > > > > > > > $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";}
> > > > > > > > > > > else { print
> > > > > > > > > > > "<td></td>";}
> > > > > > > > > > > +       if ($netsettings{'BLUE_DEV'})   {print
> > > > > > > > > > > "<td
> > > > > > > > > > > class='base' align='center'><input
> > > > > > > > > > > type='checkbox'
> > > > > > > > > > > name='REDIR_CUSTOM_BLUE'
> > > > > > > > > > > $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else
> > > > > > > > > > > { print
> > > > > > > > > > > "<td></td>";}
> > > > > > > > > > > +       if ($netsettings{'ORANGE_DEV'}) {print
> > > > > > > > > > > "<td
> > > > > > > > > > > class='base' align='center'><input
> > > > > > > > > > > type='checkbox'
> > > > > > > > > > > name='REDIR_CUSTOM_ORANGE'
> > > > > > > > > > > $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";}
> > > > > > > > > > > else { print
> > > > > > > > > > > "<td></td>";}
> > > > > > > > > > > +
> > > > > > > > > > > +       print <<END;
> > > > > > > > > > > +       </tr></table>
> > > > > > > > > > > +       <table width='80%' cellspacing='0'
> > > > > > > > > > > border='0'>
> > > > > > > > > > > +               <tr><td
> > > > > > > > > > > colspan='2'>&nbsp;</td></tr>
> > > > > > > > > > > +               <tr><td
> > > > > > > > > > > align='left'><b>$Lang::tr{'portredir
> > > > > > > > > > > save to activate'}</b></td><td width='5%'
> > > > > > > > > > > align='center'><input
> > > > > > > > > > > type='submit' name='ACTION' value='
> > > > > > > > > > >  $Lang::tr{'save'} 
> > > > > > > > > > > '></td></tr>
> > > > > > > > > > > +       </table></form>
> > > > > > > > > > > +END
> > > > > > > > > > > +
> > > > > > > > > > > +&Header::closebox();
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +# Function to show elements of the redirects
> > > > > > > > > > > file and allow to
> > > > > > > > > > > add or remove single members of it.
> > > > > > > > > > > +sub showRedirectsBox() {
> > > > > > > > > > > +        &Header::openbox('100%', 'center',
> > > > > > > > > > > "$Lang::tr{'portredir custom redirections'}");
> > > > > > > > > > > +
> > > > > > > > > > > +       print <<END;
> > > > > > > > > > > +               <table width='80%'
> > > > > > > > > > > cellspacing='1' border='0'>
> > > > > > > > > > > +                       <tr>
> > > > > > > > > > > +                               <td class='base'
> > > > > > > > > > > bgcolor='$color{'color20'}'
> > > > > > > > > > > align='center'><b>$Lang::tr{'interface'}</b></td>
> > > > > > > > > > > 
> > > > > > > > > > > +                               <td class='base'
> > > > > > > > > > > bgcolor='$color{'color20'}'
> > > > > > > > > > > align='center'><b>$Lang::tr{'protocol'}</b></td>
> > > > > > > > > > > +                               <td class='base'
> > > > > > > > > > > bgcolor='$color{'color20'}'
> > > > > > > > > > > align='center'><b>$Lang::tr{'port'}</b></td>
> > > > > > > > > > > +                               <td class='base'
> > > > > > > > > > > bgcolor='$color{'color20'}'
> > > > > > > > > > > align='center'><b>$Lang::tr{'ip
> > > > > > > > > > > address'}</b></td>
> > > > > > > > > > > +                               <td class='base'
> > > > > > > > > > > bgcolor='$color{'color20'}'
> > > > > > > > > > > align='center'><b>$Lang::tr{'remark'}</b></td>
> > > > > > > > > > > +                               <td class='base'
> > > > > > > > > > > colspan='3'
> > > > > > > > > > > bgcolor='$color{'color20'}'></td>
> > > > > > > > > > > +                       </tr>
> > > > > > > > > > > +END
> > > > > > > > > > > +                       # Check if some rules
> > > > > > > > > > > have been added
> > > > > > > > > > > to be redirects.
> > > > > > > > > > > +                       if (keys (%redirects)) {
> > > > > > > > > > > +                               my $col = "";
> > > > > > > > > > > +
> > > > > > > > > > > +                               # List all
> > > > > > > > > > > entries of the hash.
> > > > > > > > > > > +                               foreach my $key
> > > > > > > > > > > (sort keys
> > > > > > > > > > > %redirects){
> > > > > > > > > > > +
> > > > > > > > > > > +                                       # Assign
> > > > > > > > > > > data array
> > > > > > > > > > > positions to some nice variable names.
> > > > > > > > > > > +                                       my
> > > > > > > > > > > $interface =
> > > > > > > > > > > $redirects{$key}[0];
> > > > > > > > > > > +                                       my
> > > > > > > > > > > $protocol =
> > > > > > > > > > > $redirects{$key}[1];
> > > > > > > > > > > +                                       my $port
> > > > > > > > > > >  =
> > > > > > > > > > > $redirects{$key}[2];
> > > > > > > > > > > +                                       my
> > > > > > > > > > > $address =
> > > > > > > > > > > $redirects{$key}[3];
> > > > > > > > > > > +                                       my
> > > > > > > > > > > $status  =
> > > > > > > > > > > $redirects{$key}[4];
> > > > > > > > > > > +                                       my
> > > > > > > > > > > $remark  =
> > > > > > > > > > > $redirects{$key}[5];
> > > > > > > > > > > +
> > > > > > > > > > > +                                       # Check
> > > > > > > > > > > if the key (id)
> > > > > > > > > > > number is even or not.
> > > > > > > > > > > +                                       if
> > > > > > > > > > > ($settings{'ID'} eq
> > > > > > > > > > > $key) {
> > > > > > > > > > > +
> > > > > > > > > > >                                               $co
> > > > > > > > > > > l="bgcolor='
> > > > > > > > > > > ${Header::colouryellow}'";
> > > > > > > > > > > +                                       } elsif
> > > > > > > > > > > ($key % 2) {
> > > > > > > > > > > +
> > > > > > > > > > >                                               $co
> > > > > > > > > > > l="bgcolor='
> > > > > > > > > > > $color{'color22'}'";
> > > > > > > > > > > +                                       } else {
> > > > > > > > > > > +
> > > > > > > > > > >                                               $co
> > > > > > > > > > > l="bgcolor='
> > > > > > > > > > > $color{'color20'}'";
> > > > > > > > > > > +                                       }
> > > > > > > > > > > +
> > > > > > > > > > > +                                       # Choose
> > > > > > > > > > > icon for the
> > > > > > > > > > > checkbox.
> > > > > > > > > > > +                                       my $gif;
> > > > > > > > > > > +                                       my
> > > > > > > > > > > $gdesc;
> > > > > > > > > > > +
> > > > > > > > > > > +                                       # Check
> > > > > > > > > > > if the status
> > > > > > > > > > > is enabled and select the correct image and
> > > > > > > > > > > description.
> > > > > > > > > > > +                                       if
> > > > > > > > > > > ($status eq
> > > > > > > > > > > 'enabled' ) {
> > > > > > > > > > > +
> > > > > > > > > > >                                               $gi
> > > > > > > > > > > f =
> > > > > > > > > > > 'on.gif';
> > > > > > > > > > > +
> > > > > > > > > > >                                               $gd
> > > > > > > > > > > esc =
> > > > > > > > > > > $Lang::tr{'click to disable'};
> > > > > > > > > > > +                                       } else {
> > > > > > > > > > > +
> > > > > > > > > > >                                               $gi
> > > > > > > > > > > f =
> > > > > > > > > > > 'off.gif';
> > > > > > > > > > > +
> > > > > > > > > > >                                               $gd
> > > > > > > > > > > esc =
> > > > > > > > > > > $Lang::tr{'click to enable'};
> > > > > > > > > > > +                                       }
> > > > > > > > > > > +
> > > > > > > > > > > +                                       print
> > > > > > > > > > > <<END;
> > > > > > > > > > > +                                       <tr>
> > > > > > > > > > > +
> > > > > > > > > > >                                               <td
> > > > > > > > > > > width='15%'
> > > > > > > > > > > class='base' align='center' $col><b><font
> > > > > > > > > > > color=$interface>$Lang::tr{$interface}</font></b>
> > > > > > > > > > > </td>
> > > > > > > > > > > +
> > > > > > > > > > >                                               <td
> > > > > > > > > > > width='10%'
> > > > > > > > > > > class='base' align='center' $col>$protocol</td>
> > > > > > > > > > > +
> > > > > > > > > > >                                               <td
> > > > > > > > > > > width='10%'
> > > > > > > > > > > class='base' align='center' $col>$port</td>
> > > > > > > > > > > +
> > > > > > > > > > >                                               <td
> > > > > > > > > > > width='15%'
> > > > > > > > > > > class='base' align='center'
> > > > > > > > > > > $col>&nbsp;$address</td>
> > > > > > > > > > > +
> > > > > > > > > > >                                               <td
> > > > > > > > > > > width='40%'
> > > > > > > > > > > class='base' align='center'
> > > > > > > > > > > $col>&nbsp;$remark</td>
> > > > > > > > > > > +
> > > > > > > > > > >                                               <td
> > > > > > > > > > > 
> > > > > > > > > > > align='center' $col>
> > > > > > > > > > > +
> > > > > > > > > > >                                                  
> > > > > > > > > > >      <form
> > > > > > > > > > > method='post' action='$ENV{'SCRIPT_NAME'}'>
> > > > > > > > > > > +
> > > > > > > > > > >                                                  
> > > > > > > > > > >             
> > > > > > > > > > > <input type='hidden' name='ACTION'
> > > > > > > > > > > value='$Lang::tr{'toggle
> > > > > > > > > > > enable disable'}' />
> > > > > > > > > > > +
> > > > > > > > > > >                                                  
> > > > > > > > > > >             
> > > > > > > > > > > <input type='image' name='$Lang::tr{'toggle
> > > > > > > > > > > enable disable'}'
> > > > > > > > > > > src='/images/$gif' alt='$gdesc' title='$gdesc' />
> > > > > > > > > > > +
> > > > > > > > > > >                                                  
> > > > > > > > > > >             
> > > > > > > > > > > <input type='hidden' name='ID' value='$key' />
> > > > > > > > > > > +
> > > > > > > > > > >                                                  
> > > > > > > > > > >      </form>
> > > > > > > > > > > +
> > > > > > > > > > >                                               </t
> > > > > > > > > > > d>
> > > > > > > > > > > +
> > > > > > > > > > >                                               <td
> > > > > > > > > > > 
> > > > > > > > > > > align='center' $col>
> > > > > > > > > > > +
> > > > > > > > > > >                                                  
> > > > > > > > > > >      <form
> > > > > > > > > > > method='post' action='$ENV{'SCRIPT_NAME'}'>
> > > > > > > > > > > +
> > > > > > > > > > >                                                  
> > > > > > > > > > >             
> > > > > > > > > > > <input type='hidden' name='ACTION'
> > > > > > > > > > > value='$Lang::tr{'edit'}'
> > > > > > > > > > > />
> > > > > > > > > > > +
> > > > > > > > > > >                                                  
> > > > > > > > > > >             
> > > > > > > > > > > <input type='image' name='$Lang::tr{'edit'}'
> > > > > > > > > > > src='/images/edit.gif' alt='$Lang::tr{'edit'}'
> > > > > > > > > > > title='$Lang::tr{'edit'}' />
> > > > > > > > > > > +
> > > > > > > > > > >                                                  
> > > > > > > > > > >             
> > > > > > > > > > > <input type='hidden' name='ID' value='$key' />
> > > > > > > > > > > +
> > > > > > > > > > >                                                  
> > > > > > > > > > >      </form>
> > > > > > > > > > > +
> > > > > > > > > > >                                               </t
> > > > > > > > > > > d>
> > > > > > > > > > > +
> > > > > > > > > > >                                               <td
> > > > > > > > > > > 
> > > > > > > > > > > align='center' $col>
> > > > > > > > > > > +
> > > > > > > > > > >                                                  
> > > > > > > > > > >      <form
> > > > > > > > > > > method='post' name='$key'
> > > > > > > > > > > action='$ENV{'SCRIPT_NAME'}'>
> > > > > > > > > > > +
> > > > > > > > > > >                                                  
> > > > > > > > > > >             
> > > > > > > > > > > <input type='image' name='$Lang::tr{'remove'}'
> > > > > > > > > > > src='/images/delete.gif'
> > > > > > > > > > > title='$Lang::tr{'remove'}'
> > > > > > > > > > > alt='$Lang::tr{'remove'}'>
> > > > > > > > > > > +
> > > > > > > > > > >                                                  
> > > > > > > > > > >             
> > > > > > > > > > > <input type='hidden' name='ID' value='$key'>
> > > > > > > > > > > +
> > > > > > > > > > >                                                  
> > > > > > > > > > >             
> > > > > > > > > > > <input type='hidden' name='ACTION'
> > > > > > > > > > > value='$Lang::tr{'remove'}'>
> > > > > > > > > > > +
> > > > > > > > > > >                                                  
> > > > > > > > > > >      </form>
> > > > > > > > > > > +
> > > > > > > > > > >                                               </t
> > > > > > > > > > > d>
> > > > > > > > > > > +                                       </tr>
> > > > > > > > > > > +END
> > > > > > > > > > > +                               }
> > > > > > > > > > > +                       } else {
> > > > > > > > > > > +                               # Print notice
> > > > > > > > > > > that currently
> > > > > > > > > > > no ports are redirected.
> > > > > > > > > > > +                               print "<tr>\n";
> > > > > > > > > > > +                               print "<td
> > > > > > > > > > > class='base'
> > > > > > > > > > > colspan='2'>$Lang::tr{'portredir no
> > > > > > > > > > > entries'}</td>\n";
> > > > > > > > > > > +                               print "</tr>\n";
> > > > > > > > > > > +                       }
> > > > > > > > > > > +
> > > > > > > > > > > +               print "</table>\n";
> > > > > > > > > > > +
> > > > > > > > > > > +       # Section to add new elements or edit
> > > > > > > > > > > existing ones.
> > > > > > > > > > > +       print <<END;
> > > > > > > > > > > +       <br>
> > > > > > > > > > > +       <hr>
> > > > > > > > > > > +       <br>
> > > > > > > > > > > +       <div align='center'>
> > > > > > > > > > > +               <table width='100%'
> > > > > > > > > > > cellspacing='0' border='0'>
> > > > > > > > > > > +END
> > > > > > > > > > > +
> > > > > > > > > > > +       # Assign correct headline and button
> > > > > > > > > > > text.
> > > > > > > > > > > +       my $buttontext;
> > > > > > > > > > > +       my $entry_interface;
> > > > > > > > > > > +       my $entry_protocol;
> > > > > > > > > > > +       my $entry_port;
> > > > > > > > > > > +       my $entry_address;
> > > > > > > > > > > +       my $entry_remark;
> > > > > > > > > > > +
> > > > > > > > > > > +       # Check if an ID (key) has been given, in
> > > > > > > > > > > this case an
> > > > > > > > > > > existing entry should be edited.
> > > > > > > > > > > +       if ($settings{'ID'} ne '') {
> > > > > > > > > > > +               $buttontext =
> > > > > > > > > > > $Lang::tr{'update'};
> > > > > > > > > > > +               print "<tr><td class='boldbase'
> > > > > > > > > > > colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\
> > > > > > > > > > > n";
> > > > > > > > > > > +
> > > > > > > > > > > +               # Grab address and remark for the
> > > > > > > > > > > given key.
> > > > > > > > > > > +               $entry_interface =
> > > > > > > > > > > $redirects{$settings{'ID'}}[0];
> > > > > > > > > > > +               $entry_protocol =
> > > > > > > > > > > $redirects{$settings{'ID'}}[1];
> > > > > > > > > > > +               $entry_port =
> > > > > > > > > > > $redirects{$settings{'ID'}}[2];
> > > > > > > > > > > +               $entry_address =
> > > > > > > > > > > $redirects{$settings{'ID'}}[3];
> > > > > > > > > > > +               $entry_remark =
> > > > > > > > > > > $redirects{$settings{'ID'}}[5];
> > > > > > > > > > > +
> > > > > > > > > > > +       } else {
> > > > > > > > > > > +               $buttontext = $Lang::tr{'add'};
> > > > > > > > > > > +               print "<tr><td class='boldbase'
> > > > > > > > > > > colspan='11'><b>$Lang::tr{'dnsforward add a new
> > > > > > > > > > > entry'}</b></td></tr>\n";
> > > > > > > > > > > +               print
> > > > > > > > > > > "<tr><td>&nbsp</td></tr>\n";
> > > > > > > > > > > +       }
> > > > > > > > > > > +
> > > > > > > > > > > +       print <<END;
> > > > > > > > > > > +                       <tr>
> > > > > > > > > > > +                               <td class='base'
> > > > > > > > > > > width='1%' 
> > > > > > > > > > > bgcolor='$color{'color22'}'></td>
> > > > > > > > > > > +                               <td class='base'
> > > > > > > > > > > width='15%'
> > > > > > > > > > > bgcolor='$color{'color22'}'
> > > > > > > > > > > align='left'><b>$Lang::tr{'interface'}</b></td>
> > > > > > > > > > > +                               <td class='base'
> > > > > > > > > > > width='10%'
> > > > > > > > > > > bgcolor='$color{'color22'}'
> > > > > > > > > > > align='left'><b>$Lang::tr{'protocol'}</b></td>
> > > > > > > > > > > +                               <td class='base'
> > > > > > > > > > > width='10%'
> > > > > > > > > > > bgcolor='$color{'color22'}'
> > > > > > > > > > > align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
> > > > > > > > > > > +                               <td class='base'
> > > > > > > > > > > width='13%'
> > > > > > > > > > > bgcolor='$color{'color22'}'
> > > > > > > > > > > align='left'>&nbsp;<b>$Lang::tr{'ip
> > > > > > > > > > > address'}</b></td>
> > > > > > > > > > > +                               <td class='base'
> > > > > > > > > > > width='30%'
> > > > > > > > > > > bgcolor='$color{'color22'}'
> > > > > > > > > > > align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td
> > > > > > > > > > > > 
> > > > > > > > > > > +                               <td class='base'
> > > > > > > > > > > width='15%'
> > > > > > > > > > > bgcolor='$color{'color22'}'></td>
> > > > > > > > > > > +                               <td class='base'
> > > > > > > > > > > width='1%' 
> > > > > > > > > > > bgcolor='$color{'color22'}'></td>
> > > > > > > > > > > +                       </tr>
> > > > > > > > > > > +
> > > > > > > > > > > +                       <form method='post'
> > > > > > > > > > > action='$ENV{'SCRIPT_NAME'}'>
> > > > > > > > > > > +                       <input type='hidden'
> > > > > > > > > > > name='ID'
> > > > > > > > > > > value='$settings{'ID'}'>
> > > > > > > > > > > +                       <tr>
> > > > > > > > > > > +                               <td
> > > > > > > > > > > class='base'></td>
> > > > > > > > > > > +                               <td><select
> > > > > > > > > > > style='width:90px;'
> > > > > > > > > > > id='interface' name='REDIR_ENTRY_INTERFACE'>
> > > > > > > > > > > +END
> > > > > > > > > > > +                               if
> > > > > > > > > > > ($netsettings{'GREEN_DEV'})
> > > > > > > > > > > {
> > > > > > > > > > > +                                       if
> > > > > > > > > > > ($entry_interface eq
> > > > > > > > > > > "green") {
> > > > > > > > > > > +
> > > > > > > > > > >                                               pri
> > > > > > > > > > > nt "<option
> > > > > > > > > > > value='green' selected='selected'
> > > > > > > > > > > {'green'}>$Lang::tr{'green'}</option>";
> > > > > > > > > > > +                                       } else {
> > > > > > > > > > > print "<option
> > > > > > > > > > > value='green'
> > > > > > > > > > > {'green'}>$Lang::tr{'green'}</option>";}
> > > > > > > > > > > +                               }
> > > > > > > > > > > +                               if
> > > > > > > > > > > ($netsettings{'BLUE_DEV'}) {
> > > > > > > > > > > +                                       if
> > > > > > > > > > > ($entry_interface eq
> > > > > > > > > > > "blue") { 
> > > > > > > > > > > +
> > > > > > > > > > >                                               pri
> > > > > > > > > > > nt "<option
> > > > > > > > > > > value='blue' selected='selected'
> > > > > > > > > > > {'blue'}>$Lang::tr{'blue'}</option>";
> > > > > > > > > > > +                                       } else {
> > > > > > > > > > > print "<option
> > > > > > > > > > > value='blue'
> > > > > > > > > > > {'blue'}>$Lang::tr{'blue'}</option>";}
> > > > > > > > > > > +                               }
> > > > > > > > > > > +                               if
> > > > > > > > > > > ($netsettings{'ORANGE_DEV'})
> > > > > > > > > > > {
> > > > > > > > > > > +                                       if
> > > > > > > > > > > ($entry_interface eq
> > > > > > > > > > > "orange") { 
> > > > > > > > > > > +
> > > > > > > > > > >                                               pri
> > > > > > > > > > > nt "<option
> > > > > > > > > > > value='orange' selected='selected'
> > > > > > > > > > > {'orange'}>$Lang::tr{'orange'}</option>";
> > > > > > > > > > > +                                       } else {
> > > > > > > > > > > print "<option
> > > > > > > > > > > value='orange'
> > > > > > > > > > > {'orange'}>$Lang::tr{'orange'}</option>";}
> > > > > > > > > > > +                               }
> > > > > > > > > > > +
> > > > > > > > > > > +                       print
> > > > > > > > > > > "</select><td><select
> > > > > > > > > > > style='width:50px;'
> > > > > > > > > > > name='REDIR_ENTRY_PROTOCOL'>";
> > > > > > > > > > > +                       if ((!$entry_protocol) ||
> > > > > > > > > > > ($entry_protocol eq "tcp")) {
> > > > > > > > > > > +                               print "<option
> > > > > > > > > > > selected='selected' id='protocol' value='tcp'
> > > > > > > > > > > {'tcp'}>tcp</option>";
> > > > > > > > > > > +                               print "<option
> > > > > > > > > > > value='udp'
> > > > > > > > > > > {'udp'}>udp</option>";
> > > > > > > > > > > +                       } elsif ($entry_protocol
> > > > > > > > > > > eq "udp") {
> > > > > > > > > > > +                               print "<option
> > > > > > > > > > > value='tcp'
> > > > > > > > > > > {'tcp'}>tcp</option>";
> > > > > > > > > > > +                               print "<option
> > > > > > > > > > > selected='selected' value='udp'
> > > > > > > > > > > {'udp'}>udp</option>";
> > > > > > > > > > > +                       }
> > > > > > > > > > > +       print <<END;
> > > > > > > > > > > +                               </select></td>
> > > > > > > > > > > +                               <td><input
> > > > > > > > > > > type='text'
> > > > > > > > > > > name='REDIR_ENTRY_PORT'    value='$entry_port'   
> > > > > > > > > > > size='4'></td>
> > > > > > > > > > > +                               <td><input
> > > > > > > > > > > type='text'
> > > > > > > > > > > name='REDIR_ENTRY_ADDRESS' value='$entry_address'
> > > > > > > > > > > size='14'></td>
> > > > > > > > > > > +                               <td><input
> > > > > > > > > > > type='text'
> > > > > > > > > > > name='REDIR_ENTRY_REMARK'  value='$entry_remark' 
> > > > > > > > > > > size='35'></td>
> > > > > > > > > > > +                               <td width='10%'
> > > > > > > > > > > align='center'><input type='submit' name='ACTION'
> > > > > > > > > > > value=' 
> > > > > > > > > > > $buttontext  '></td>
> > > > > > > > > > > +                               <td
> > > > > > > > > > > class='base'></td>
> > > > > > > > > > > +                       </tr>
> > > > > > > > > > > +                       </form>
> > > > > > > > > > > +               </table>
> > > > > > > > > > > +       </div>
> > > > > > > > > > > +END
> > > > > > > > > > > +       &Header::closebox();
> > > > > > > > > > > +}
> > > > > > > > > > > diff --git a/config/rootfiles/common/misc-progs
> > > > > > > > > > > b/config/rootfiles/common/misc-progs
> > > > > > > > > > > index d6594b3f8..fbad2af8b 100644
> > > > > > > > > > > --- a/config/rootfiles/common/misc-progs
> > > > > > > > > > > +++ b/config/rootfiles/common/misc-progs
> > > > > > > > > > > @@ -17,6 +17,7 @@ usr/local/bin/logwatch
> > > > > > > > > > > #usr/local/bin/mpfirectrl
> > > > > > > > > > > usr/local/bin/openvpnctrl
> > > > > > > > > > > usr/local/bin/pakfire
> > > > > > > > > > > +#usr/local/bin/portredirctrl
> > > > > > > > > > > usr/local/bin/qosctrl
> > > > > > > > > > > usr/local/bin/rebuildhosts
> > > > > > > > > > > usr/local/bin/rebuildroutes
> > > > > > > > > > > diff --git a/config/rootfiles/packages/portredir
> > > > > > > > > > > b/config/rootfiles/packages/portredir
> > > > > > > > > > > new file mode 100644
> > > > > > > > > > > index 000000000..4b4ba8366
> > > > > > > > > > > --- /dev/null
> > > > > > > > > > > +++ b/config/rootfiles/packages/portredir
> > > > > > > > > > > @@ -0,0 +1,11 @@
> > > > > > > > > > > +etc/rc.d/init.d/portredir
> > > > > > > > > > > +etc/rc.d/rc0.d/K77portredir
> > > > > > > > > > > +etc/rc.d/rc3.d/S23portredir
> > > > > > > > > > > +etc/rc.d/rc6.d/K77portredir
> > > > > > > > > > > +srv/web/ipfire/cgi-bin/portredir.cgi
> > > > > > > > > > > +usr/local/bin/portredirctrl
> > > > > > > > > > > +var/ipfire/addon-lang/portredir.de.pl
> > > > > > > > > > > +var/ipfire/addon-lang/portredir.en.pl
> > > > > > > > > > > +var/ipfire/backup/addons/includes/portredir
> > > > > > > > > > > +var/ipfire/menu.d/EX-portredir.menu
> > > > > > > > > > > +var/ipfire/portredir
> > > > > > > > > > > diff --git a/lfs/portredir b/lfs/portredir
> > > > > > > > > > > new file mode 100644
> > > > > > > > > > > index 000000000..a4911f71f
> > > > > > > > > > > --- /dev/null
> > > > > > > > > > > +++ b/lfs/portredir
> > > > > > > > > > > @@ -0,0 +1,85 @@
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > #################
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >                #
> > > > > > > > > > > +# IPFire.org - A linux based
> > > > > > > > > > > firewall
> > > > > > > > > > >                                         #
> > > > > > > > > > > +# Copyright (C) 2007-2021  IPFire Team 
> > > > > > > > > > > <info@ipfire.org>                     #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >                #
> > > > > > > > > > > +# This program is free software: you can
> > > > > > > > > > > redistribute it
> > > > > > > > > > > and/or modify        #
> > > > > > > > > > > +# it under the terms of the GNU General Public
> > > > > > > > > > > License as
> > > > > > > > > > > published by        #
> > > > > > > > > > > +# the Free Software Foundation, either version 3
> > > > > > > > > > > of the
> > > > > > > > > > > License, or           #
> > > > > > > > > > > +# (at your option) any later
> > > > > > > > > > > version.
> > > > > > > > > > >                                         #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >                #
> > > > > > > > > > > +# This program is distributed in the hope that
> > > > > > > > > > > it will be
> > > > > > > > > > > useful,             #
> > > > > > > > > > > +# but WITHOUT ANY WARRANTY; without even the
> > > > > > > > > > > implied warranty
> > > > > > > > > > > of              #
> > > > > > > > > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR
> > > > > > > > > > > PURPOSE.  See
> > > > > > > > > > > the               #
> > > > > > > > > > > +# GNU General Public License for more
> > > > > > > > > > > details.                                #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >                #
> > > > > > > > > > > +# You should have received a copy of the GNU
> > > > > > > > > > > General Public
> > > > > > > > > > > License           #
> > > > > > > > > > > +# along with this program.  If not, see <
> > > > > > > > > > > http://www.gnu.org/licenses/>.       #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >                #
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > #################
> > > > > > > > > > > +
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > #################
> > > > > > > > > > > +# Definitions
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > #################
> > > > > > > > > > > +
> > > > > > > > > > > +include Config
> > > > > > > > > > > +
> > > > > > > > > > > +VER        = 1.0
> > > > > > > > > > > +
> > > > > > > > > > > +THISAPP    = portredir-$(VER)
> > > > > > > > > > > +DIR_APP    = $(DIR_SRC)/$(THISAPP)
> > > > > > > > > > > +TARGET     = $(DIR_INFO)/$(THISAPP)
> > > > > > > > > > > +PROG       = portredir
> > > > > > > > > > > +PAK_VER    = 1
> > > > > > > > > > > +
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > #################
> > > > > > > > > > > +# Top-level Rules
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > #################
> > > > > > > > > > > +
> > > > > > > > > > > +install : $(TARGET)
> > > > > > > > > > > +
> > > > > > > > > > > +check :
> > > > > > > > > > > +
> > > > > > > > > > > +download :
> > > > > > > > > > > +
> > > > > > > > > > > +md5 :
> > > > > > > > > > > +
> > > > > > > > > > > +dist: 
> > > > > > > > > > > +       @$(PAK)
> > > > > > > > > > > +
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > #################
> > > > > > > > > > > +# Installation Details
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > #################
> > > > > > > > > > > +
> > > > > > > > > > > +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
> > > > > > > > > > > +       @$(PREBUILD)
> > > > > > > > > > > +       @rm -rf $(DIR_APP) && cd $(DIR_SRC)
> > > > > > > > > > > +
> > > > > > > > > > > +       #install cgi 
> > > > > > > > > > > +       install -v -m 755
> > > > > > > > > > > $(DIR_CONF)/portredir/portredir.cgi
> > > > > > > > > > > /srv/web/ipfire/cgi-bin/
> > > > > > > > > > > +
> > > > > > > > > > > +       #create configuration dir 
> > > > > > > > > > > +       -mkdir -pv /var/ipfire/portredir/
> > > > > > > > > > > +       chown -R nobody:nobody
> > > > > > > > > > > /var/ipfire/portredir/
> > > > > > > > > > > +
> > > > > > > > > > > +       # Install include file for backup
> > > > > > > > > > > +       install -v -m 644
> > > > > > > > > > > $(DIR_CONF)/portredir/portredir-
> > > > > > > > > > > backup
> > > > > > > > > > > /var/ipfire/backup/addons/includes/portredir
> > > > > > > > > > > +
> > > > > > > > > > > +       # Install menu file
> > > > > > > > > > > +       install -v -m 644
> > > > > > > > > > > $(DIR_CONF)/portredir/EX-
> > > > > > > > > > > portredir.menu /var/ipfire/menu.d/
> > > > > > > > > > > +       chown nobody:nobody
> > > > > > > > > > > /var/ipfire/menu.d/EX-
> > > > > > > > > > > portredir.menu
> > > > > > > > > > > +
> > > > > > > > > > > +       # Install addon-specific language-files
> > > > > > > > > > > +       install -v -m 644
> > > > > > > > > > > $(DIR_CONF)/portredir/lang/portredir.*.pl
> > > > > > > > > > > /var/ipfire/addon-
> > > > > > > > > > > lang/
> > > > > > > > > > > +
> > > > > > > > > > > +       #install initscripts
> > > > > > > > > > > +       $(call INSTALL_INITSCRIPT,portredir)
> > > > > > > > > > > +
> > > > > > > > > > > +       # Create symlinks for runlevel
> > > > > > > > > > > interaction.
> > > > > > > > > > > +       ln -svf /etc/rc.d/init.d/portredir
> > > > > > > > > > > /etc/rc.d/rc3.d/S23portredir
> > > > > > > > > > > +       ln -svf /etc/rc.d/init.d/portredir
> > > > > > > > > > > /etc/rc.d/rc0.d/K77portredir
> > > > > > > > > > > +       ln -svf /etc/rc.d/init.d/portredir
> > > > > > > > > > > /etc/rc.d/rc6.d/K77portredir
> > > > > > > > > > > +
> > > > > > > > > > > +       @rm -rf $(DIR_APP)
> > > > > > > > > > > +       @$(POSTBUILD)
> > > > > > > > > > > +
> > > > > > > > > > > diff --git a/make.sh b/make.sh
> > > > > > > > > > > index fc03ebcd5..ab9fe881a 100755
> > > > > > > > > > > --- a/make.sh
> > > > > > > > > > > +++ b/make.sh
> > > > > > > > > > > @@ -1623,6 +1623,7 @@ buildipfire() {
> > > > > > > > > > > lfsmake2 socat
> > > > > > > > > > > lfsmake2 libcdada
> > > > > > > > > > > lfsmake2 pmacct
> > > > > > > > > > > +  lfsmake2 portredir
> > > > > > > > > > > }
> > > > > > > > > > > 
> > > > > > > > > > > buildinstaller() {
> > > > > > > > > > > diff --git a/src/initscripts/packages/portredir
> > > > > > > > > > > b/src/initscripts/packages/portredir
> > > > > > > > > > > new file mode 100644
> > > > > > > > > > > index 000000000..cc57fb9cc
> > > > > > > > > > > --- /dev/null
> > > > > > > > > > > +++ b/src/initscripts/packages/portredir
> > > > > > > > > > > @@ -0,0 +1,191 @@
> > > > > > > > > > > +#!/bin/sh
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > ##########
> > > > > > > > > > > +# Begin $rc_base/init.d/portredir
> > > > > > > > > > > +#
> > > > > > > > > > > +# Description : portredir init script for
> > > > > > > > > > > DNS/NTP and custom 
> > > > > > > > > > > +#              port redirection rules
> > > > > > > > > > > +#
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > ##########
> > > > > > > > > > > +
> > > > > > > > > > > +. /etc/sysconfig/rc
> > > > > > > > > > > +. ${rc_functions}
> > > > > > > > > > > +
> > > > > > > > > > > +IPT="/sbin/iptables";
> > > > > > > > > > > +parent_chain="PREROUTING";
> > > > > > > > > > > +chain="PORT_REDIRECT";
> > > > > > > > > > > +
> > > > > > > > > > > +confdir="/var/ipfire/portredir";
> > > > > > > > > > > +settingsfile="${confdir}/settings";
> > > > > > > > > > > +redirectsfile="${confdir}/redirects";
> > > > > > > > > > > +SYSLOG="NO";
> > > > > > > > > > > +VERBOSE="NO";
> > > > > > > > > > > +
> > > > > > > > > > > +eval $(/usr/local/bin/readhash
> > > > > > > > > > > /var/ipfire/ethernet/settings);
> > > > > > > > > > > +eval $(/usr/local/bin/readhash ${settingsfile});
> > > > > > > > > > > +
> > > > > > > > > > > +logtext() {
> > > > > > > > > > > +       if [ "${SYSLOG}" = "YES" ]; then logger -
> > > > > > > > > > > t "portredir"
> > > > > > > > > > > ${1}; fi;
> > > > > > > > > > > +       if [ "${VERBOSE}" = "YES" ]; then echo
> > > > > > > > > > > ${1}; fi;}
> > > > > > > > > > > +
> > > > > > > > > > > +create_chain() {
> > > > > > > > > > > +
> > > > > > > > > > > +       local line=$(${IPT} -t nat -L
> > > > > > > > > > > ${parent_chain} --line-
> > > > > > > > > > > numbers |grep "SQUID" |awk '{printf($1)}');
> > > > > > > > > > > +
> > > > > > > > > > > +       if [[ "${REDIR_ENABLE_ADDON}" == "off" ||
> > > > > > > > > > > -z
> > > > > > > > > > > "${REDIR_ENABLE_ADDON}" ]]; then
> > > > > > > > > > > +               logtext "addon not enabled in web
> > > > > > > > > > > interface...";
> > > > > > > > > > > +               echo "Portredir addon not enabled
> > > > > > > > > > > in web
> > > > > > > > > > > interface...";
> > > > > > > > > > > +               exit 0;
> > > > > > > > > > > +       fi;
> > > > > > > > > > > +
> > > > > > > > > > > +       if [ -z "$(${IPT} -t nat -L
> > > > > > > > > > > ${parent_chain} |grep
> > > > > > > > > > > ${chain})" ]; then
> > > > > > > > > > > +               ${IPT} -t nat -N ${chain};
> > > > > > > > > > > +
> > > > > > > > > > > +               if [ ! -z "${line}" ]; then
> > > > > > > > > > > +                       logtext "create chain
> > > > > > > > > > > ${chain} and link
> > > > > > > > > > > in ${parent_chain} at position ${line}...";
> > > > > > > > > > > +                       ${IPT} -t nat -I
> > > > > > > > > > > ${parent_chain}
> > > > > > > > > > > ${line} -j ${chain};
> > > > > > > > > > > +               else
> > > > > > > > > > > +                       logtext "create chain
> > > > > > > > > > > ${chain} and link
> > > > > > > > > > > in ${parent_chain} at last position...";
> > > > > > > > > > > +                       ${IPT} -t nat -A
> > > > > > > > > > > ${parent_chain} -j
> > > > > > > > > > > ${chain};
> > > > > > > > > > > +               fi
> > > > > > > > > > > +       else
> > > > > > > > > > > +               return 1;
> > > > > > > > > > > +       fi;
> > > > > > > > > > > +       return 0;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +remove_chain() {
> > > > > > > > > > > +       if [ ! -z "$(${IPT} -t nat -L
> > > > > > > > > > > ${parent_chain} |grep
> > > > > > > > > > > ${chain})" ]; then
> > > > > > > > > > > +               logtext "remove chain ${chain}
> > > > > > > > > > > and link in
> > > > > > > > > > > ${parent_chain} from system...";
> > > > > > > > > > > +               ${IPT} -t nat -D
> > > > > > > > > > > "${parent_chain}" -j ${chain};
> > > > > > > > > > > +               ${IPT} -t nat -F ${chain};
> > > > > > > > > > > +               ${IPT} -t nat -X ${chain};
> > > > > > > > > > > +       else
> > > > > > > > > > > +               return 1;
> > > > > > > > > > > +       fi;
> > > > > > > > > > > +       return 0;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +activate_custom_redirections() {
> > > > > > > > > > > +       
> > > > > > > > > > > +       local array=();
> > > > > > > > > > > +       local redirects=();
> > > > > > > > > > > +       local i;
> > > > > > > > > > > +       index=();
> > > > > > > > > > > +       iface=();
> > > > > > > > > > > +       protocol=();
> > > > > > > > > > > +       port=();
> > > > > > > > > > > +       targetip=();
> > > > > > > > > > > +       enabled=();
> > > > > > > > > > > +
> > > > > > > > > > > +       IFS=$'\n' read -d '' -ra redirects <
> > > > > > > > > > > ${redirectsfile};
> > > > > > > > > > > +
> > > > > > > > > > > +       for i in "${!redirects[@]}"
> > > > > > > > > > > +       do
> > > > > > > > > > > +               IFS=$',' read -ra array <<<
> > > > > > > > > > > ${redirects[i]};
> > > > > > > > > > > +               index[i]=${array[0]};
> > > > > > > > > > > +               iface[i]=${array[1]};
> > > > > > > > > > > +               protocol[i]=${array[2]};
> > > > > > > > > > > +               port[i]=${array[3]};
> > > > > > > > > > > +               targetip[i]=${array[4]};
> > > > > > > > > > > +               enabled[i]=${array[5]};
> > > > > > > > > > > +       done
> > > > > > > > > > > +
> > > > > > > > > > > +       for i in "${!index[@]}"
> > > > > > > > > > > +       do
> > > > > > > > > > > +               if [[ ! -z "${GREEN_DEV}" &&
> > > > > > > > > > >  "${iface[i]}" =
> > > > > > > > > > > "green" && "${enabled[i]}" = "enabled" ]]; then
> > > > > > > > > > > +
> > > > > > > > > > > +                       logtext "add redirect in
> > > > > > > > > > > ${chain} on
> > > > > > > > > > > ${GREEN_DEV} ip ${targetip[i]} protocol
> > > > > > > > > > > ${protocol[i]} port
> > > > > > > > > > > ${port[i]} ";
> > > > > > > > > > > +                       ${IPT} -t nat -A ${chain}
> > > > > > > > > > > -i
> > > > > > > > > > > ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]}
> > > > > > > > > > > -m
> > > > > > > > > > > ${protocol[i]} --dport ${port[i]} -j RETURN;
> > > > > > > > > > > +                       ${IPT} -t nat -A ${chain}
> > > > > > > > > > > -i
> > > > > > > > > > > ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]}
> > > > > > > > > > > --dport
> > > > > > > > > > > ${port[i]} -j REDIRECT;
> > > > > > > > > > > +               fi
> > > > > > > > > > > +               if [[ ! -z "${BLUE_DEV}" &&
> > > > > > > > > > >  "${iface[i]}" =
> > > > > > > > > > > "blue" && "${enabled[i]}" = "enabled" ]]; then
> > > > > > > > > > > +                       logtext "add redirect in
> > > > > > > > > > > ${chain} on
> > > > > > > > > > > ${BLUE_DEV} ip ${targetip[i]} protocol
> > > > > > > > > > > ${protocol[i]} port
> > > > > > > > > > > ${port[i]} ";
> > > > > > > > > > > +                       ${IPT} -t nat -A ${chain}
> > > > > > > > > > > -i
> > > > > > > > > > > ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -
> > > > > > > > > > > m
> > > > > > > > > > > ${protocol[i]} --dport ${port[i]} -j RETURN;
> > > > > > > > > > > +                       ${IPT} -t nat -A ${chain}
> > > > > > > > > > > -i
> > > > > > > > > > > ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} -
> > > > > > > > > > > -dport
> > > > > > > > > > > ${port[i]} -j REDIRECT;
> > > > > > > > > > > +               fi
> > > > > > > > > > > +               if [[ ! -z "${ORANGE_DEV}" &&
> > > > > > > > > > >  "${iface[i]}" =
> > > > > > > > > > > "orange" && "${enabled[i]}" = "enabled" ]]; then
> > > > > > > > > > > +                       logtext "add redirect in
> > > > > > > > > > > ${chain} on
> > > > > > > > > > > ${ORANGE_DEV} ip ${targetip[i]} protocol
> > > > > > > > > > > ${protocol[i]} port
> > > > > > > > > > > ${port[i]} ";
> > > > > > > > > > > +                       ${IPT} -t nat -A ${chain}
> > > > > > > > > > > -i
> > > > > > > > > > > ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]}
> > > > > > > > > > > -m
> > > > > > > > > > > ${protocol[i]} --dport ${port[i]} -j RETURN;
> > > > > > > > > > > +                       ${IPT} -t nat -A ${chain}
> > > > > > > > > > > -i
> > > > > > > > > > > ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]}
> > > > > > > > > > > --dport
> > > > > > > > > > > ${port[i]} -j REDIRECT;
> > > > > > > > > > > +               fi
> > > > > > > > > > > +       done
> > > > > > > > > > > +       unset array redirects i index iface
> > > > > > > > > > > protocol port
> > > > > > > > > > > targetip enabled;
> > > > > > > > > > > +       return 0;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +activate_redirections() {
> > > > > > > > > > > +
> > > > > > > > > > > +       if ! create_chain; then return 1; fi;
> > > > > > > > > > > +       
> > > > > > > > > > > +       # Force DNS REDIRECTs on GREEN (udp, tcp,
> > > > > > > > > > > 53)
> > > > > > > > > > > +       if [[ "${REDIR_DNS_GREEN}" == "on" && 
> > > > > > > > > > > "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${GREEN_DEV} -d
> > > > > > > > > > > ${GREEN_ADDRESS} -p udp -m udp --dport domain -j
> > > > > > > > > > > RETURN;
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${GREEN_DEV} -p
> > > > > > > > > > > udp -m udp --dport domain -j REDIRECT;
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${GREEN_DEV} -d
> > > > > > > > > > > ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j
> > > > > > > > > > > RETURN;
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${GREEN_DEV} -p
> > > > > > > > > > > tcp -m tcp --dport domain -j REDIRECT;
> > > > > > > > > > > +       fi
> > > > > > > > > > > +
> > > > > > > > > > > +       # Force DNS REDIRECTs on BLUE (udp, tcp,
> > > > > > > > > > > 53)
> > > > > > > > > > > +       if [[ "${REDIR_DNS_BLUE}" == "on" && 
> > > > > > > > > > > "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${BLUE_DEV} -d
> > > > > > > > > > > ${BLUE_ADDRESS} -p udp -m udp --dport domain -j
> > > > > > > > > > > RETURN
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${BLUE_DEV} -p udp
> > > > > > > > > > > -m udp --dport domain -j REDIRECT
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${BLUE_DEV} -d
> > > > > > > > > > > ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j
> > > > > > > > > > > RETURN
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${BLUE_DEV} -p tcp
> > > > > > > > > > > -m tcp --dport domain -j REDIRECT
> > > > > > > > > > > +       fi
> > > > > > > > > > > +
> > > > > > > > > > > +       # Force DNS REDIRECTs on ORANGE (udp,
> > > > > > > > > > > tcp, 53)
> > > > > > > > > > > +       if [[ "${REDIR_DNS_ORANGE}" == "on" && 
> > > > > > > > > > > "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${ORANGE_DEV} -d
> > > > > > > > > > > ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j
> > > > > > > > > > > RETURN
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${ORANGE_DEV} -p
> > > > > > > > > > > udp -m udp --dport domain -j REDIRECT
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${ORANGE_DEV} -d
> > > > > > > > > > > ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j
> > > > > > > > > > > RETURN
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${ORANGE_DEV} -p
> > > > > > > > > > > tcp -m tcp --dport domain -j REDIRECT
> > > > > > > > > > > +       fi
> > > > > > > > > > > +
> > > > > > > > > > > +       # Force NTP REDIRECTs on GREEN (udp, 123)
> > > > > > > > > > > +       if [[ "${REDIR_NTP_GREEN}" == "on" && 
> > > > > > > > > > > "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${GREEN_DEV} -d
> > > > > > > > > > > ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j
> > > > > > > > > > > RETURN
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${GREEN_DEV} -p
> > > > > > > > > > > udp -m udp --dport ntp -j REDIRECT
> > > > > > > > > > > +       fi
> > > > > > > > > > > +
> > > > > > > > > > > +       # Force NTP REDIRECTs on BLUE (udp, 123)
> > > > > > > > > > > +       if [[ "${REDIR_NTP_BLUE}" == "on" && 
> > > > > > > > > > > "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${BLUE_DEV} -d
> > > > > > > > > > > ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j
> > > > > > > > > > > RETURN
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${BLUE_DEV} -p udp
> > > > > > > > > > > -m udp --dport ntp -j REDIRECT
> > > > > > > > > > > +       fi
> > > > > > > > > > > +
> > > > > > > > > > > +       # Force NTP REDIRECTs on ORANGE (udp,
> > > > > > > > > > > 123)
> > > > > > > > > > > +       if [[ "${REDIR_NTP_ORANGE}" == "on" && 
> > > > > > > > > > > "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${ORANGE_DEV} -d
> > > > > > > > > > > ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j
> > > > > > > > > > > RETURN
> > > > > > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > > > > > ${ORANGE_DEV} -p
> > > > > > > > > > > udp -m udp --dport ntp -j REDIRECT
> > > > > > > > > > > +       fi
> > > > > > > > > > > +
> > > > > > > > > > > +       if ! activate_custom_redirections; then
> > > > > > > > > > > return 1; fi;
> > > > > > > > > > > +
> > > > > > > > > > > +       return 0;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +case "${1}" in
> > > > > > > > > > > +       start)
> > > > > > > > > > > +               boot_mesg "Loading port
> > > > > > > > > > > redirections..."
> > > > > > > > > > > +               activate_redirections;
> > > > > > > > > > > +               evaluate_retval;
> > > > > > > > > > > +               ;;
> > > > > > > > > > > +
> > > > > > > > > > > +       stop)   
> > > > > > > > > > > +               boot_mesg "Removing port
> > > > > > > > > > > redirections..."
> > > > > > > > > > > +               remove_chain;
> > > > > > > > > > > +               evaluate_retval;
> > > > > > > > > > > +               ;;
> > > > > > > > > > > +
> > > > > > > > > > > +       restart)
> > > > > > > > > > > +               ${0} stop
> > > > > > > > > > > +               ${0} start
> > > > > > > > > > > +               ;;
> > > > > > > > > > > +
> > > > > > > > > > > +       *)
> > > > > > > > > > > +               echo "Usage: ${0}
> > > > > > > > > > > {start|stop|restart}"
> > > > > > > > > > > +               exit 1
> > > > > > > > > > > +               ;;
> > > > > > > > > > > +esac
> > > > > > > > > > > +
> > > > > > > > > > > +# End $rc_base/init.d/portredir
> > > > > > > > > > > diff --git a/src/misc-progs/Makefile b/src/misc-
> > > > > > > > > > > progs/Makefile
> > > > > > > > > > > index 7c3ef7529..850f8fdcc 100644
> > > > > > > > > > > --- a/src/misc-progs/Makefile
> > > > > > > > > > > +++ b/src/misc-progs/Makefile
> > > > > > > > > > > @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl
> > > > > > > > > > > ipfirereboot \
> > > > > > > > > > >        wirelessctrl getipstat qosctrl \
> > > > > > > > > > >        redctrl syslogdctrl extrahdctrl sambactrl
> > > > > > > > > > > \
> > > > > > > > > > >        smartctrl clamavctrl addonctrl pakfire
> > > > > > > > > > > mpfirectrl
> > > > > > > > > > > wlanapctrl \
> > > > > > > > > > > -       setaliases urlfilterctrl updxlratorctrl
> > > > > > > > > > > fireinfoctrl
> > > > > > > > > > > rebuildroutes \
> > > > > > > > > > > +       setaliases urlfilterctrl updxlratorctrl
> > > > > > > > > > > fireinfoctrl
> > > > > > > > > > > rebuildroutes portredirctrl \
> > > > > > > > > > >        getconntracktable wirelessclient torctrl
> > > > > > > > > > > ddnsctrl
> > > > > > > > > > > unboundctrl \
> > > > > > > > > > >        captivectrl
> > > > > > > > > > > 
> > > > > > > > > > > diff --git a/src/misc-progs/portredirctrl.c
> > > > > > > > > > > b/src/misc-
> > > > > > > > > > > progs/portredirctrl.c
> > > > > > > > > > > new file mode 100644
> > > > > > > > > > > index 000000000..7897d711c
> > > > > > > > > > > --- /dev/null
> > > > > > > > > > > +++ b/src/misc-progs/portredirctrl.c
> > > > > > > > > > > @@ -0,0 +1,47 @@
> > > > > > > > > > > +/* This file is part of the IPFire Firewall.
> > > > > > > > > > > + *
> > > > > > > > > > > + * This program is distributed under the terms
> > > > > > > > > > > of the GNU
> > > > > > > > > > > General Public
> > > > > > > > > > > + * Licence.  See the file COPYING for details.
> > > > > > > > > > > + *
> > > > > > > > > > > + */
> > > > > > > > > > > +
> > > > > > > > > > > +#include <stdlib.h>
> > > > > > > > > > > +#include <stdio.h>
> > > > > > > > > > > +#include <string.h>
> > > > > > > > > > > +#include <unistd.h>
> > > > > > > > > > > +#include <sys/types.h>
> > > > > > > > > > > +#include <fcntl.h>
> > > > > > > > > > > +#include "setuid.h"
> > > > > > > > > > > +
> > > > > > > > > > > +int main(int argc, char *argv[]) {
> > > > > > > > > > > +       if (!(initsetuid()))
> > > > > > > > > > > +               exit(1);
> > > > > > > > > > > +
> > > > > > > > > > > +       // Check what command is asked
> > > > > > > > > > > +        if (argc < 2) {
> > > > > > > > > > > +                fprintf(stderr, "\nNo argument
> > > > > > > > > > > given.\n\nportredirctrl
> > > > > > > > > > > (start|stop|restart|enable|disable)\n\n");
> > > > > > > > > > > +                exit(1);
> > > > > > > > > > > +        }
> > > > > > > > > > > +
> > > > > > > > > > > +        if (strcmp(argv[1], "start") == 0) {
> > > > > > > > > > > +
> > > > > > > > > > >                safe_system("/etc/rc.d/init.d/port
> > > > > > > > > > > redir
> > > > > > > > > > > start");
> > > > > > > > > > > +        } else if (strcmp(argv[1], "stop") == 0)
> > > > > > > > > > > {
> > > > > > > > > > > +
> > > > > > > > > > >                safe_system("/etc/rc.d/init.d/port
> > > > > > > > > > > redir
> > > > > > > > > > > stop");
> > > > > > > > > > > +        } else if (strcmp(argv[1], "restart") ==
> > > > > > > > > > > 0) {
> > > > > > > > > > > +
> > > > > > > > > > >                safe_system("/etc/rc.d/init.d/port
> > > > > > > > > > > redir
> > > > > > > > > > > restart");
> > > > > > > > > > > +       } else if (strcmp(argv[1], "enable") ==
> > > > > > > > > > > 0) {
> > > > > > > > > > > +               safe_system("touch
> > > > > > > > > > > /var/ipfire/portredir/enable");
> > > > > > > > > > > +               safe_system("ln -snf
> > > > > > > > > > > /etc/rc.d/init.d/portredir
> > > > > > > > > > > /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
> > > > > > > > > > > +               safe_system("ln -snf
> > > > > > > > > > > /etc/rc.d/init.d/portredir
> > > > > > > > > > > /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
> > > > > > > > > > > +               safe_system("ln -snf
> > > > > > > > > > > /etc/rc.d/init.d/portredir
> > > > > > > > > > > /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
> > > > > > > > > > > +       } else if (strcmp(argv[1], "disable") ==
> > > > > > > > > > > 0) {
> > > > > > > > > > > +
> > > > > > > > > > >               safe_system("/etc/rc.d/init.d/portr
> > > > > > > > > > > edir stop");
> > > > > > > > > > > +               safe_system("unlink
> > > > > > > > > > > /var/ipfire/portredir/enable");
> > > > > > > > > > > +               safe_system("rm -rf
> > > > > > > > > > > /etc/rc.d/rc*.d/*portredir
> > > > > > > > > > > > /dev/null 2>&1");
> > > > > > > > > > > +        } else {
> > > > > > > > > > > +                fprintf(stderr, "\nBad argument
> > > > > > > > > > > given.\n\nportredirctrl
> > > > > > > > > > > (start|stop|restart|enable|disable)\n\n");
> > > > > > > > > > > +                exit(1);
> > > > > > > > > > > +        }
> > > > > > > > > > > +
> > > > > > > > > > > +       return 0;
> > > > > > > > > > > +}
> > > > > > > > > > > diff --git a/src/paks/portredir/install.sh
> > > > > > > > > > > b/src/paks/portredir/install.sh
> > > > > > > > > > > new file mode 100644
> > > > > > > > > > > index 000000000..9f69aeae2
> > > > > > > > > > > --- /dev/null
> > > > > > > > > > > +++ b/src/paks/portredir/install.sh
> > > > > > > > > > > @@ -0,0 +1,32 @@
> > > > > > > > > > > +#!/bin/bash
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > ##############
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +# This file is part of the IPFire
> > > > > > > > > > > Firewall.                                #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +# IPFire is free software; you can redistribute
> > > > > > > > > > > it and/or
> > > > > > > > > > > modify           #
> > > > > > > > > > > +# it under the terms of the GNU General Public
> > > > > > > > > > > License as
> > > > > > > > > > > published by     #
> > > > > > > > > > > +# the Free Software Foundation; either version 2
> > > > > > > > > > > of the
> > > > > > > > > > > License, or        #
> > > > > > > > > > > +# (at your option) any later
> > > > > > > > > > > version.                                      #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +# IPFire is distributed in the hope that it will
> > > > > > > > > > > be
> > > > > > > > > > > useful,                #
> > > > > > > > > > > +# but WITHOUT ANY WARRANTY; without even the
> > > > > > > > > > > implied warranty
> > > > > > > > > > > of           #
> > > > > > > > > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR
> > > > > > > > > > > PURPOSE.  See
> > > > > > > > > > > the            #
> > > > > > > > > > > +# GNU General Public License for more
> > > > > > > > > > > details.                             #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +# You should have received a copy of the GNU
> > > > > > > > > > > General Public
> > > > > > > > > > > License        #
> > > > > > > > > > > +# along with IPFire; if not, write to the Free
> > > > > > > > > > > Software                    #
> > > > > > > > > > > +# Foundation, Inc., 59 Temple Place, Suite 330,
> > > > > > > > > > > Boston, MA 
> > > > > > > > > > > 02111-1307 USA #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +# Copyright (C) 2021 IPFire-Team
> > > > > > > > > > > <info@ipfire.org>.                        #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > ##############
> > > > > > > > > > > +#
> > > > > > > > > > > +. /opt/pakfire/lib/functions.sh
> > > > > > > > > > > +extract_files
> > > > > > > > > > > +restore_backup ${NAME}
> > > > > > > > > > > +
> > > > > > > > > > > +/usr/local/bin/update-lang-cache
> > > > > > > > > > > +
> > > > > > > > > > > +chown root:nobody /usr/local/bin/portredirctrl
> > > > > > > > > > > +chmod 4750 /usr/local/bin/portredirctrl
> > > > > > > > > > > +chmod u+s /usr/local/bin/portredirctrl
> > > > > > > > > > > diff --git a/src/paks/portredir/uninstall.sh
> > > > > > > > > > > b/src/paks/portredir/uninstall.sh
> > > > > > > > > > > new file mode 100644
> > > > > > > > > > > index 000000000..df9270125
> > > > > > > > > > > --- /dev/null
> > > > > > > > > > > +++ b/src/paks/portredir/uninstall.sh
> > > > > > > > > > > @@ -0,0 +1,28 @@
> > > > > > > > > > > +#!/bin/bash
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > ##############
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +# This file is part of the IPFire
> > > > > > > > > > > Firewall.                                #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +# IPFire is free software; you can redistribute
> > > > > > > > > > > it and/or
> > > > > > > > > > > modify           #
> > > > > > > > > > > +# it under the terms of the GNU General Public
> > > > > > > > > > > License as
> > > > > > > > > > > published by     #
> > > > > > > > > > > +# the Free Software Foundation; either version 2
> > > > > > > > > > > of the
> > > > > > > > > > > License, or        #
> > > > > > > > > > > +# (at your option) any later
> > > > > > > > > > > version.                                      #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +# IPFire is distributed in the hope that it will
> > > > > > > > > > > be
> > > > > > > > > > > useful,                #
> > > > > > > > > > > +# but WITHOUT ANY WARRANTY; without even the
> > > > > > > > > > > implied warranty
> > > > > > > > > > > of           #
> > > > > > > > > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR
> > > > > > > > > > > PURPOSE.  See
> > > > > > > > > > > the            #
> > > > > > > > > > > +# GNU General Public License for more
> > > > > > > > > > > details.                             #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +# You should have received a copy of the GNU
> > > > > > > > > > > General Public
> > > > > > > > > > > License        #
> > > > > > > > > > > +# along with IPFire; if not, write to the Free
> > > > > > > > > > > Software                    #
> > > > > > > > > > > +# Foundation, Inc., 59 Temple Place, Suite 330,
> > > > > > > > > > > Boston, MA 
> > > > > > > > > > > 02111-1307 USA #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +# Copyright (C) 2007 IPFire-Team
> > > > > > > > > > > <info@ipfire.org>.                        #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > ##############
> > > > > > > > > > > +#
> > > > > > > > > > > +. /opt/pakfire/lib/functions.sh
> > > > > > > > > > > +make_backup ${NAME}
> > > > > > > > > > > +remove_files
> > > > > > > > > > > +
> > > > > > > > > > > +/usr/local/bin/update-lang-cache
> > > > > > > > > > > diff --git a/src/paks/portredir/update.sh
> > > > > > > > > > > b/src/paks/portredir/update.sh
> > > > > > > > > > > new file mode 100644
> > > > > > > > > > > index 000000000..89c40d0d7
> > > > > > > > > > > --- /dev/null
> > > > > > > > > > > +++ b/src/paks/portredir/update.sh
> > > > > > > > > > > @@ -0,0 +1,26 @@
> > > > > > > > > > > +#!/bin/bash
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > ##############
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +# This file is part of the IPFire
> > > > > > > > > > > Firewall.                                #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +# IPFire is free software; you can redistribute
> > > > > > > > > > > it and/or
> > > > > > > > > > > modify           #
> > > > > > > > > > > +# it under the terms of the GNU General Public
> > > > > > > > > > > License as
> > > > > > > > > > > published by     #
> > > > > > > > > > > +# the Free Software Foundation; either version 2
> > > > > > > > > > > of the
> > > > > > > > > > > License, or        #
> > > > > > > > > > > +# (at your option) any later
> > > > > > > > > > > version.                                      #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +# IPFire is distributed in the hope that it will
> > > > > > > > > > > be
> > > > > > > > > > > useful,                #
> > > > > > > > > > > +# but WITHOUT ANY WARRANTY; without even the
> > > > > > > > > > > implied warranty
> > > > > > > > > > > of           #
> > > > > > > > > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR
> > > > > > > > > > > PURPOSE.  See
> > > > > > > > > > > the            #
> > > > > > > > > > > +# GNU General Public License for more
> > > > > > > > > > > details.                             #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +# You should have received a copy of the GNU
> > > > > > > > > > > General Public
> > > > > > > > > > > License        #
> > > > > > > > > > > +# along with IPFire; if not, write to the Free
> > > > > > > > > > > Software                    #
> > > > > > > > > > > +# Foundation, Inc., 59 Temple Place, Suite 330,
> > > > > > > > > > > Boston, MA 
> > > > > > > > > > > 02111-1307 USA #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +# Copyright (C) 2007 IPFire-Team
> > > > > > > > > > > <info@ipfire.org>.                        #
> > > > > > > > > > > +#
> > > > > > > > > > >                                                  
> > > > > > > > > > >            
> > > > > > > > > > >             #
> > > > > > > > > > > +################################################
> > > > > > > > > > > ##############
> > > > > > > > > > > ##############
> > > > > > > > > > > +#
> > > > > > > > > > > +. /opt/pakfire/lib/functions.sh
> > > > > > > > > > > +./uninstall.sh
> > > > > > > > > > > +./install.sh
> > > > > > > > > > > -- 
> > > > > > > > > > > 2.18.0
> > > > > > > > > > > 
> > > > > > > > > > 
> > > > > > > > > 
> > > > > > > > 
> > > > > > > 
> > > > > > 
> > > > > 
> > > > 
> > > 
> > 
>
  
Jon Murphy Aug. 3, 2021, 4:04 p.m. UTC | #13
Hello all!

I’m having trouble following the changes to firewall.cgi.  I think there were 4 or 5 patches 
submitted to the Dev Mailing List.

Did these get approved?

I looked at the Changelog for Core 158 and core 159 (testing) but I didn’t see anything:

https://nightly.ipfire.org/core158/2021-07-21%2015:47:35%20+0000-23498c61/x86_64/changelog.txt <https://nightly.ipfire.org/core158/2021-07-21%2015:47:35%20+0000-23498c61/x86_64/changelog.txt>

https://nightly.ipfire.org/master/latest/x86_64/changelog.txt <https://nightly.ipfire.org/master/latest/x86_64/changelog.txt>


Can they be added to CU 159?

To be honest I am mainly interested in the DNS redirect (and its cousin NTP redirect).

I’d be happy to help test but I do need some help applying the patches.  
Right now I apply them by grabbing a copy of firewall.cgi and manually deleting and then manually adding a patch.
Not the smartest way but it works (if I don’t screw-up and make a mistake!).


Jon

> On Jun 30, 2021, at 2:14 PM, Stefan Schantl <stefan.schantl@ipfire.org> wrote:
> 
> Hello Matthias, Hello Michael, Hello Jon, Hello *,
> 
> I've followed the conversation on this list since the first mail and
> thoughts about forcing DNS traffic to use the local resolver.
> 
> It was a very long journey and lot of time and work has been spent to
> get to the present point.
> 
> As Michael requested here, I've digged through the lines of the perl
> script which is responsible for creating the firewall rules and
> surprisingly found that everyting which is needed to create generic
> REDIRECT rules already was written in the past - it just did not work
> as designed/expected.
> 
> Finaly I was able to adjust these lines of code and to repair that
> feature.
> 
> A redirect rule can be created by picking a single host or group of
> hosts or entire network(s) as source, selecting NAT (DNAT) and choosing
> the Firewall itself as target.
> 
> The protocol or service or service group which should be redirected has
> to be selected afterwards. If you want to redirect a given port to
> another one it can be specified as "Target port".
> 
> All created redirect rules are displayed as "input rules".
> 
> 
> The patch directly can be accessed here:
> 
> https://patchwork.ipfire.org/project/ipfire/patch/20210630184031.7726-1-stefan.schantl@ipfire.org/
> 
> Best regards,
> 
> -Stefan
> 
>> Hello,
>> 
>>> On 28 Jun 2021, at 18:53, Jon Murphy <jcmurphy26@gmail.com> wrote:
>>> 
>>> Hi Michael!  Happy Monday!
>>> 
>>> 
>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>> of code instead of adding many hundreds of lines?
>>>> 
>>>> Please can someone elaborate on this more?
>>> 
>>> Doing a DNS redirect, via the WegBUI, has been an issue since
>>> 2015.  I found this quote in the old forum:
>>> 
>>> "Having investigated a bit more I have concluded that it's not
>>> currently possible to create such rules through the WUI.
>>> 
>>> There are a number of obstacles:
>>> 1. It is not allowed to create a rule where source IP and
>>> destination nat IP is on the same subnetwork (e.g. GREEN), WUI
>>> error message: "Source and destination IP addresses are from the
>>> same subnet."
>>> 
>>> 2. WUI will not allow you to create a rule without a destination
>>> (the filtered packet must adhere to a destination, not only a port)
>>> and the destination MUST be an IP address of one of the IPFire
>>> interfaces, which limits whats possible a great deal." 
>> 
>> And these cannot be changed?
>> 
>>> And I found this from 2016:
>>> https://bugzilla.ipfire.org/show_bug.cgi?id=11168
>>> 
>>> So I am guessing that no one has been able to determine a way to
>>> extend the WebGUI.  
>> 
>> Has anyone tried? I do not see any obvious reasons why this should
>> not be possible.
>> 
>>> I am curious - Who created the 
>>> https://ipfire:444/cgi-bin/firewall.cgi page?  And could they help?
>> 
>> -Michael
>> 
>>> Jon
>>> 
>>> 
>>>> On Jun 28, 2021, at 11:04 AM, Michael Tremer <
>>>> michael.tremer@ipfire.org> wrote:
>>>> 
>>>> Hello Matthias,
>>>> 
>>>>> On 27 Jun 2021, at 14:48, Matthias Fischer <
>>>>> matthias.fischer@ipfire.org> wrote:
>>>>> 
>>>>> From: Marcel Lorenz <marcel.lorenz@ipfire.org>
>>>> 
>>>> Thank you for sending this patch on Marcel’s behalf, but I would
>>>> much more prefer if he would submit his patches on his own. I do
>>>> not see why that isn’t possible.
>>>> 
>>>>> Please note:
>>>>> This is a new addon written by Marcel Lorenz <
>>>>> marcel.lorenz@ipfire.org>.
>>>>> 
>>>>> It adds a new GUI to IPFire for DNS/NTP *and* user specific
>>>>> port redirections.
>>>>> 
>>>>> How its working:
>>>>> It has exactly the same functionalities as "Forcing
>>>>> DNS/NTP..."  - and some more.
>>>>> 
>>>>> By setting switches, DNS/NTP requests are automatically
>>>>> redirected to the local IPFire DNS/NTP servers.
>>>>> 
>>>>> Additionally, the user can specify custom redirections.
>>>>> 
>>>>> These rules are added to a new chain in PREROUTING =>
>>>>> PORT_REDIRECT.
>>>>> 
>>>>> To avoid problems with (e.g.) transparent 'squid'
>>>>> configurations,
>>>>> redirection rules are added automatically before existing
>>>>> 'squid' rules.
>>>> 
>>>> This message does unfortunately not say why this add-on would be
>>>> useful. I am emphasising this again and again that it is not very
>>>> important how something is done specially. That should be
>>>> commented in the code and other implementation details should
>>>> also be documented there.
>>>> 
>>>> As I have stated on this functionality many times before, I do
>>>> not see why this is necessary at all.
>>>> 
>>>> Why is this an add-on?
>>>> 
>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>> of code instead of adding many hundreds of lines?
>>>> 
>>>> Please can someone elaborate on this more?
>>>> 
>>>> -Michael
>>>> 
>>>>> Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
>>>>> ---
>>>>> config/portredir/EX-portredir.menu    |   6 +
>>>>> config/portredir/lang/portredir.de.pl |  19 +
>>>>> config/portredir/lang/portredir.en.pl |  19 +
>>>>> config/portredir/portredir-backup     |   1 +
>>>>> config/portredir/portredir.cgi        | 525
>>>>> ++++++++++++++++++++++++++
>>>>> config/rootfiles/common/misc-progs    |   1 +
>>>>> config/rootfiles/packages/portredir   |  11 +
>>>>> lfs/portredir                         |  85 +++++
>>>>> make.sh                               |   1 +
>>>>> src/initscripts/packages/portredir    | 191 ++++++++++
>>>>> src/misc-progs/Makefile               |   2 +-
>>>>> src/misc-progs/portredirctrl.c        |  47 +++
>>>>> src/paks/portredir/install.sh         |  32 ++
>>>>> src/paks/portredir/uninstall.sh       |  28 ++
>>>>> src/paks/portredir/update.sh          |  26 ++
>>>>> 15 files changed, 993 insertions(+), 1 deletion(-)
>>>>> create mode 100644 config/portredir/EX-portredir.menu
>>>>> create mode 100644 config/portredir/lang/portredir.de.pl
>>>>> create mode 100644 config/portredir/lang/portredir.en.pl
>>>>> create mode 100644 config/portredir/portredir-backup
>>>>> create mode 100644 config/portredir/portredir.cgi
>>>>> create mode 100644 config/rootfiles/packages/portredir
>>>>> create mode 100644 lfs/portredir
>>>>> create mode 100644 src/initscripts/packages/portredir
>>>>> create mode 100644 src/misc-progs/portredirctrl.c
>>>>> create mode 100644 src/paks/portredir/install.sh
>>>>> create mode 100644 src/paks/portredir/uninstall.sh
>>>>> create mode 100644 src/paks/portredir/update.sh
>>>>> 
>>>>> diff --git a/config/portredir/EX-portredir.menu
>>>>> b/config/portredir/EX-portredir.menu
>>>>> new file mode 100644
>>>>> index 000000000..8376e8053
>>>>> --- /dev/null
>>>>> +++ b/config/portredir/EX-portredir.menu
>>>>> @@ -0,0 +1,6 @@
>>>>> +    $subfirewall->{'95.portredir'} = {
>>>>> +                               'caption' =>
>>>>> $Lang::tr{'portredir port redirections'},
>>>>> +                               'uri' => '/cgi-
>>>>> bin/portredir.cgi',
>>>>> +                               'title' =>
>>>>> "$Lang::tr{'portredir port redirections'}",
>>>>> +                               'enabled' => 1
>>>>> +                               };
>>>>> diff --git a/config/portredir/lang/portredir.de.pl
>>>>> b/config/portredir/lang/portredir.de.pl
>>>>> new file mode 100644
>>>>> index 000000000..b932d4a85
>>>>> --- /dev/null
>>>>> +++ b/config/portredir/lang/portredir.de.pl
>>>>> @@ -0,0 +1,19 @@
>>>>> +%tr = (
>>>>> +%tr,
>>>>> +'portredir enable addon' => 'Addon aktivieren',
>>>>> +'portredir common settings' => 'Allgemeine Einstellungen',
>>>>> +'portredir port redirections' => 'Portumleitungen',
>>>>> +'portredir fw for interface' => 'Firewalloptionen für das
>>>>> Interface',
>>>>> +'portredir enable user redirections' => 'Aktiviere
>>>>> benutzerdefinierte Portumleitungen',
>>>>> +'portredir force local dns' => 'Erzwinge lokale DNS-Server',
>>>>> +'portredir force local ntp' => 'Erzwinge lokale NTP-Server',
>>>>> +'portredir custom redirections' => 'Benutzerdefinierte
>>>>> Portumleitungen',
>>>>> +'portredir remove rule' => 'Entferne Regel',
>>>>> +'portredir add rule' => 'Hinzufügen',
>>>>> +'portredir no entries' => 'Keine Einträge vorhanden.',
>>>>> +'portredir invalid address' => 'Ungültige Host-Addresse.',
>>>>> +'portredir empty input' => 'Fehlende Angabe: Bitte geben Sie
>>>>> einen gültigen Host an.',
>>>>> +'portredir save to activate' => 'Speichern, um Änderungen zu
>>>>> aktivieren',
>>>>> +);
>>>>> +
>>>>> +#EOF
>>>>> diff --git a/config/portredir/lang/portredir.en.pl
>>>>> b/config/portredir/lang/portredir.en.pl
>>>>> new file mode 100644
>>>>> index 000000000..f442f3eaa
>>>>> --- /dev/null
>>>>> +++ b/config/portredir/lang/portredir.en.pl
>>>>> @@ -0,0 +1,19 @@
>>>>> +%tr = (
>>>>> +%tr,
>>>>> +'portredir enable addon' => 'Enable addon',
>>>>> +'portredir common settings' => 'Common settings',
>>>>> +'portredir port redirections' => 'Port redirections',
>>>>> +'portredir fw for interface' => 'Firewall options for
>>>>> interface',
>>>>> +'portredir enable user redirections' => 'Enable user port
>>>>> redirections',
>>>>> +'portredir force local dns' => 'Enforce local DNS servers',
>>>>> +'portredir force local ntp' => 'Enforce local NTP servers',
>>>>> +'portredir custom redirections' => 'Custom port redirections',
>>>>> +'portredir remove rule' => 'Remove rule',
>>>>> +'portredir add rule' => 'Add new',
>>>>> +'portredir no entries' => 'No entries at the moment.',
>>>>> +'portredir invalid address' => 'Invalid host address.',
>>>>> +'portredir empty input' => 'Empty input: Please enter a valid
>>>>> host.',
>>>>> +'portredir save to activate' => 'Save to activate changes',
>>>>> +);
>>>>> +
>>>>> +#EOF
>>>>> diff --git a/config/portredir/portredir-backup
>>>>> b/config/portredir/portredir-backup
>>>>> new file mode 100644
>>>>> index 000000000..bd2ada742
>>>>> --- /dev/null
>>>>> +++ b/config/portredir/portredir-backup
>>>>> @@ -0,0 +1 @@
>>>>> +/var/ipfire/portredir
>>>>> diff --git a/config/portredir/portredir.cgi
>>>>> b/config/portredir/portredir.cgi
>>>>> new file mode 100644
>>>>> index 000000000..4913dda3f
>>>>> --- /dev/null
>>>>> +++ b/config/portredir/portredir.cgi
>>>>> @@ -0,0 +1,525 @@
>>>>> +#!/usr/bin/perl
>>>>> +##############################################################
>>>>> #################
>>>>> +#                                                             
>>>>>                 #
>>>>> +# IPFire.org - A linux based
>>>>> firewall                                         #
>>>>> +# Copyright (C) 2021  IPFire Team 
>>>>> <info@ipfire.org>                          #
>>>>> +#                                                             
>>>>>                 #
>>>>> +# This program is free software: you can redistribute it
>>>>> and/or modify        #
>>>>> +# it under the terms of the GNU General Public License as
>>>>> published by        #
>>>>> +# the Free Software Foundation, either version 3 of the
>>>>> License, or           #
>>>>> +# (at your option) any later
>>>>> version.                                         #
>>>>> +#                                                             
>>>>>                 #
>>>>> +# This program is distributed in the hope that it will be
>>>>> useful,             #
>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>> of              #
>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>> the               #
>>>>> +# GNU General Public License for more
>>>>> details.                                #
>>>>> +#                                                             
>>>>>                 #
>>>>> +# You should have received a copy of the GNU General Public
>>>>> License           #
>>>>> +# along with this program.  If not, see <
>>>>> http://www.gnu.org/licenses/>.       #
>>>>> +#                                                             
>>>>>                 #
>>>>> +##############################################################
>>>>> #################
>>>>> +
>>>>> +use strict;
>>>>> +
>>>>> +# enable only the following on debugging purpose
>>>>> +use warnings;
>>>>> +use CGI::Carp 'fatalsToBrowser';
>>>>> +
>>>>> +require '/var/ipfire/general-functions.pl';
>>>>> +require "${General::swroot}/lang.pl";
>>>>> +require "${General::swroot}/header.pl";
>>>>> +
>>>>> +# File declarations
>>>>> +my $settingsfile = "${General::swroot}/portredir/settings";
>>>>> +my $redirectsfile = "${General::swroot}/portredir/redirects";
>>>>> +
>>>>> +# Create empty settingsfiles if they does not exist yet
>>>>> +unless (-e "$settingsfile") { system ("touch $settingsfile");
>>>>> }
>>>>> +unless (-e "$redirectsfile") { system ("touch
>>>>> $redirectsfile"); }
>>>>> +
>>>>> +# load ipfire settings
>>>>> +our %netsettings = ();
>>>>> +our %color = ();
>>>>> +&General::readhash("${General::swroot}/ethernet/settings",
>>>>> \%netsettings);
>>>>> +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include
>>>>> /colors.txt", \%color);
>>>>> +
>>>>> +my %settings=();
>>>>> +my %portredirs=();
>>>>> +my %checked=(); # Checkbox manipulations
>>>>> +my $errormessage='';
>>>>> +my %selected=();
>>>>> +our %redirects=();
>>>>> +
>>>>> +$settings{'ACTION'} = '';
>>>>> +$settings{'REDIR_ENABLE_ADDON'}="off";
>>>>> +$settings{'REDIR_CUSTOM_GREEN'}="off";
>>>>> +$settings{'REDIR_CUSTOM_BLUE'}="off";
>>>>> +$settings{'REDIR_CUSTOM_ORANGE'}="off";
>>>>> +$settings{'REDIR_DNS_GREEN'}="off";
>>>>> +$settings{'REDIR_NTP_GREEN'}="off";
>>>>> +$settings{'REDIR_DNS_BLUE'}="off";
>>>>> +$settings{'REDIR_NTP_BLUE'}="off";
>>>>> +$settings{'REDIR_DNS_ORANGE'}="off";
>>>>> +$settings{'REDIR_NTP_ORANGE'}="off";
>>>>> +
>>>>> +&Header::showhttpheaders();
>>>>> +
>>>>> +# Get GUI values
>>>>> +&Header::getcgihash(\%settings);
>>>>> +
>>>>> +# Save action
>>>>> +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
>>>>> +
>>>>> +       # If custom rules enabled, deactivate default rules on
>>>>> interface
>>>>> +       if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
>>>>> +               $settings{'REDIR_DNS_GREEN'}="off";
>>>>> +               $settings{'REDIR_NTP_GREEN'}="off";
>>>>> +       }
>>>>> +
>>>>> +       if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
>>>>> +               $settings{'REDIR_DNS_BLUE'}="off";
>>>>> +               $settings{'REDIR_NTP_BLUE'}="off";
>>>>> +       }
>>>>> +
>>>>> +       if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
>>>>> +               $settings{'REDIR_DNS_ORANGE'}="off";
>>>>> +               $settings{'REDIR_NTP_ORANGE'}="off";
>>>>> +       }
>>>>> +
>>>>> +       &General::writehash($settingsfile, \%settings);
>>>>> +
>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
>>>>> +               system ('/usr/local/bin/portredirctrl restart
>>>>>> /dev/null 2>&1');
>>>>> +               system ('/usr/local/bin/portredirctrl enable
>>>>>> /dev/null 2>&1');
>>>>> +               &General::log('portredir addon: port
>>>>> redirections enabled');
>>>>> +       }
>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
>>>>> +               system ('/usr/local/bin/portredirctrl disable
>>>>>> /dev/null 2>&1');
>>>>> +               system ('/usr/local/bin/portredirctrl stop
>>>>>> /dev/null 2>&1');
>>>>> +               &General::log('portredir addon: port
>>>>> redirections disabled');
>>>>> +       }
>>>>> +
>>>>> +# Add/edit an entry to the redirectsfile.
>>>>> +
>>>>> +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) ||
>>>>> ($settings{'ACTION'} eq $Lang::tr{'update'})) {
>>>>> +
>>>>> +       # Check if any input has been performed.
>>>>> +       if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
>>>>> +
>>>>> +               # Check if the given input is no valid IP-
>>>>> address, display an error message.
>>>>> +               if
>>>>> (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
>>>>> +                       $errormessage = "$Lang::tr{'portredir
>>>>> invalid address'}";
>>>>> +               }
>>>>> +       } else {
>>>>> +               $errormessage = "$Lang::tr{'portredir empty
>>>>> input'}";
>>>>> +       }
>>>>> +
>>>>> +       # Go further if there was no error.
>>>>> +       if ($errormessage eq '') {
>>>>> +               my %redirects = ();
>>>>> +               my $id;
>>>>> +               my $status;
>>>>> +
>>>>> +               # Assign hash values.
>>>>> +               my $new_entry_interface =
>>>>> $settings{'REDIR_ENTRY_INTERFACE'};
>>>>> +               my $new_entry_protocol =
>>>>> $settings{'REDIR_ENTRY_PROTOCOL'};
>>>>> +               my $new_entry_port =
>>>>> $settings{'REDIR_ENTRY_PORT'};
>>>>> +               my $new_entry_address =
>>>>> $settings{'REDIR_ENTRY_ADDRESS'};
>>>>> +               my $new_entry_remark =
>>>>> $settings{'REDIR_ENTRY_REMARK'};
>>>>> +
>>>>> +               # Read-in redirectsfile.
>>>>> +               &General::readhasharray($redirectsfile,
>>>>> \%redirects);
>>>>> +
>>>>> +               # Check if we should edit an existing entry and
>>>>> got an ID.
>>>>> +               if (($settings{'ACTION'} eq
>>>>> $Lang::tr{'update'}) && ($settings{'ID'})) {
>>>>> +                       # Assin the provided id.
>>>>> +                       $id = $settings{'ID'};
>>>>> +
>>>>> +                       # Undef the given ID.
>>>>> +                       undef($settings{'ID'});
>>>>> +
>>>>> +                       # Grab the configured status of the
>>>>> corresponding entry.
>>>>> +                       $status = $redirects{$id}[4];
>>>>> +               } else {
>>>>> +                       # Each newly added entry automatically
>>>>> should be enabled.
>>>>> +                       $status = "enabled";
>>>>> +
>>>>> +                       # Generate the ID for the new entry.
>>>>> +                       #
>>>>> +                       # Sort the keys by their ID and store
>>>>> them in an array.
>>>>> +                       my @keys = sort { $a <=> $b } keys
>>>>> %redirects;
>>>>> +
>>>>> +                       # Reverse the key array.
>>>>> +                       my @reversed = reverse(@keys);
>>>>> +
>>>>> +                       # Obtain the last used id.
>>>>> +                       my $last_id = @reversed[0];
>>>>> +
>>>>> +                       # Increase the last id by one and use
>>>>> it as id for the new entry.
>>>>> +                       $id = ++$last_id;
>>>>> +               }
>>>>> +
>>>>> +               # Add/Modify the entry to/in the redirects
>>>>> hash.
>>>>> +               $redirects{$id} = ["$new_entry_interface",
>>>>> "$new_entry_protocol", "$new_entry_port",
>>>>> "$new_entry_address","$status", "$new_entry_remark"];
>>>>> +
>>>>> +               # Write the changed redirects hash to the
>>>>> redirects file.
>>>>> +               &General::writehasharray($redirectsfile,
>>>>> \%redirects);
>>>>> +       }
>>>>> +
>>>>> +# Toggle Enabled/Disabled for an existing entry on the
>>>>> redirects list.
>>>>> +
>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable
>>>>> disable'}) {
>>>>> +       my %redirects = ();
>>>>> +
>>>>> +       # Only go further, if an ID has been passed.
>>>>> +       if ($settings{'ID'}) {
>>>>> +               # Assign the given ID.
>>>>> +               my $id = $settings{'ID'};
>>>>> +
>>>>> +               # Undef the given ID.
>>>>> +               undef($settings{'ID'});
>>>>> +
>>>>> +               # Read-in ignoredfile.
>>>>> +               &General::readhasharray($redirectsfile,
>>>>> \%redirects);
>>>>> +
>>>>> +               # Grab the configured status of the
>>>>> corresponding entry.
>>>>> +               my $status = $redirects{$id}[4];
>>>>> +
>>>>> +               # Switch the status.
>>>>> +               if ($status eq "disabled") {
>>>>> +                       $status = "enabled";
>>>>> +               } else {
>>>>> +                       $status = "disabled";
>>>>> +               }
>>>>> +
>>>>> +               # Modify the status of the existing entry.
>>>>> +               $redirects{$id} = ["$redirects{$id}[0]",
>>>>> "$redirects{$id}[1]", "$redirects{$id}[2]",
>>>>> "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
>>>>> +
>>>>> +               # Write the changed ignored hash to the
>>>>> redirects file.
>>>>> +               &General::writehasharray($redirectsfile,
>>>>> \%redirects);
>>>>> +       }
>>>>> +
>>>>> +# Remove entry from redirects list.
>>>>> +
>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
>>>>> +       my %redirects = ();
>>>>> +
>>>>> +       # Read-in redirectsfile.
>>>>> +       &General::readhasharray($redirectsfile, \%redirects);
>>>>> +
>>>>> +       # move data on key up
>>>>> +       foreach my $key (sort keys %redirects) {
>>>>> +               if ($key >= $settings{'ID'}) {
>>>>> +                       my $next = $key + 1;
>>>>> +                       if (exists $redirects{$next}) {
>>>>> +                               foreach my $i (0 ..
>>>>> $#{$redirects{$next}}) { $redirects{$key}[$i] =
>>>>> $redirects{$next}[$i]; }
>>>>> +                       }
>>>>> +               }
>>>>> +       }
>>>>> +
>>>>> +       my $last_key = (sort {$a <=> $b} keys %redirects)[-1];
>>>>> +       delete $redirects{$last_key};
>>>>> +
>>>>> +       # Undef the given ID.
>>>>> +       undef($settings{'ID'});
>>>>> +
>>>>> +       # Write the changed redirects hash to file.
>>>>> +       &General::writehasharray($redirectsfile, \%redirects);
>>>>> +}
>>>>> +
>>>>> +# Load settings from file
>>>>> +&General::readhash($settingsfile, \%settings);
>>>>> +&General::readhasharray($redirectsfile, \%redirects);
>>>>> +
>>>>> +# Call functions to generate whole page.
>>>>> +&Header::openpage($Lang::tr{'portredir port redirections'}, 1,
>>>>> '');
>>>>> +&Header::openbigbox('100%', 'left', '', $errormessage);
>>>>> +
>>>>> +if ($errormessage) {
>>>>> +        &Header::openbox('100%', 'left', $Lang::tr{'warning
>>>>> messages'});
>>>>> +        print "<font color='red'>$errormessage&nbsp;</font>";
>>>>> +        &Header::closebox();
>>>>> +}
>>>>> +
>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
>>>>> +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_ADDON'}
>>>>> } = "checked='checked'";
>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_GREEN'}
>>>>> } = "checked='checked'";
>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BLUE'}}
>>>>> = "checked='checked'";
>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_ORANGE
>>>>> '}} = "checked='checked'";
>>>>> +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
>>>>> +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
>>>>> +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}} =
>>>>> "checked='checked'";
>>>>> +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
>>>>> +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
>>>>> +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}} =
>>>>> "checked='checked'";
>>>>> +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
>>>>> +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
>>>>> +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}} =
>>>>> "checked='checked'";
>>>>> +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
>>>>> +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
>>>>> +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}} =
>>>>> "checked='checked'";
>>>>> +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
>>>>> +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
>>>>> +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE'}} =
>>>>> "checked='checked'";
>>>>> +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
>>>>> +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
>>>>> +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE'}} =
>>>>> "checked='checked'";
>>>>> +
>>>>> +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTRY_INTE
>>>>> RFACE'}} = 'selected';
>>>>> +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY_PROTO
>>>>> COL'}} = 'selected';
>>>>> +
>>>>> +&showMainBox();
>>>>> +&showRedirectsBox();
>>>>> +
>>>>> +&Header::closebigbox();
>>>>> +&Header::closepage();
>>>>> +
>>>>> +# Function to show main settings and options.
>>>>> +sub showMainBox() {
>>>>> +
>>>>> +       &Header::openbox('100%', 'center',
>>>>> "$Lang::tr{'settings'}");
>>>>> +       print "<form method='post'
>>>>> action='$ENV{'SCRIPT_NAME'}'>";
>>>>> +
>>>>> +print <<END;
>>>>> +<table width='80%' cellspacing='0' border='0'>
>>>>> +       <tr><td colspan='2' class='base'
>>>>> bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir common
>>>>> settings'}</b></td></tr
>>>>> +       <tr>
>>>>> +               <td width='25%'
>>>>> class='base'>$Lang::tr{'portredir enable addon'}:</td>
>>>>> +               <td><input type='checkbox'
>>>>> name='REDIR_ENABLE_ADDON' $checked{'REDIR_ENABLE_ADDON'}{'on'}
>>>>> /></td>
>>>>> +       </tr>
>>>>> +       <tr><td colspan='2'></td></tr>
>>>>> +       <tr><td colspan='2'>&nbsp;</td></tr>
>>>>> +END
>>>>> +
>>>>> +       # create html table with header line 1
>>>>> +       print "<table width='80%' cellspacing='0'
>>>>> border='0'><tr>";
>>>>> +       print "<th class='base' width='40%'
>>>>> align='left'><b>$Lang::tr{'portredir fw for interface'}</th>";
>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<th
>>>>> class='base' width='10%'><b><font
>>>>> color=green>$Lang::tr{'green'}</font></th>";
>>>>> +               } else {                 print "<th
>>>>> class='base' width='10%'></font></th>"; }
>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<th
>>>>> class='base' width='10%'><b><font
>>>>> color=blue>$Lang::tr{'blue'}</font></th>";
>>>>> +               } else {                 print "<th
>>>>> class='base' width='10%'></font></th>"; }
>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<th
>>>>> class='base' width='10%'><b><font
>>>>> color=orange>$Lang::tr{'orange'}</font></th>";
>>>>> +               } else {                 print "<th
>>>>> class='base' width='10%'></font></th>"; }
>>>>> +
>>>>> +       # the empty right row
>>>>> +       print "<th class='base'
>>>>> width='30%'><td></td></th></tr>";
>>>>> +
>>>>> +       # line 2
>>>>> +       print "<tr><td>$Lang::tr{'portredir force local
>>>>> dns'}</td>";
>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_DNS_GREEN'
>>>>> $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print
>>>>> "<td></td>";}
>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_DNS_BLUE' $checked{'REDIR_DNS_BLUE'}{'on'}></td>";}
>>>>> else { print "<td></td>";}
>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_DNS_ORANGE'
>>>>> $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print
>>>>> "<td></td>";}
>>>>> +
>>>>> +       # line 3
>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir force local
>>>>> ntp'}</td>";
>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_NTP_GREEN'
>>>>> $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print
>>>>> "<td></td>";}
>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_NTP_BLUE' $checked{'REDIR_NTP_BLUE'}{'on'}></td>";}
>>>>> else { print "<td></td>";}
>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_NTP_ORANGE'
>>>>> $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print
>>>>> "<td></td>";}
>>>>> +
>>>>> +       # line 4
>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir enable user
>>>>> redirections'}</td>";
>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_CUSTOM_GREEN'
>>>>> $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else { print
>>>>> "<td></td>";}
>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_CUSTOM_BLUE'
>>>>> $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print
>>>>> "<td></td>";}
>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>> class='base' align='center'><input type='checkbox'
>>>>> name='REDIR_CUSTOM_ORANGE'
>>>>> $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else { print
>>>>> "<td></td>";}
>>>>> +
>>>>> +       print <<END;
>>>>> +       </tr></table>
>>>>> +       <table width='80%' cellspacing='0' border='0'>
>>>>> +               <tr><td colspan='2'>&nbsp;</td></tr>
>>>>> +               <tr><td align='left'><b>$Lang::tr{'portredir
>>>>> save to activate'}</b></td><td width='5%' align='center'><input
>>>>> type='submit' name='ACTION' value='  $Lang::tr{'save'} 
>>>>> '></td></tr>
>>>>> +       </table></form>
>>>>> +END
>>>>> +
>>>>> +&Header::closebox();
>>>>> +}
>>>>> +
>>>>> +# Function to show elements of the redirects file and allow to
>>>>> add or remove single members of it.
>>>>> +sub showRedirectsBox() {
>>>>> +        &Header::openbox('100%', 'center',
>>>>> "$Lang::tr{'portredir custom redirections'}");
>>>>> +
>>>>> +       print <<END;
>>>>> +               <table width='80%' cellspacing='1' border='0'>
>>>>> +                       <tr>
>>>>> +                               <td class='base'
>>>>> bgcolor='$color{'color20'}'
>>>>> align='center'><b>$Lang::tr{'interface'}</b></td>
>>>>> +                               <td class='base'
>>>>> bgcolor='$color{'color20'}'
>>>>> align='center'><b>$Lang::tr{'protocol'}</b></td>
>>>>> +                               <td class='base'
>>>>> bgcolor='$color{'color20'}'
>>>>> align='center'><b>$Lang::tr{'port'}</b></td>
>>>>> +                               <td class='base'
>>>>> bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'ip
>>>>> address'}</b></td>
>>>>> +                               <td class='base'
>>>>> bgcolor='$color{'color20'}'
>>>>> align='center'><b>$Lang::tr{'remark'}</b></td>
>>>>> +                               <td class='base' colspan='3'
>>>>> bgcolor='$color{'color20'}'></td>
>>>>> +                       </tr>
>>>>> +END
>>>>> +                       # Check if some rules have been added
>>>>> to be redirects.
>>>>> +                       if (keys (%redirects)) {
>>>>> +                               my $col = "";
>>>>> +
>>>>> +                               # List all entries of the hash.
>>>>> +                               foreach my $key (sort keys
>>>>> %redirects){
>>>>> +
>>>>> +                                       # Assign data array
>>>>> positions to some nice variable names.
>>>>> +                                       my $interface =
>>>>> $redirects{$key}[0];
>>>>> +                                       my $protocol =
>>>>> $redirects{$key}[1];
>>>>> +                                       my $port  =
>>>>> $redirects{$key}[2];
>>>>> +                                       my $address =
>>>>> $redirects{$key}[3];
>>>>> +                                       my $status  =
>>>>> $redirects{$key}[4];
>>>>> +                                       my $remark  =
>>>>> $redirects{$key}[5];
>>>>> +
>>>>> +                                       # Check if the key (id)
>>>>> number is even or not.
>>>>> +                                       if ($settings{'ID'} eq
>>>>> $key) {
>>>>> +                                               $col="bgcolor='
>>>>> ${Header::colouryellow}'";
>>>>> +                                       } elsif ($key % 2) {
>>>>> +                                               $col="bgcolor='
>>>>> $color{'color22'}'";
>>>>> +                                       } else {
>>>>> +                                               $col="bgcolor='
>>>>> $color{'color20'}'";
>>>>> +                                       }
>>>>> +
>>>>> +                                       # Choose icon for the
>>>>> checkbox.
>>>>> +                                       my $gif;
>>>>> +                                       my $gdesc;
>>>>> +
>>>>> +                                       # Check if the status
>>>>> is enabled and select the correct image and description.
>>>>> +                                       if ($status eq
>>>>> 'enabled' ) {
>>>>> +                                               $gif =
>>>>> 'on.gif';
>>>>> +                                               $gdesc =
>>>>> $Lang::tr{'click to disable'};
>>>>> +                                       } else {
>>>>> +                                               $gif =
>>>>> 'off.gif';
>>>>> +                                               $gdesc =
>>>>> $Lang::tr{'click to enable'};
>>>>> +                                       }
>>>>> +
>>>>> +                                       print <<END;
>>>>> +                                       <tr>
>>>>> +                                               <td width='15%'
>>>>> class='base' align='center' $col><b><font
>>>>> color=$interface>$Lang::tr{$interface}</font></b></td>
>>>>> +                                               <td width='10%'
>>>>> class='base' align='center' $col>$protocol</td>
>>>>> +                                               <td width='10%'
>>>>> class='base' align='center' $col>$port</td>
>>>>> +                                               <td width='15%'
>>>>> class='base' align='center' $col>&nbsp;$address</td>
>>>>> +                                               <td width='40%'
>>>>> class='base' align='center' $col>&nbsp;$remark</td>
>>>>> +                                               <td
>>>>> align='center' $col>
>>>>> +                                                       <form
>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>> +                                                              
>>>>>  <input type='hidden' name='ACTION' value='$Lang::tr{'toggle
>>>>> enable disable'}' />
>>>>> +                                                              
>>>>>  <input type='image' name='$Lang::tr{'toggle enable disable'}'
>>>>> src='/images/$gif' alt='$gdesc' title='$gdesc' />
>>>>> +                                                              
>>>>>  <input type='hidden' name='ID' value='$key' />
>>>>> +                                                       </form>
>>>>> +                                               </td>
>>>>> +                                               <td
>>>>> align='center' $col>
>>>>> +                                                       <form
>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>> +                                                              
>>>>>  <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}'
>>>>> />
>>>>> +                                                              
>>>>>  <input type='image' name='$Lang::tr{'edit'}'
>>>>> src='/images/edit.gif' alt='$Lang::tr{'edit'}'
>>>>> title='$Lang::tr{'edit'}' />
>>>>> +                                                              
>>>>>  <input type='hidden' name='ID' value='$key' />
>>>>> +                                                       </form>
>>>>> +                                               </td>
>>>>> +                                               <td
>>>>> align='center' $col>
>>>>> +                                                       <form
>>>>> method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
>>>>> +                                                              
>>>>>  <input type='image' name='$Lang::tr{'remove'}'
>>>>> src='/images/delete.gif' title='$Lang::tr{'remove'}'
>>>>> alt='$Lang::tr{'remove'}'>
>>>>> +                                                              
>>>>>  <input type='hidden' name='ID' value='$key'>
>>>>> +                                                              
>>>>>  <input type='hidden' name='ACTION'
>>>>> value='$Lang::tr{'remove'}'>
>>>>> +                                                       </form>
>>>>> +                                               </td>
>>>>> +                                       </tr>
>>>>> +END
>>>>> +                               }
>>>>> +                       } else {
>>>>> +                               # Print notice that currently
>>>>> no ports are redirected.
>>>>> +                               print "<tr>\n";
>>>>> +                               print "<td class='base'
>>>>> colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
>>>>> +                               print "</tr>\n";
>>>>> +                       }
>>>>> +
>>>>> +               print "</table>\n";
>>>>> +
>>>>> +       # Section to add new elements or edit existing ones.
>>>>> +       print <<END;
>>>>> +       <br>
>>>>> +       <hr>
>>>>> +       <br>
>>>>> +       <div align='center'>
>>>>> +               <table width='100%' cellspacing='0' border='0'>
>>>>> +END
>>>>> +
>>>>> +       # Assign correct headline and button text.
>>>>> +       my $buttontext;
>>>>> +       my $entry_interface;
>>>>> +       my $entry_protocol;
>>>>> +       my $entry_port;
>>>>> +       my $entry_address;
>>>>> +       my $entry_remark;
>>>>> +
>>>>> +       # Check if an ID (key) has been given, in this case an
>>>>> existing entry should be edited.
>>>>> +       if ($settings{'ID'} ne '') {
>>>>> +               $buttontext = $Lang::tr{'update'};
>>>>> +               print "<tr><td class='boldbase'
>>>>> colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
>>>>> +
>>>>> +               # Grab address and remark for the given key.
>>>>> +               $entry_interface =
>>>>> $redirects{$settings{'ID'}}[0];
>>>>> +               $entry_protocol =
>>>>> $redirects{$settings{'ID'}}[1];
>>>>> +               $entry_port = $redirects{$settings{'ID'}}[2];
>>>>> +               $entry_address =
>>>>> $redirects{$settings{'ID'}}[3];
>>>>> +               $entry_remark = $redirects{$settings{'ID'}}[5];
>>>>> +
>>>>> +       } else {
>>>>> +               $buttontext = $Lang::tr{'add'};
>>>>> +               print "<tr><td class='boldbase'
>>>>> colspan='11'><b>$Lang::tr{'dnsforward add a new
>>>>> entry'}</b></td></tr>\n";
>>>>> +               print "<tr><td>&nbsp</td></tr>\n";
>>>>> +       }
>>>>> +
>>>>> +       print <<END;
>>>>> +                       <tr>
>>>>> +                               <td class='base' width='1%' 
>>>>> bgcolor='$color{'color22'}'></td>
>>>>> +                               <td class='base' width='15%'
>>>>> bgcolor='$color{'color22'}'
>>>>> align='left'><b>$Lang::tr{'interface'}</b></td>
>>>>> +                               <td class='base' width='10%'
>>>>> bgcolor='$color{'color22'}'
>>>>> align='left'><b>$Lang::tr{'protocol'}</b></td>
>>>>> +                               <td class='base' width='10%'
>>>>> bgcolor='$color{'color22'}'
>>>>> align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
>>>>> +                               <td class='base' width='13%'
>>>>> bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'ip
>>>>> address'}</b></td>
>>>>> +                               <td class='base' width='30%'
>>>>> bgcolor='$color{'color22'}'
>>>>> align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
>>>>> +                               <td class='base' width='15%'
>>>>> bgcolor='$color{'color22'}'></td>
>>>>> +                               <td class='base' width='1%' 
>>>>> bgcolor='$color{'color22'}'></td>
>>>>> +                       </tr>
>>>>> +
>>>>> +                       <form method='post'
>>>>> action='$ENV{'SCRIPT_NAME'}'>
>>>>> +                       <input type='hidden' name='ID'
>>>>> value='$settings{'ID'}'>
>>>>> +                       <tr>
>>>>> +                               <td class='base'></td>
>>>>> +                               <td><select style='width:90px;'
>>>>> id='interface' name='REDIR_ENTRY_INTERFACE'>
>>>>> +END
>>>>> +                               if ($netsettings{'GREEN_DEV'})
>>>>> {
>>>>> +                                       if ($entry_interface eq
>>>>> "green") {
>>>>> +                                               print "<option
>>>>> value='green' selected='selected'
>>>>> {'green'}>$Lang::tr{'green'}</option>";
>>>>> +                                       } else { print "<option
>>>>> value='green' {'green'}>$Lang::tr{'green'}</option>";}
>>>>> +                               }
>>>>> +                               if ($netsettings{'BLUE_DEV'}) {
>>>>> +                                       if ($entry_interface eq
>>>>> "blue") { 
>>>>> +                                               print "<option
>>>>> value='blue' selected='selected'
>>>>> {'blue'}>$Lang::tr{'blue'}</option>";
>>>>> +                                       } else { print "<option
>>>>> value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
>>>>> +                               }
>>>>> +                               if ($netsettings{'ORANGE_DEV'})
>>>>> {
>>>>> +                                       if ($entry_interface eq
>>>>> "orange") { 
>>>>> +                                               print "<option
>>>>> value='orange' selected='selected'
>>>>> {'orange'}>$Lang::tr{'orange'}</option>";
>>>>> +                                       } else { print "<option
>>>>> value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
>>>>> +                               }
>>>>> +
>>>>> +                       print "</select><td><select
>>>>> style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
>>>>> +                       if ((!$entry_protocol) ||
>>>>> ($entry_protocol eq "tcp")) {
>>>>> +                               print "<option
>>>>> selected='selected' id='protocol' value='tcp'
>>>>> {'tcp'}>tcp</option>";
>>>>> +                               print "<option value='udp'
>>>>> {'udp'}>udp</option>";
>>>>> +                       } elsif ($entry_protocol eq "udp") {
>>>>> +                               print "<option value='tcp'
>>>>> {'tcp'}>tcp</option>";
>>>>> +                               print "<option
>>>>> selected='selected' value='udp' {'udp'}>udp</option>";
>>>>> +                       }
>>>>> +       print <<END;
>>>>> +                               </select></td>
>>>>> +                               <td><input type='text'
>>>>> name='REDIR_ENTRY_PORT'    value='$entry_port'   
>>>>> size='4'></td>
>>>>> +                               <td><input type='text'
>>>>> name='REDIR_ENTRY_ADDRESS' value='$entry_address'
>>>>> size='14'></td>
>>>>> +                               <td><input type='text'
>>>>> name='REDIR_ENTRY_REMARK'  value='$entry_remark' 
>>>>> size='35'></td>
>>>>> +                               <td width='10%'
>>>>> align='center'><input type='submit' name='ACTION' value=' 
>>>>> $buttontext  '></td>
>>>>> +                               <td class='base'></td>
>>>>> +                       </tr>
>>>>> +                       </form>
>>>>> +               </table>
>>>>> +       </div>
>>>>> +END
>>>>> +       &Header::closebox();
>>>>> +}
>>>>> diff --git a/config/rootfiles/common/misc-progs
>>>>> b/config/rootfiles/common/misc-progs
>>>>> index d6594b3f8..fbad2af8b 100644
>>>>> --- a/config/rootfiles/common/misc-progs
>>>>> +++ b/config/rootfiles/common/misc-progs
>>>>> @@ -17,6 +17,7 @@ usr/local/bin/logwatch
>>>>> #usr/local/bin/mpfirectrl
>>>>> usr/local/bin/openvpnctrl
>>>>> usr/local/bin/pakfire
>>>>> +#usr/local/bin/portredirctrl
>>>>> usr/local/bin/qosctrl
>>>>> usr/local/bin/rebuildhosts
>>>>> usr/local/bin/rebuildroutes
>>>>> diff --git a/config/rootfiles/packages/portredir
>>>>> b/config/rootfiles/packages/portredir
>>>>> new file mode 100644
>>>>> index 000000000..4b4ba8366
>>>>> --- /dev/null
>>>>> +++ b/config/rootfiles/packages/portredir
>>>>> @@ -0,0 +1,11 @@
>>>>> +etc/rc.d/init.d/portredir
>>>>> +etc/rc.d/rc0.d/K77portredir
>>>>> +etc/rc.d/rc3.d/S23portredir
>>>>> +etc/rc.d/rc6.d/K77portredir
>>>>> +srv/web/ipfire/cgi-bin/portredir.cgi
>>>>> +usr/local/bin/portredirctrl
>>>>> +var/ipfire/addon-lang/portredir.de.pl
>>>>> +var/ipfire/addon-lang/portredir.en.pl
>>>>> +var/ipfire/backup/addons/includes/portredir
>>>>> +var/ipfire/menu.d/EX-portredir.menu
>>>>> +var/ipfire/portredir
>>>>> diff --git a/lfs/portredir b/lfs/portredir
>>>>> new file mode 100644
>>>>> index 000000000..a4911f71f
>>>>> --- /dev/null
>>>>> +++ b/lfs/portredir
>>>>> @@ -0,0 +1,85 @@
>>>>> +##############################################################
>>>>> #################
>>>>> +#                                                             
>>>>>                 #
>>>>> +# IPFire.org - A linux based
>>>>> firewall                                         #
>>>>> +# Copyright (C) 2007-2021  IPFire Team 
>>>>> <info@ipfire.org>                     #
>>>>> +#                                                             
>>>>>                 #
>>>>> +# This program is free software: you can redistribute it
>>>>> and/or modify        #
>>>>> +# it under the terms of the GNU General Public License as
>>>>> published by        #
>>>>> +# the Free Software Foundation, either version 3 of the
>>>>> License, or           #
>>>>> +# (at your option) any later
>>>>> version.                                         #
>>>>> +#                                                             
>>>>>                 #
>>>>> +# This program is distributed in the hope that it will be
>>>>> useful,             #
>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>> of              #
>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>> the               #
>>>>> +# GNU General Public License for more
>>>>> details.                                #
>>>>> +#                                                             
>>>>>                 #
>>>>> +# You should have received a copy of the GNU General Public
>>>>> License           #
>>>>> +# along with this program.  If not, see <
>>>>> http://www.gnu.org/licenses/>.       #
>>>>> +#                                                             
>>>>>                 #
>>>>> +##############################################################
>>>>> #################
>>>>> +
>>>>> +##############################################################
>>>>> #################
>>>>> +# Definitions
>>>>> +##############################################################
>>>>> #################
>>>>> +
>>>>> +include Config
>>>>> +
>>>>> +VER        = 1.0
>>>>> +
>>>>> +THISAPP    = portredir-$(VER)
>>>>> +DIR_APP    = $(DIR_SRC)/$(THISAPP)
>>>>> +TARGET     = $(DIR_INFO)/$(THISAPP)
>>>>> +PROG       = portredir
>>>>> +PAK_VER    = 1
>>>>> +
>>>>> +##############################################################
>>>>> #################
>>>>> +# Top-level Rules
>>>>> +##############################################################
>>>>> #################
>>>>> +
>>>>> +install : $(TARGET)
>>>>> +
>>>>> +check :
>>>>> +
>>>>> +download :
>>>>> +
>>>>> +md5 :
>>>>> +
>>>>> +dist: 
>>>>> +       @$(PAK)
>>>>> +
>>>>> +##############################################################
>>>>> #################
>>>>> +# Installation Details
>>>>> +##############################################################
>>>>> #################
>>>>> +
>>>>> +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
>>>>> +       @$(PREBUILD)
>>>>> +       @rm -rf $(DIR_APP) && cd $(DIR_SRC)
>>>>> +
>>>>> +       #install cgi 
>>>>> +       install -v -m 755 $(DIR_CONF)/portredir/portredir.cgi
>>>>> /srv/web/ipfire/cgi-bin/
>>>>> +
>>>>> +       #create configuration dir 
>>>>> +       -mkdir -pv /var/ipfire/portredir/
>>>>> +       chown -R nobody:nobody /var/ipfire/portredir/
>>>>> +
>>>>> +       # Install include file for backup
>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/portredir-
>>>>> backup /var/ipfire/backup/addons/includes/portredir
>>>>> +
>>>>> +       # Install menu file
>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/EX-
>>>>> portredir.menu /var/ipfire/menu.d/
>>>>> +       chown nobody:nobody /var/ipfire/menu.d/EX-
>>>>> portredir.menu
>>>>> +
>>>>> +       # Install addon-specific language-files
>>>>> +       install -v -m 644
>>>>> $(DIR_CONF)/portredir/lang/portredir.*.pl /var/ipfire/addon-
>>>>> lang/
>>>>> +
>>>>> +       #install initscripts
>>>>> +       $(call INSTALL_INITSCRIPT,portredir)
>>>>> +
>>>>> +       # Create symlinks for runlevel interaction.
>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>> /etc/rc.d/rc3.d/S23portredir
>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>> /etc/rc.d/rc0.d/K77portredir
>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>> /etc/rc.d/rc6.d/K77portredir
>>>>> +
>>>>> +       @rm -rf $(DIR_APP)
>>>>> +       @$(POSTBUILD)
>>>>> +
>>>>> diff --git a/make.sh b/make.sh
>>>>> index fc03ebcd5..ab9fe881a 100755
>>>>> --- a/make.sh
>>>>> +++ b/make.sh
>>>>> @@ -1623,6 +1623,7 @@ buildipfire() {
>>>>>  lfsmake2 socat
>>>>>  lfsmake2 libcdada
>>>>>  lfsmake2 pmacct
>>>>> +  lfsmake2 portredir
>>>>> }
>>>>> 
>>>>> buildinstaller() {
>>>>> diff --git a/src/initscripts/packages/portredir
>>>>> b/src/initscripts/packages/portredir
>>>>> new file mode 100644
>>>>> index 000000000..cc57fb9cc
>>>>> --- /dev/null
>>>>> +++ b/src/initscripts/packages/portredir
>>>>> @@ -0,0 +1,191 @@
>>>>> +#!/bin/sh
>>>>> +##############################################################
>>>>> ##########
>>>>> +# Begin $rc_base/init.d/portredir
>>>>> +#
>>>>> +# Description : portredir init script for DNS/NTP and custom 
>>>>> +#              port redirection rules
>>>>> +#
>>>>> +##############################################################
>>>>> ##########
>>>>> +
>>>>> +. /etc/sysconfig/rc
>>>>> +. ${rc_functions}
>>>>> +
>>>>> +IPT="/sbin/iptables";
>>>>> +parent_chain="PREROUTING";
>>>>> +chain="PORT_REDIRECT";
>>>>> +
>>>>> +confdir="/var/ipfire/portredir";
>>>>> +settingsfile="${confdir}/settings";
>>>>> +redirectsfile="${confdir}/redirects";
>>>>> +SYSLOG="NO";
>>>>> +VERBOSE="NO";
>>>>> +
>>>>> +eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings);
>>>>> +eval $(/usr/local/bin/readhash ${settingsfile});
>>>>> +
>>>>> +logtext() {
>>>>> +       if [ "${SYSLOG}" = "YES" ]; then logger -t "portredir"
>>>>> ${1}; fi;
>>>>> +       if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
>>>>> +
>>>>> +create_chain() {
>>>>> +
>>>>> +       local line=$(${IPT} -t nat -L ${parent_chain} --line-
>>>>> numbers |grep "SQUID" |awk '{printf($1)}');
>>>>> +
>>>>> +       if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z
>>>>> "${REDIR_ENABLE_ADDON}" ]]; then
>>>>> +               logtext "addon not enabled in web
>>>>> interface...";
>>>>> +               echo "Portredir addon not enabled in web
>>>>> interface...";
>>>>> +               exit 0;
>>>>> +       fi;
>>>>> +
>>>>> +       if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>> ${chain})" ]; then
>>>>> +               ${IPT} -t nat -N ${chain};
>>>>> +
>>>>> +               if [ ! -z "${line}" ]; then
>>>>> +                       logtext "create chain ${chain} and link
>>>>> in ${parent_chain} at position ${line}...";
>>>>> +                       ${IPT} -t nat -I ${parent_chain}
>>>>> ${line} -j ${chain};
>>>>> +               else
>>>>> +                       logtext "create chain ${chain} and link
>>>>> in ${parent_chain} at last position...";
>>>>> +                       ${IPT} -t nat -A ${parent_chain} -j
>>>>> ${chain};
>>>>> +               fi
>>>>> +       else
>>>>> +               return 1;
>>>>> +       fi;
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +remove_chain() {
>>>>> +       if [ ! -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>> ${chain})" ]; then
>>>>> +               logtext "remove chain ${chain} and link in
>>>>> ${parent_chain} from system...";
>>>>> +               ${IPT} -t nat -D "${parent_chain}" -j ${chain};
>>>>> +               ${IPT} -t nat -F ${chain};
>>>>> +               ${IPT} -t nat -X ${chain};
>>>>> +       else
>>>>> +               return 1;
>>>>> +       fi;
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +activate_custom_redirections() {
>>>>> +       
>>>>> +       local array=();
>>>>> +       local redirects=();
>>>>> +       local i;
>>>>> +       index=();
>>>>> +       iface=();
>>>>> +       protocol=();
>>>>> +       port=();
>>>>> +       targetip=();
>>>>> +       enabled=();
>>>>> +
>>>>> +       IFS=$'\n' read -d '' -ra redirects < ${redirectsfile};
>>>>> +
>>>>> +       for i in "${!redirects[@]}"
>>>>> +       do
>>>>> +               IFS=$',' read -ra array <<< ${redirects[i]};
>>>>> +               index[i]=${array[0]};
>>>>> +               iface[i]=${array[1]};
>>>>> +               protocol[i]=${array[2]};
>>>>> +               port[i]=${array[3]};
>>>>> +               targetip[i]=${array[4]};
>>>>> +               enabled[i]=${array[5]};
>>>>> +       done
>>>>> +
>>>>> +       for i in "${!index[@]}"
>>>>> +       do
>>>>> +               if [[ ! -z "${GREEN_DEV}" &&  "${iface[i]}" =
>>>>> "green" && "${enabled[i]}" = "enabled" ]]; then
>>>>> +
>>>>> +                       logtext "add redirect in ${chain} on
>>>>> ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>> ${port[i]} ";
>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>> ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>> ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>> ${port[i]} -j REDIRECT;
>>>>> +               fi
>>>>> +               if [[ ! -z "${BLUE_DEV}" &&  "${iface[i]}" =
>>>>> "blue" && "${enabled[i]}" = "enabled" ]]; then
>>>>> +                       logtext "add redirect in ${chain} on
>>>>> ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>> ${port[i]} ";
>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>> ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>> ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>> ${port[i]} -j REDIRECT;
>>>>> +               fi
>>>>> +               if [[ ! -z "${ORANGE_DEV}" &&  "${iface[i]}" =
>>>>> "orange" && "${enabled[i]}" = "enabled" ]]; then
>>>>> +                       logtext "add redirect in ${chain} on
>>>>> ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>> ${port[i]} ";
>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>> ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>> ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>> ${port[i]} -j REDIRECT;
>>>>> +               fi
>>>>> +       done
>>>>> +       unset array redirects i index iface protocol port
>>>>> targetip enabled;
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +activate_redirections() {
>>>>> +
>>>>> +       if ! create_chain; then return 1; fi;
>>>>> +       
>>>>> +       # Force DNS REDIRECTs on GREEN (udp, tcp, 53)
>>>>> +       if [[ "${REDIR_DNS_GREEN}" == "on" && 
>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>> udp -m udp --dport domain -j REDIRECT;
>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>> ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>> tcp -m tcp --dport domain -j REDIRECT;
>>>>> +       fi
>>>>> +
>>>>> +       # Force DNS REDIRECTs on BLUE (udp, tcp, 53)
>>>>> +       if [[ "${REDIR_DNS_BLUE}" == "on" && 
>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>> -m udp --dport domain -j REDIRECT
>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>> ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p tcp
>>>>> -m tcp --dport domain -j REDIRECT
>>>>> +       fi
>>>>> +
>>>>> +       # Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
>>>>> +       if [[ "${REDIR_DNS_ORANGE}" == "on" && 
>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>> udp -m udp --dport domain -j REDIRECT
>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>> ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>> tcp -m tcp --dport domain -j REDIRECT
>>>>> +       fi
>>>>> +
>>>>> +       # Force NTP REDIRECTs on GREEN (udp, 123)
>>>>> +       if [[ "${REDIR_NTP_GREEN}" == "on" && 
>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>> +       fi
>>>>> +
>>>>> +       # Force NTP REDIRECTs on BLUE (udp, 123)
>>>>> +       if [[ "${REDIR_NTP_BLUE}" == "on" && 
>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>> -m udp --dport ntp -j REDIRECT
>>>>> +       fi
>>>>> +
>>>>> +       # Force NTP REDIRECTs on ORANGE (udp, 123)
>>>>> +       if [[ "${REDIR_NTP_ORANGE}" == "on" && 
>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>> +       fi
>>>>> +
>>>>> +       if ! activate_custom_redirections; then return 1; fi;
>>>>> +
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +case "${1}" in
>>>>> +       start)
>>>>> +               boot_mesg "Loading port redirections..."
>>>>> +               activate_redirections;
>>>>> +               evaluate_retval;
>>>>> +               ;;
>>>>> +
>>>>> +       stop)   
>>>>> +               boot_mesg "Removing port redirections..."
>>>>> +               remove_chain;
>>>>> +               evaluate_retval;
>>>>> +               ;;
>>>>> +
>>>>> +       restart)
>>>>> +               ${0} stop
>>>>> +               ${0} start
>>>>> +               ;;
>>>>> +
>>>>> +       *)
>>>>> +               echo "Usage: ${0} {start|stop|restart}"
>>>>> +               exit 1
>>>>> +               ;;
>>>>> +esac
>>>>> +
>>>>> +# End $rc_base/init.d/portredir
>>>>> diff --git a/src/misc-progs/Makefile b/src/misc-progs/Makefile
>>>>> index 7c3ef7529..850f8fdcc 100644
>>>>> --- a/src/misc-progs/Makefile
>>>>> +++ b/src/misc-progs/Makefile
>>>>> @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
>>>>>         wirelessctrl getipstat qosctrl \
>>>>>         redctrl syslogdctrl extrahdctrl sambactrl \
>>>>>         smartctrl clamavctrl addonctrl pakfire mpfirectrl
>>>>> wlanapctrl \
>>>>> -       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>> rebuildroutes \
>>>>> +       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>> rebuildroutes portredirctrl \
>>>>>         getconntracktable wirelessclient torctrl ddnsctrl
>>>>> unboundctrl \
>>>>>         captivectrl
>>>>> 
>>>>> diff --git a/src/misc-progs/portredirctrl.c b/src/misc-
>>>>> progs/portredirctrl.c
>>>>> new file mode 100644
>>>>> index 000000000..7897d711c
>>>>> --- /dev/null
>>>>> +++ b/src/misc-progs/portredirctrl.c
>>>>> @@ -0,0 +1,47 @@
>>>>> +/* This file is part of the IPFire Firewall.
>>>>> + *
>>>>> + * This program is distributed under the terms of the GNU
>>>>> General Public
>>>>> + * Licence.  See the file COPYING for details.
>>>>> + *
>>>>> + */
>>>>> +
>>>>> +#include <stdlib.h>
>>>>> +#include <stdio.h>
>>>>> +#include <string.h>
>>>>> +#include <unistd.h>
>>>>> +#include <sys/types.h>
>>>>> +#include <fcntl.h>
>>>>> +#include "setuid.h"
>>>>> +
>>>>> +int main(int argc, char *argv[]) {
>>>>> +       if (!(initsetuid()))
>>>>> +               exit(1);
>>>>> +
>>>>> +       // Check what command is asked
>>>>> +        if (argc < 2) {
>>>>> +                fprintf(stderr, "\nNo argument
>>>>> given.\n\nportredirctrl
>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>> +                exit(1);
>>>>> +        }
>>>>> +
>>>>> +        if (strcmp(argv[1], "start") == 0) {
>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>> start");
>>>>> +        } else if (strcmp(argv[1], "stop") == 0) {
>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>> stop");
>>>>> +        } else if (strcmp(argv[1], "restart") == 0) {
>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>> restart");
>>>>> +       } else if (strcmp(argv[1], "enable") == 0) {
>>>>> +               safe_system("touch
>>>>> /var/ipfire/portredir/enable");
>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>> /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>> /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>> /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
>>>>> +       } else if (strcmp(argv[1], "disable") == 0) {
>>>>> +               safe_system("/etc/rc.d/init.d/portredir stop");
>>>>> +               safe_system("unlink
>>>>> /var/ipfire/portredir/enable");
>>>>> +               safe_system("rm -rf /etc/rc.d/rc*.d/*portredir
>>>>>> /dev/null 2>&1");
>>>>> +        } else {
>>>>> +                fprintf(stderr, "\nBad argument
>>>>> given.\n\nportredirctrl
>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>> +                exit(1);
>>>>> +        }
>>>>> +
>>>>> +       return 0;
>>>>> +}
>>>>> diff --git a/src/paks/portredir/install.sh
>>>>> b/src/paks/portredir/install.sh
>>>>> new file mode 100644
>>>>> index 000000000..9f69aeae2
>>>>> --- /dev/null
>>>>> +++ b/src/paks/portredir/install.sh
>>>>> @@ -0,0 +1,32 @@
>>>>> +#!/bin/bash
>>>>> +##############################################################
>>>>> ##############
>>>>> +#                                                             
>>>>>              #
>>>>> +# This file is part of the IPFire
>>>>> Firewall.                                #
>>>>> +#                                                             
>>>>>              #
>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>> modify           #
>>>>> +# it under the terms of the GNU General Public License as
>>>>> published by     #
>>>>> +# the Free Software Foundation; either version 2 of the
>>>>> License, or        #
>>>>> +# (at your option) any later
>>>>> version.                                      #
>>>>> +#                                                             
>>>>>              #
>>>>> +# IPFire is distributed in the hope that it will be
>>>>> useful,                #
>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>> of           #
>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>> the            #
>>>>> +# GNU General Public License for more
>>>>> details.                             #
>>>>> +#                                                             
>>>>>              #
>>>>> +# You should have received a copy of the GNU General Public
>>>>> License        #
>>>>> +# along with IPFire; if not, write to the Free
>>>>> Software                    #
>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>> 02111-1307 USA #
>>>>> +#                                                             
>>>>>              #
>>>>> +# Copyright (C) 2021 IPFire-Team
>>>>> <info@ipfire.org>.                        #
>>>>> +#                                                             
>>>>>              #
>>>>> +##############################################################
>>>>> ##############
>>>>> +#
>>>>> +. /opt/pakfire/lib/functions.sh
>>>>> +extract_files
>>>>> +restore_backup ${NAME}
>>>>> +
>>>>> +/usr/local/bin/update-lang-cache
>>>>> +
>>>>> +chown root:nobody /usr/local/bin/portredirctrl
>>>>> +chmod 4750 /usr/local/bin/portredirctrl
>>>>> +chmod u+s /usr/local/bin/portredirctrl
>>>>> diff --git a/src/paks/portredir/uninstall.sh
>>>>> b/src/paks/portredir/uninstall.sh
>>>>> new file mode 100644
>>>>> index 000000000..df9270125
>>>>> --- /dev/null
>>>>> +++ b/src/paks/portredir/uninstall.sh
>>>>> @@ -0,0 +1,28 @@
>>>>> +#!/bin/bash
>>>>> +##############################################################
>>>>> ##############
>>>>> +#                                                             
>>>>>              #
>>>>> +# This file is part of the IPFire
>>>>> Firewall.                                #
>>>>> +#                                                             
>>>>>              #
>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>> modify           #
>>>>> +# it under the terms of the GNU General Public License as
>>>>> published by     #
>>>>> +# the Free Software Foundation; either version 2 of the
>>>>> License, or        #
>>>>> +# (at your option) any later
>>>>> version.                                      #
>>>>> +#                                                             
>>>>>              #
>>>>> +# IPFire is distributed in the hope that it will be
>>>>> useful,                #
>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>> of           #
>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>> the            #
>>>>> +# GNU General Public License for more
>>>>> details.                             #
>>>>> +#                                                             
>>>>>              #
>>>>> +# You should have received a copy of the GNU General Public
>>>>> License        #
>>>>> +# along with IPFire; if not, write to the Free
>>>>> Software                    #
>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>> 02111-1307 USA #
>>>>> +#                                                             
>>>>>              #
>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>> <info@ipfire.org>.                        #
>>>>> +#                                                             
>>>>>              #
>>>>> +##############################################################
>>>>> ##############
>>>>> +#
>>>>> +. /opt/pakfire/lib/functions.sh
>>>>> +make_backup ${NAME}
>>>>> +remove_files
>>>>> +
>>>>> +/usr/local/bin/update-lang-cache
>>>>> diff --git a/src/paks/portredir/update.sh
>>>>> b/src/paks/portredir/update.sh
>>>>> new file mode 100644
>>>>> index 000000000..89c40d0d7
>>>>> --- /dev/null
>>>>> +++ b/src/paks/portredir/update.sh
>>>>> @@ -0,0 +1,26 @@
>>>>> +#!/bin/bash
>>>>> +##############################################################
>>>>> ##############
>>>>> +#                                                             
>>>>>              #
>>>>> +# This file is part of the IPFire
>>>>> Firewall.                                #
>>>>> +#                                                             
>>>>>              #
>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>> modify           #
>>>>> +# it under the terms of the GNU General Public License as
>>>>> published by     #
>>>>> +# the Free Software Foundation; either version 2 of the
>>>>> License, or        #
>>>>> +# (at your option) any later
>>>>> version.                                      #
>>>>> +#                                                             
>>>>>              #
>>>>> +# IPFire is distributed in the hope that it will be
>>>>> useful,                #
>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>> of           #
>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>> the            #
>>>>> +# GNU General Public License for more
>>>>> details.                             #
>>>>> +#                                                             
>>>>>              #
>>>>> +# You should have received a copy of the GNU General Public
>>>>> License        #
>>>>> +# along with IPFire; if not, write to the Free
>>>>> Software                    #
>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>> 02111-1307 USA #
>>>>> +#                                                             
>>>>>              #
>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>> <info@ipfire.org>.                        #
>>>>> +#                                                             
>>>>>              #
>>>>> +##############################################################
>>>>> ##############
>>>>> +#
>>>>> +. /opt/pakfire/lib/functions.sh
>>>>> +./uninstall.sh
>>>>> +./install.sh
>>>>> -- 
>>>>> 2.18.0
>>>>> 
>>>> 
>>> 
>> 
>
  
Michael Tremer Aug. 5, 2021, 8:33 p.m. UTC | #14
Hello,

> On 3 Aug 2021, at 18:04, Jon Murphy <jcmurphy26@gmail.com> wrote:
> 
> Hello all!
> 
> I’m having trouble following the changes to firewall.cgi.  I think there were 4 or 5 patches 
> submitted to the Dev Mailing List.
> 
> Did these get approved?

That is a good question. AFAIK they are not in c159.

> 
> I looked at the Changelog for Core 158 and core 159 (testing) but I didn’t see anything:
> 
> https://nightly.ipfire.org/core158/2021-07-21%2015:47:35%20+0000-23498c61/x86_64/changelog.txt
> 
> https://nightly.ipfire.org/master/latest/x86_64/changelog.txt
> 
> 
> Can they be added to CU 159?
> 
> To be honest I am mainly interested in the DNS redirect (and its cousin NTP redirect).
> 
> I’d be happy to help test but I do need some help applying the patches.  
> Right now I apply them by grabbing a copy of firewall.cgi and manually deleting and then manually adding a patch.
> Not the smartest way but it works (if I don’t screw-up and make a mistake!).

@Stefan: What is the status on this?

-Michael

> Jon
> 
>> On Jun 30, 2021, at 2:14 PM, Stefan Schantl <stefan.schantl@ipfire.org> wrote:
>> 
>> Hello Matthias, Hello Michael, Hello Jon, Hello *,
>> 
>> I've followed the conversation on this list since the first mail and
>> thoughts about forcing DNS traffic to use the local resolver.
>> 
>> It was a very long journey and lot of time and work has been spent to
>> get to the present point.
>> 
>> As Michael requested here, I've digged through the lines of the perl
>> script which is responsible for creating the firewall rules and
>> surprisingly found that everyting which is needed to create generic
>> REDIRECT rules already was written in the past - it just did not work
>> as designed/expected.
>> 
>> Finaly I was able to adjust these lines of code and to repair that
>> feature.
>> 
>> A redirect rule can be created by picking a single host or group of
>> hosts or entire network(s) as source, selecting NAT (DNAT) and choosing
>> the Firewall itself as target.
>> 
>> The protocol or service or service group which should be redirected has
>> to be selected afterwards. If you want to redirect a given port to
>> another one it can be specified as "Target port".
>> 
>> All created redirect rules are displayed as "input rules".
>> 
>> 
>> The patch directly can be accessed here:
>> 
>> https://patchwork.ipfire.org/project/ipfire/patch/20210630184031.7726-1-stefan.schantl@ipfire.org/
>> 
>> Best regards,
>> 
>> -Stefan
>> 
>>> Hello,
>>> 
>>>> On 28 Jun 2021, at 18:53, Jon Murphy <jcmurphy26@gmail.com> wrote:
>>>> 
>>>> Hi Michael!  Happy Monday!
>>>> 
>>>> 
>>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>>> of code instead of adding many hundreds of lines?
>>>>> 
>>>>> Please can someone elaborate on this more?
>>>> 
>>>> Doing a DNS redirect, via the WegBUI, has been an issue since
>>>> 2015.  I found this quote in the old forum:
>>>> 
>>>> "Having investigated a bit more I have concluded that it's not
>>>> currently possible to create such rules through the WUI.
>>>> 
>>>> There are a number of obstacles:
>>>> 1. It is not allowed to create a rule where source IP and
>>>> destination nat IP is on the same subnetwork (e.g. GREEN), WUI
>>>> error message: "Source and destination IP addresses are from the
>>>> same subnet."
>>>> 
>>>> 2. WUI will not allow you to create a rule without a destination
>>>> (the filtered packet must adhere to a destination, not only a port)
>>>> and the destination MUST be an IP address of one of the IPFire
>>>> interfaces, which limits whats possible a great deal." 
>>> 
>>> And these cannot be changed?
>>> 
>>>> And I found this from 2016:
>>>> https://bugzilla.ipfire.org/show_bug.cgi?id=11168
>>>> 
>>>> So I am guessing that no one has been able to determine a way to
>>>> extend the WebGUI.  
>>> 
>>> Has anyone tried? I do not see any obvious reasons why this should
>>> not be possible.
>>> 
>>>> I am curious - Who created the 
>>>> https://ipfire:444/cgi-bin/firewall.cgi page?  And could they help?
>>> 
>>> -Michael
>>> 
>>>> Jon
>>>> 
>>>> 
>>>>> On Jun 28, 2021, at 11:04 AM, Michael Tremer <
>>>>> michael.tremer@ipfire.org> wrote:
>>>>> 
>>>>> Hello Matthias,
>>>>> 
>>>>>> On 27 Jun 2021, at 14:48, Matthias Fischer <
>>>>>> matthias.fischer@ipfire.org> wrote:
>>>>>> 
>>>>>> From: Marcel Lorenz <marcel.lorenz@ipfire.org>
>>>>> 
>>>>> Thank you for sending this patch on Marcel’s behalf, but I would
>>>>> much more prefer if he would submit his patches on his own. I do
>>>>> not see why that isn’t possible.
>>>>> 
>>>>>> Please note:
>>>>>> This is a new addon written by Marcel Lorenz <
>>>>>> marcel.lorenz@ipfire.org>.
>>>>>> 
>>>>>> It adds a new GUI to IPFire for DNS/NTP *and* user specific
>>>>>> port redirections.
>>>>>> 
>>>>>> How its working:
>>>>>> It has exactly the same functionalities as "Forcing
>>>>>> DNS/NTP..."  - and some more.
>>>>>> 
>>>>>> By setting switches, DNS/NTP requests are automatically
>>>>>> redirected to the local IPFire DNS/NTP servers.
>>>>>> 
>>>>>> Additionally, the user can specify custom redirections.
>>>>>> 
>>>>>> These rules are added to a new chain in PREROUTING =>
>>>>>> PORT_REDIRECT.
>>>>>> 
>>>>>> To avoid problems with (e.g.) transparent 'squid'
>>>>>> configurations,
>>>>>> redirection rules are added automatically before existing
>>>>>> 'squid' rules.
>>>>> 
>>>>> This message does unfortunately not say why this add-on would be
>>>>> useful. I am emphasising this again and again that it is not very
>>>>> important how something is done specially. That should be
>>>>> commented in the code and other implementation details should
>>>>> also be documented there.
>>>>> 
>>>>> As I have stated on this functionality many times before, I do
>>>>> not see why this is necessary at all.
>>>>> 
>>>>> Why is this an add-on?
>>>>> 
>>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>>> of code instead of adding many hundreds of lines?
>>>>> 
>>>>> Please can someone elaborate on this more?
>>>>> 
>>>>> -Michael
>>>>> 
>>>>>> Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
>>>>>> ---
>>>>>> config/portredir/EX-portredir.menu    |   6 +
>>>>>> config/portredir/lang/portredir.de.pl |  19 +
>>>>>> config/portredir/lang/portredir.en.pl |  19 +
>>>>>> config/portredir/portredir-backup     |   1 +
>>>>>> config/portredir/portredir.cgi        | 525
>>>>>> ++++++++++++++++++++++++++
>>>>>> config/rootfiles/common/misc-progs    |   1 +
>>>>>> config/rootfiles/packages/portredir   |  11 +
>>>>>> lfs/portredir                         |  85 +++++
>>>>>> make.sh                               |   1 +
>>>>>> src/initscripts/packages/portredir    | 191 ++++++++++
>>>>>> src/misc-progs/Makefile               |   2 +-
>>>>>> src/misc-progs/portredirctrl.c        |  47 +++
>>>>>> src/paks/portredir/install.sh         |  32 ++
>>>>>> src/paks/portredir/uninstall.sh       |  28 ++
>>>>>> src/paks/portredir/update.sh          |  26 ++
>>>>>> 15 files changed, 993 insertions(+), 1 deletion(-)
>>>>>> create mode 100644 config/portredir/EX-portredir.menu
>>>>>> create mode 100644 config/portredir/lang/portredir.de.pl
>>>>>> create mode 100644 config/portredir/lang/portredir.en.pl
>>>>>> create mode 100644 config/portredir/portredir-backup
>>>>>> create mode 100644 config/portredir/portredir.cgi
>>>>>> create mode 100644 config/rootfiles/packages/portredir
>>>>>> create mode 100644 lfs/portredir
>>>>>> create mode 100644 src/initscripts/packages/portredir
>>>>>> create mode 100644 src/misc-progs/portredirctrl.c
>>>>>> create mode 100644 src/paks/portredir/install.sh
>>>>>> create mode 100644 src/paks/portredir/uninstall.sh
>>>>>> create mode 100644 src/paks/portredir/update.sh
>>>>>> 
>>>>>> diff --git a/config/portredir/EX-portredir.menu
>>>>>> b/config/portredir/EX-portredir.menu
>>>>>> new file mode 100644
>>>>>> index 000000000..8376e8053
>>>>>> --- /dev/null
>>>>>> +++ b/config/portredir/EX-portredir.menu
>>>>>> @@ -0,0 +1,6 @@
>>>>>> +    $subfirewall->{'95.portredir'} = {
>>>>>> +                               'caption' =>
>>>>>> $Lang::tr{'portredir port redirections'},
>>>>>> +                               'uri' => '/cgi-
>>>>>> bin/portredir.cgi',
>>>>>> +                               'title' =>
>>>>>> "$Lang::tr{'portredir port redirections'}",
>>>>>> +                               'enabled' => 1
>>>>>> +                               };
>>>>>> diff --git a/config/portredir/lang/portredir.de.pl
>>>>>> b/config/portredir/lang/portredir.de.pl
>>>>>> new file mode 100644
>>>>>> index 000000000..b932d4a85
>>>>>> --- /dev/null
>>>>>> +++ b/config/portredir/lang/portredir.de.pl
>>>>>> @@ -0,0 +1,19 @@
>>>>>> +%tr = (
>>>>>> +%tr,
>>>>>> +'portredir enable addon' => 'Addon aktivieren',
>>>>>> +'portredir common settings' => 'Allgemeine Einstellungen',
>>>>>> +'portredir port redirections' => 'Portumleitungen',
>>>>>> +'portredir fw for interface' => 'Firewalloptionen für das
>>>>>> Interface',
>>>>>> +'portredir enable user redirections' => 'Aktiviere
>>>>>> benutzerdefinierte Portumleitungen',
>>>>>> +'portredir force local dns' => 'Erzwinge lokale DNS-Server',
>>>>>> +'portredir force local ntp' => 'Erzwinge lokale NTP-Server',
>>>>>> +'portredir custom redirections' => 'Benutzerdefinierte
>>>>>> Portumleitungen',
>>>>>> +'portredir remove rule' => 'Entferne Regel',
>>>>>> +'portredir add rule' => 'Hinzufügen',
>>>>>> +'portredir no entries' => 'Keine Einträge vorhanden.',
>>>>>> +'portredir invalid address' => 'Ungültige Host-Addresse.',
>>>>>> +'portredir empty input' => 'Fehlende Angabe: Bitte geben Sie
>>>>>> einen gültigen Host an.',
>>>>>> +'portredir save to activate' => 'Speichern, um Änderungen zu
>>>>>> aktivieren',
>>>>>> +);
>>>>>> +
>>>>>> +#EOF
>>>>>> diff --git a/config/portredir/lang/portredir.en.pl
>>>>>> b/config/portredir/lang/portredir.en.pl
>>>>>> new file mode 100644
>>>>>> index 000000000..f442f3eaa
>>>>>> --- /dev/null
>>>>>> +++ b/config/portredir/lang/portredir.en.pl
>>>>>> @@ -0,0 +1,19 @@
>>>>>> +%tr = (
>>>>>> +%tr,
>>>>>> +'portredir enable addon' => 'Enable addon',
>>>>>> +'portredir common settings' => 'Common settings',
>>>>>> +'portredir port redirections' => 'Port redirections',
>>>>>> +'portredir fw for interface' => 'Firewall options for
>>>>>> interface',
>>>>>> +'portredir enable user redirections' => 'Enable user port
>>>>>> redirections',
>>>>>> +'portredir force local dns' => 'Enforce local DNS servers',
>>>>>> +'portredir force local ntp' => 'Enforce local NTP servers',
>>>>>> +'portredir custom redirections' => 'Custom port redirections',
>>>>>> +'portredir remove rule' => 'Remove rule',
>>>>>> +'portredir add rule' => 'Add new',
>>>>>> +'portredir no entries' => 'No entries at the moment.',
>>>>>> +'portredir invalid address' => 'Invalid host address.',
>>>>>> +'portredir empty input' => 'Empty input: Please enter a valid
>>>>>> host.',
>>>>>> +'portredir save to activate' => 'Save to activate changes',
>>>>>> +);
>>>>>> +
>>>>>> +#EOF
>>>>>> diff --git a/config/portredir/portredir-backup
>>>>>> b/config/portredir/portredir-backup
>>>>>> new file mode 100644
>>>>>> index 000000000..bd2ada742
>>>>>> --- /dev/null
>>>>>> +++ b/config/portredir/portredir-backup
>>>>>> @@ -0,0 +1 @@
>>>>>> +/var/ipfire/portredir
>>>>>> diff --git a/config/portredir/portredir.cgi
>>>>>> b/config/portredir/portredir.cgi
>>>>>> new file mode 100644
>>>>>> index 000000000..4913dda3f
>>>>>> --- /dev/null
>>>>>> +++ b/config/portredir/portredir.cgi
>>>>>> @@ -0,0 +1,525 @@
>>>>>> +#!/usr/bin/perl
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# IPFire.org - A linux based
>>>>>> firewall                                         #
>>>>>> +# Copyright (C) 2021  IPFire Team 
>>>>>> <info@ipfire.org>                          #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# This program is free software: you can redistribute it
>>>>>> and/or modify        #
>>>>>> +# it under the terms of the GNU General Public License as
>>>>>> published by        #
>>>>>> +# the Free Software Foundation, either version 3 of the
>>>>>> License, or           #
>>>>>> +# (at your option) any later
>>>>>> version.                                         #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# This program is distributed in the hope that it will be
>>>>>> useful,             #
>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>> of              #
>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>> the               #
>>>>>> +# GNU General Public License for more
>>>>>> details.                                #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# You should have received a copy of the GNU General Public
>>>>>> License           #
>>>>>> +# along with this program.  If not, see <
>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +
>>>>>> +use strict;
>>>>>> +
>>>>>> +# enable only the following on debugging purpose
>>>>>> +use warnings;
>>>>>> +use CGI::Carp 'fatalsToBrowser';
>>>>>> +
>>>>>> +require '/var/ipfire/general-functions.pl';
>>>>>> +require "${General::swroot}/lang.pl";
>>>>>> +require "${General::swroot}/header.pl";
>>>>>> +
>>>>>> +# File declarations
>>>>>> +my $settingsfile = "${General::swroot}/portredir/settings";
>>>>>> +my $redirectsfile = "${General::swroot}/portredir/redirects";
>>>>>> +
>>>>>> +# Create empty settingsfiles if they does not exist yet
>>>>>> +unless (-e "$settingsfile") { system ("touch $settingsfile");
>>>>>> }
>>>>>> +unless (-e "$redirectsfile") { system ("touch
>>>>>> $redirectsfile"); }
>>>>>> +
>>>>>> +# load ipfire settings
>>>>>> +our %netsettings = ();
>>>>>> +our %color = ();
>>>>>> +&General::readhash("${General::swroot}/ethernet/settings",
>>>>>> \%netsettings);
>>>>>> +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include
>>>>>> /colors.txt", \%color);
>>>>>> +
>>>>>> +my %settings=();
>>>>>> +my %portredirs=();
>>>>>> +my %checked=(); # Checkbox manipulations
>>>>>> +my $errormessage='';
>>>>>> +my %selected=();
>>>>>> +our %redirects=();
>>>>>> +
>>>>>> +$settings{'ACTION'} = '';
>>>>>> +$settings{'REDIR_ENABLE_ADDON'}="off";
>>>>>> +$settings{'REDIR_CUSTOM_GREEN'}="off";
>>>>>> +$settings{'REDIR_CUSTOM_BLUE'}="off";
>>>>>> +$settings{'REDIR_CUSTOM_ORANGE'}="off";
>>>>>> +$settings{'REDIR_DNS_GREEN'}="off";
>>>>>> +$settings{'REDIR_NTP_GREEN'}="off";
>>>>>> +$settings{'REDIR_DNS_BLUE'}="off";
>>>>>> +$settings{'REDIR_NTP_BLUE'}="off";
>>>>>> +$settings{'REDIR_DNS_ORANGE'}="off";
>>>>>> +$settings{'REDIR_NTP_ORANGE'}="off";
>>>>>> +
>>>>>> +&Header::showhttpheaders();
>>>>>> +
>>>>>> +# Get GUI values
>>>>>> +&Header::getcgihash(\%settings);
>>>>>> +
>>>>>> +# Save action
>>>>>> +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
>>>>>> +
>>>>>> +       # If custom rules enabled, deactivate default rules on
>>>>>> interface
>>>>>> +       if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
>>>>>> +               $settings{'REDIR_DNS_GREEN'}="off";
>>>>>> +               $settings{'REDIR_NTP_GREEN'}="off";
>>>>>> +       }
>>>>>> +
>>>>>> +       if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
>>>>>> +               $settings{'REDIR_DNS_BLUE'}="off";
>>>>>> +               $settings{'REDIR_NTP_BLUE'}="off";
>>>>>> +       }
>>>>>> +
>>>>>> +       if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
>>>>>> +               $settings{'REDIR_DNS_ORANGE'}="off";
>>>>>> +               $settings{'REDIR_NTP_ORANGE'}="off";
>>>>>> +       }
>>>>>> +
>>>>>> +       &General::writehash($settingsfile, \%settings);
>>>>>> +
>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
>>>>>> +               system ('/usr/local/bin/portredirctrl restart
>>>>>>> /dev/null 2>&1');
>>>>>> +               system ('/usr/local/bin/portredirctrl enable
>>>>>>> /dev/null 2>&1');
>>>>>> +               &General::log('portredir addon: port
>>>>>> redirections enabled');
>>>>>> +       }
>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
>>>>>> +               system ('/usr/local/bin/portredirctrl disable
>>>>>>> /dev/null 2>&1');
>>>>>> +               system ('/usr/local/bin/portredirctrl stop
>>>>>>> /dev/null 2>&1');
>>>>>> +               &General::log('portredir addon: port
>>>>>> redirections disabled');
>>>>>> +       }
>>>>>> +
>>>>>> +# Add/edit an entry to the redirectsfile.
>>>>>> +
>>>>>> +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) ||
>>>>>> ($settings{'ACTION'} eq $Lang::tr{'update'})) {
>>>>>> +
>>>>>> +       # Check if any input has been performed.
>>>>>> +       if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
>>>>>> +
>>>>>> +               # Check if the given input is no valid IP-
>>>>>> address, display an error message.
>>>>>> +               if
>>>>>> (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
>>>>>> +                       $errormessage = "$Lang::tr{'portredir
>>>>>> invalid address'}";
>>>>>> +               }
>>>>>> +       } else {
>>>>>> +               $errormessage = "$Lang::tr{'portredir empty
>>>>>> input'}";
>>>>>> +       }
>>>>>> +
>>>>>> +       # Go further if there was no error.
>>>>>> +       if ($errormessage eq '') {
>>>>>> +               my %redirects = ();
>>>>>> +               my $id;
>>>>>> +               my $status;
>>>>>> +
>>>>>> +               # Assign hash values.
>>>>>> +               my $new_entry_interface =
>>>>>> $settings{'REDIR_ENTRY_INTERFACE'};
>>>>>> +               my $new_entry_protocol =
>>>>>> $settings{'REDIR_ENTRY_PROTOCOL'};
>>>>>> +               my $new_entry_port =
>>>>>> $settings{'REDIR_ENTRY_PORT'};
>>>>>> +               my $new_entry_address =
>>>>>> $settings{'REDIR_ENTRY_ADDRESS'};
>>>>>> +               my $new_entry_remark =
>>>>>> $settings{'REDIR_ENTRY_REMARK'};
>>>>>> +
>>>>>> +               # Read-in redirectsfile.
>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>> \%redirects);
>>>>>> +
>>>>>> +               # Check if we should edit an existing entry and
>>>>>> got an ID.
>>>>>> +               if (($settings{'ACTION'} eq
>>>>>> $Lang::tr{'update'}) && ($settings{'ID'})) {
>>>>>> +                       # Assin the provided id.
>>>>>> +                       $id = $settings{'ID'};
>>>>>> +
>>>>>> +                       # Undef the given ID.
>>>>>> +                       undef($settings{'ID'});
>>>>>> +
>>>>>> +                       # Grab the configured status of the
>>>>>> corresponding entry.
>>>>>> +                       $status = $redirects{$id}[4];
>>>>>> +               } else {
>>>>>> +                       # Each newly added entry automatically
>>>>>> should be enabled.
>>>>>> +                       $status = "enabled";
>>>>>> +
>>>>>> +                       # Generate the ID for the new entry.
>>>>>> +                       #
>>>>>> +                       # Sort the keys by their ID and store
>>>>>> them in an array.
>>>>>> +                       my @keys = sort { $a <=> $b } keys
>>>>>> %redirects;
>>>>>> +
>>>>>> +                       # Reverse the key array.
>>>>>> +                       my @reversed = reverse(@keys);
>>>>>> +
>>>>>> +                       # Obtain the last used id.
>>>>>> +                       my $last_id = @reversed[0];
>>>>>> +
>>>>>> +                       # Increase the last id by one and use
>>>>>> it as id for the new entry.
>>>>>> +                       $id = ++$last_id;
>>>>>> +               }
>>>>>> +
>>>>>> +               # Add/Modify the entry to/in the redirects
>>>>>> hash.
>>>>>> +               $redirects{$id} = ["$new_entry_interface",
>>>>>> "$new_entry_protocol", "$new_entry_port",
>>>>>> "$new_entry_address","$status", "$new_entry_remark"];
>>>>>> +
>>>>>> +               # Write the changed redirects hash to the
>>>>>> redirects file.
>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>> \%redirects);
>>>>>> +       }
>>>>>> +
>>>>>> +# Toggle Enabled/Disabled for an existing entry on the
>>>>>> redirects list.
>>>>>> +
>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable
>>>>>> disable'}) {
>>>>>> +       my %redirects = ();
>>>>>> +
>>>>>> +       # Only go further, if an ID has been passed.
>>>>>> +       if ($settings{'ID'}) {
>>>>>> +               # Assign the given ID.
>>>>>> +               my $id = $settings{'ID'};
>>>>>> +
>>>>>> +               # Undef the given ID.
>>>>>> +               undef($settings{'ID'});
>>>>>> +
>>>>>> +               # Read-in ignoredfile.
>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>> \%redirects);
>>>>>> +
>>>>>> +               # Grab the configured status of the
>>>>>> corresponding entry.
>>>>>> +               my $status = $redirects{$id}[4];
>>>>>> +
>>>>>> +               # Switch the status.
>>>>>> +               if ($status eq "disabled") {
>>>>>> +                       $status = "enabled";
>>>>>> +               } else {
>>>>>> +                       $status = "disabled";
>>>>>> +               }
>>>>>> +
>>>>>> +               # Modify the status of the existing entry.
>>>>>> +               $redirects{$id} = ["$redirects{$id}[0]",
>>>>>> "$redirects{$id}[1]", "$redirects{$id}[2]",
>>>>>> "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
>>>>>> +
>>>>>> +               # Write the changed ignored hash to the
>>>>>> redirects file.
>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>> \%redirects);
>>>>>> +       }
>>>>>> +
>>>>>> +# Remove entry from redirects list.
>>>>>> +
>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
>>>>>> +       my %redirects = ();
>>>>>> +
>>>>>> +       # Read-in redirectsfile.
>>>>>> +       &General::readhasharray($redirectsfile, \%redirects);
>>>>>> +
>>>>>> +       # move data on key up
>>>>>> +       foreach my $key (sort keys %redirects) {
>>>>>> +               if ($key >= $settings{'ID'}) {
>>>>>> +                       my $next = $key + 1;
>>>>>> +                       if (exists $redirects{$next}) {
>>>>>> +                               foreach my $i (0 ..
>>>>>> $#{$redirects{$next}}) { $redirects{$key}[$i] =
>>>>>> $redirects{$next}[$i]; }
>>>>>> +                       }
>>>>>> +               }
>>>>>> +       }
>>>>>> +
>>>>>> +       my $last_key = (sort {$a <=> $b} keys %redirects)[-1];
>>>>>> +       delete $redirects{$last_key};
>>>>>> +
>>>>>> +       # Undef the given ID.
>>>>>> +       undef($settings{'ID'});
>>>>>> +
>>>>>> +       # Write the changed redirects hash to file.
>>>>>> +       &General::writehasharray($redirectsfile, \%redirects);
>>>>>> +}
>>>>>> +
>>>>>> +# Load settings from file
>>>>>> +&General::readhash($settingsfile, \%settings);
>>>>>> +&General::readhasharray($redirectsfile, \%redirects);
>>>>>> +
>>>>>> +# Call functions to generate whole page.
>>>>>> +&Header::openpage($Lang::tr{'portredir port redirections'}, 1,
>>>>>> '');
>>>>>> +&Header::openbigbox('100%', 'left', '', $errormessage);
>>>>>> +
>>>>>> +if ($errormessage) {
>>>>>> +        &Header::openbox('100%', 'left', $Lang::tr{'warning
>>>>>> messages'});
>>>>>> +        print "<font color='red'>$errormessage&nbsp;</font>";
>>>>>> +        &Header::closebox();
>>>>>> +}
>>>>>> +
>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_ADDON'}
>>>>>> } = "checked='checked'";
>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_GREEN'}
>>>>>> } = "checked='checked'";
>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BLUE'}}
>>>>>> = "checked='checked'";
>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_ORANGE
>>>>>> '}} = "checked='checked'";
>>>>>> +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
>>>>>> +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
>>>>>> +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}} =
>>>>>> "checked='checked'";
>>>>>> +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
>>>>>> +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
>>>>>> +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}} =
>>>>>> "checked='checked'";
>>>>>> +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
>>>>>> +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
>>>>>> +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}} =
>>>>>> "checked='checked'";
>>>>>> +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
>>>>>> +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
>>>>>> +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}} =
>>>>>> "checked='checked'";
>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
>>>>>> +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE'}} =
>>>>>> "checked='checked'";
>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
>>>>>> +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE'}} =
>>>>>> "checked='checked'";
>>>>>> +
>>>>>> +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTRY_INTE
>>>>>> RFACE'}} = 'selected';
>>>>>> +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY_PROTO
>>>>>> COL'}} = 'selected';
>>>>>> +
>>>>>> +&showMainBox();
>>>>>> +&showRedirectsBox();
>>>>>> +
>>>>>> +&Header::closebigbox();
>>>>>> +&Header::closepage();
>>>>>> +
>>>>>> +# Function to show main settings and options.
>>>>>> +sub showMainBox() {
>>>>>> +
>>>>>> +       &Header::openbox('100%', 'center',
>>>>>> "$Lang::tr{'settings'}");
>>>>>> +       print "<form method='post'
>>>>>> action='$ENV{'SCRIPT_NAME'}'>";
>>>>>> +
>>>>>> +print <<END;
>>>>>> +<table width='80%' cellspacing='0' border='0'>
>>>>>> +       <tr><td colspan='2' class='base'
>>>>>> bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir common
>>>>>> settings'}</b></td></tr
>>>>>> +       <tr>
>>>>>> +               <td width='25%'
>>>>>> class='base'>$Lang::tr{'portredir enable addon'}:</td>
>>>>>> +               <td><input type='checkbox'
>>>>>> name='REDIR_ENABLE_ADDON' $checked{'REDIR_ENABLE_ADDON'}{'on'}
>>>>>> /></td>
>>>>>> +       </tr>
>>>>>> +       <tr><td colspan='2'></td></tr>
>>>>>> +       <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>> +END
>>>>>> +
>>>>>> +       # create html table with header line 1
>>>>>> +       print "<table width='80%' cellspacing='0'
>>>>>> border='0'><tr>";
>>>>>> +       print "<th class='base' width='40%'
>>>>>> align='left'><b>$Lang::tr{'portredir fw for interface'}</th>";
>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<th
>>>>>> class='base' width='10%'><b><font
>>>>>> color=green>$Lang::tr{'green'}</font></th>";
>>>>>> +               } else {                 print "<th
>>>>>> class='base' width='10%'></font></th>"; }
>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<th
>>>>>> class='base' width='10%'><b><font
>>>>>> color=blue>$Lang::tr{'blue'}</font></th>";
>>>>>> +               } else {                 print "<th
>>>>>> class='base' width='10%'></font></th>"; }
>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<th
>>>>>> class='base' width='10%'><b><font
>>>>>> color=orange>$Lang::tr{'orange'}</font></th>";
>>>>>> +               } else {                 print "<th
>>>>>> class='base' width='10%'></font></th>"; }
>>>>>> +
>>>>>> +       # the empty right row
>>>>>> +       print "<th class='base'
>>>>>> width='30%'><td></td></th></tr>";
>>>>>> +
>>>>>> +       # line 2
>>>>>> +       print "<tr><td>$Lang::tr{'portredir force local
>>>>>> dns'}</td>";
>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_DNS_GREEN'
>>>>>> $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print
>>>>>> "<td></td>";}
>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_DNS_BLUE' $checked{'REDIR_DNS_BLUE'}{'on'}></td>";}
>>>>>> else { print "<td></td>";}
>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_DNS_ORANGE'
>>>>>> $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print
>>>>>> "<td></td>";}
>>>>>> +
>>>>>> +       # line 3
>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir force local
>>>>>> ntp'}</td>";
>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_NTP_GREEN'
>>>>>> $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print
>>>>>> "<td></td>";}
>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_NTP_BLUE' $checked{'REDIR_NTP_BLUE'}{'on'}></td>";}
>>>>>> else { print "<td></td>";}
>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_NTP_ORANGE'
>>>>>> $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print
>>>>>> "<td></td>";}
>>>>>> +
>>>>>> +       # line 4
>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir enable user
>>>>>> redirections'}</td>";
>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_CUSTOM_GREEN'
>>>>>> $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else { print
>>>>>> "<td></td>";}
>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_CUSTOM_BLUE'
>>>>>> $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print
>>>>>> "<td></td>";}
>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>> class='base' align='center'><input type='checkbox'
>>>>>> name='REDIR_CUSTOM_ORANGE'
>>>>>> $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else { print
>>>>>> "<td></td>";}
>>>>>> +
>>>>>> +       print <<END;
>>>>>> +       </tr></table>
>>>>>> +       <table width='80%' cellspacing='0' border='0'>
>>>>>> +               <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>> +               <tr><td align='left'><b>$Lang::tr{'portredir
>>>>>> save to activate'}</b></td><td width='5%' align='center'><input
>>>>>> type='submit' name='ACTION' value='  $Lang::tr{'save'} 
>>>>>> '></td></tr>
>>>>>> +       </table></form>
>>>>>> +END
>>>>>> +
>>>>>> +&Header::closebox();
>>>>>> +}
>>>>>> +
>>>>>> +# Function to show elements of the redirects file and allow to
>>>>>> add or remove single members of it.
>>>>>> +sub showRedirectsBox() {
>>>>>> +        &Header::openbox('100%', 'center',
>>>>>> "$Lang::tr{'portredir custom redirections'}");
>>>>>> +
>>>>>> +       print <<END;
>>>>>> +               <table width='80%' cellspacing='1' border='0'>
>>>>>> +                       <tr>
>>>>>> +                               <td class='base'
>>>>>> bgcolor='$color{'color20'}'
>>>>>> align='center'><b>$Lang::tr{'interface'}</b></td>
>>>>>> +                               <td class='base'
>>>>>> bgcolor='$color{'color20'}'
>>>>>> align='center'><b>$Lang::tr{'protocol'}</b></td>
>>>>>> +                               <td class='base'
>>>>>> bgcolor='$color{'color20'}'
>>>>>> align='center'><b>$Lang::tr{'port'}</b></td>
>>>>>> +                               <td class='base'
>>>>>> bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'ip
>>>>>> address'}</b></td>
>>>>>> +                               <td class='base'
>>>>>> bgcolor='$color{'color20'}'
>>>>>> align='center'><b>$Lang::tr{'remark'}</b></td>
>>>>>> +                               <td class='base' colspan='3'
>>>>>> bgcolor='$color{'color20'}'></td>
>>>>>> +                       </tr>
>>>>>> +END
>>>>>> +                       # Check if some rules have been added
>>>>>> to be redirects.
>>>>>> +                       if (keys (%redirects)) {
>>>>>> +                               my $col = "";
>>>>>> +
>>>>>> +                               # List all entries of the hash.
>>>>>> +                               foreach my $key (sort keys
>>>>>> %redirects){
>>>>>> +
>>>>>> +                                       # Assign data array
>>>>>> positions to some nice variable names.
>>>>>> +                                       my $interface =
>>>>>> $redirects{$key}[0];
>>>>>> +                                       my $protocol =
>>>>>> $redirects{$key}[1];
>>>>>> +                                       my $port  =
>>>>>> $redirects{$key}[2];
>>>>>> +                                       my $address =
>>>>>> $redirects{$key}[3];
>>>>>> +                                       my $status  =
>>>>>> $redirects{$key}[4];
>>>>>> +                                       my $remark  =
>>>>>> $redirects{$key}[5];
>>>>>> +
>>>>>> +                                       # Check if the key (id)
>>>>>> number is even or not.
>>>>>> +                                       if ($settings{'ID'} eq
>>>>>> $key) {
>>>>>> +                                               $col="bgcolor='
>>>>>> ${Header::colouryellow}'";
>>>>>> +                                       } elsif ($key % 2) {
>>>>>> +                                               $col="bgcolor='
>>>>>> $color{'color22'}'";
>>>>>> +                                       } else {
>>>>>> +                                               $col="bgcolor='
>>>>>> $color{'color20'}'";
>>>>>> +                                       }
>>>>>> +
>>>>>> +                                       # Choose icon for the
>>>>>> checkbox.
>>>>>> +                                       my $gif;
>>>>>> +                                       my $gdesc;
>>>>>> +
>>>>>> +                                       # Check if the status
>>>>>> is enabled and select the correct image and description.
>>>>>> +                                       if ($status eq
>>>>>> 'enabled' ) {
>>>>>> +                                               $gif =
>>>>>> 'on.gif';
>>>>>> +                                               $gdesc =
>>>>>> $Lang::tr{'click to disable'};
>>>>>> +                                       } else {
>>>>>> +                                               $gif =
>>>>>> 'off.gif';
>>>>>> +                                               $gdesc =
>>>>>> $Lang::tr{'click to enable'};
>>>>>> +                                       }
>>>>>> +
>>>>>> +                                       print <<END;
>>>>>> +                                       <tr>
>>>>>> +                                               <td width='15%'
>>>>>> class='base' align='center' $col><b><font
>>>>>> color=$interface>$Lang::tr{$interface}</font></b></td>
>>>>>> +                                               <td width='10%'
>>>>>> class='base' align='center' $col>$protocol</td>
>>>>>> +                                               <td width='10%'
>>>>>> class='base' align='center' $col>$port</td>
>>>>>> +                                               <td width='15%'
>>>>>> class='base' align='center' $col>&nbsp;$address</td>
>>>>>> +                                               <td width='40%'
>>>>>> class='base' align='center' $col>&nbsp;$remark</td>
>>>>>> +                                               <td
>>>>>> align='center' $col>
>>>>>> +                                                       <form
>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>> +                                                              
>>>>>>  <input type='hidden' name='ACTION' value='$Lang::tr{'toggle
>>>>>> enable disable'}' />
>>>>>> +                                                              
>>>>>>  <input type='image' name='$Lang::tr{'toggle enable disable'}'
>>>>>> src='/images/$gif' alt='$gdesc' title='$gdesc' />
>>>>>> +                                                              
>>>>>>  <input type='hidden' name='ID' value='$key' />
>>>>>> +                                                       </form>
>>>>>> +                                               </td>
>>>>>> +                                               <td
>>>>>> align='center' $col>
>>>>>> +                                                       <form
>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>> +                                                              
>>>>>>  <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}'
>>>>>> />
>>>>>> +                                                              
>>>>>>  <input type='image' name='$Lang::tr{'edit'}'
>>>>>> src='/images/edit.gif' alt='$Lang::tr{'edit'}'
>>>>>> title='$Lang::tr{'edit'}' />
>>>>>> +                                                              
>>>>>>  <input type='hidden' name='ID' value='$key' />
>>>>>> +                                                       </form>
>>>>>> +                                               </td>
>>>>>> +                                               <td
>>>>>> align='center' $col>
>>>>>> +                                                       <form
>>>>>> method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
>>>>>> +                                                              
>>>>>>  <input type='image' name='$Lang::tr{'remove'}'
>>>>>> src='/images/delete.gif' title='$Lang::tr{'remove'}'
>>>>>> alt='$Lang::tr{'remove'}'>
>>>>>> +                                                              
>>>>>>  <input type='hidden' name='ID' value='$key'>
>>>>>> +                                                              
>>>>>>  <input type='hidden' name='ACTION'
>>>>>> value='$Lang::tr{'remove'}'>
>>>>>> +                                                       </form>
>>>>>> +                                               </td>
>>>>>> +                                       </tr>
>>>>>> +END
>>>>>> +                               }
>>>>>> +                       } else {
>>>>>> +                               # Print notice that currently
>>>>>> no ports are redirected.
>>>>>> +                               print "<tr>\n";
>>>>>> +                               print "<td class='base'
>>>>>> colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
>>>>>> +                               print "</tr>\n";
>>>>>> +                       }
>>>>>> +
>>>>>> +               print "</table>\n";
>>>>>> +
>>>>>> +       # Section to add new elements or edit existing ones.
>>>>>> +       print <<END;
>>>>>> +       <br>
>>>>>> +       <hr>
>>>>>> +       <br>
>>>>>> +       <div align='center'>
>>>>>> +               <table width='100%' cellspacing='0' border='0'>
>>>>>> +END
>>>>>> +
>>>>>> +       # Assign correct headline and button text.
>>>>>> +       my $buttontext;
>>>>>> +       my $entry_interface;
>>>>>> +       my $entry_protocol;
>>>>>> +       my $entry_port;
>>>>>> +       my $entry_address;
>>>>>> +       my $entry_remark;
>>>>>> +
>>>>>> +       # Check if an ID (key) has been given, in this case an
>>>>>> existing entry should be edited.
>>>>>> +       if ($settings{'ID'} ne '') {
>>>>>> +               $buttontext = $Lang::tr{'update'};
>>>>>> +               print "<tr><td class='boldbase'
>>>>>> colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
>>>>>> +
>>>>>> +               # Grab address and remark for the given key.
>>>>>> +               $entry_interface =
>>>>>> $redirects{$settings{'ID'}}[0];
>>>>>> +               $entry_protocol =
>>>>>> $redirects{$settings{'ID'}}[1];
>>>>>> +               $entry_port = $redirects{$settings{'ID'}}[2];
>>>>>> +               $entry_address =
>>>>>> $redirects{$settings{'ID'}}[3];
>>>>>> +               $entry_remark = $redirects{$settings{'ID'}}[5];
>>>>>> +
>>>>>> +       } else {
>>>>>> +               $buttontext = $Lang::tr{'add'};
>>>>>> +               print "<tr><td class='boldbase'
>>>>>> colspan='11'><b>$Lang::tr{'dnsforward add a new
>>>>>> entry'}</b></td></tr>\n";
>>>>>> +               print "<tr><td>&nbsp</td></tr>\n";
>>>>>> +       }
>>>>>> +
>>>>>> +       print <<END;
>>>>>> +                       <tr>
>>>>>> +                               <td class='base' width='1%' 
>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>> +                               <td class='base' width='15%'
>>>>>> bgcolor='$color{'color22'}'
>>>>>> align='left'><b>$Lang::tr{'interface'}</b></td>
>>>>>> +                               <td class='base' width='10%'
>>>>>> bgcolor='$color{'color22'}'
>>>>>> align='left'><b>$Lang::tr{'protocol'}</b></td>
>>>>>> +                               <td class='base' width='10%'
>>>>>> bgcolor='$color{'color22'}'
>>>>>> align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
>>>>>> +                               <td class='base' width='13%'
>>>>>> bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'ip
>>>>>> address'}</b></td>
>>>>>> +                               <td class='base' width='30%'
>>>>>> bgcolor='$color{'color22'}'
>>>>>> align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
>>>>>> +                               <td class='base' width='15%'
>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>> +                               <td class='base' width='1%' 
>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>> +                       </tr>
>>>>>> +
>>>>>> +                       <form method='post'
>>>>>> action='$ENV{'SCRIPT_NAME'}'>
>>>>>> +                       <input type='hidden' name='ID'
>>>>>> value='$settings{'ID'}'>
>>>>>> +                       <tr>
>>>>>> +                               <td class='base'></td>
>>>>>> +                               <td><select style='width:90px;'
>>>>>> id='interface' name='REDIR_ENTRY_INTERFACE'>
>>>>>> +END
>>>>>> +                               if ($netsettings{'GREEN_DEV'})
>>>>>> {
>>>>>> +                                       if ($entry_interface eq
>>>>>> "green") {
>>>>>> +                                               print "<option
>>>>>> value='green' selected='selected'
>>>>>> {'green'}>$Lang::tr{'green'}</option>";
>>>>>> +                                       } else { print "<option
>>>>>> value='green' {'green'}>$Lang::tr{'green'}</option>";}
>>>>>> +                               }
>>>>>> +                               if ($netsettings{'BLUE_DEV'}) {
>>>>>> +                                       if ($entry_interface eq
>>>>>> "blue") { 
>>>>>> +                                               print "<option
>>>>>> value='blue' selected='selected'
>>>>>> {'blue'}>$Lang::tr{'blue'}</option>";
>>>>>> +                                       } else { print "<option
>>>>>> value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
>>>>>> +                               }
>>>>>> +                               if ($netsettings{'ORANGE_DEV'})
>>>>>> {
>>>>>> +                                       if ($entry_interface eq
>>>>>> "orange") { 
>>>>>> +                                               print "<option
>>>>>> value='orange' selected='selected'
>>>>>> {'orange'}>$Lang::tr{'orange'}</option>";
>>>>>> +                                       } else { print "<option
>>>>>> value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
>>>>>> +                               }
>>>>>> +
>>>>>> +                       print "</select><td><select
>>>>>> style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
>>>>>> +                       if ((!$entry_protocol) ||
>>>>>> ($entry_protocol eq "tcp")) {
>>>>>> +                               print "<option
>>>>>> selected='selected' id='protocol' value='tcp'
>>>>>> {'tcp'}>tcp</option>";
>>>>>> +                               print "<option value='udp'
>>>>>> {'udp'}>udp</option>";
>>>>>> +                       } elsif ($entry_protocol eq "udp") {
>>>>>> +                               print "<option value='tcp'
>>>>>> {'tcp'}>tcp</option>";
>>>>>> +                               print "<option
>>>>>> selected='selected' value='udp' {'udp'}>udp</option>";
>>>>>> +                       }
>>>>>> +       print <<END;
>>>>>> +                               </select></td>
>>>>>> +                               <td><input type='text'
>>>>>> name='REDIR_ENTRY_PORT'    value='$entry_port'   
>>>>>> size='4'></td>
>>>>>> +                               <td><input type='text'
>>>>>> name='REDIR_ENTRY_ADDRESS' value='$entry_address'
>>>>>> size='14'></td>
>>>>>> +                               <td><input type='text'
>>>>>> name='REDIR_ENTRY_REMARK'  value='$entry_remark' 
>>>>>> size='35'></td>
>>>>>> +                               <td width='10%'
>>>>>> align='center'><input type='submit' name='ACTION' value=' 
>>>>>> $buttontext  '></td>
>>>>>> +                               <td class='base'></td>
>>>>>> +                       </tr>
>>>>>> +                       </form>
>>>>>> +               </table>
>>>>>> +       </div>
>>>>>> +END
>>>>>> +       &Header::closebox();
>>>>>> +}
>>>>>> diff --git a/config/rootfiles/common/misc-progs
>>>>>> b/config/rootfiles/common/misc-progs
>>>>>> index d6594b3f8..fbad2af8b 100644
>>>>>> --- a/config/rootfiles/common/misc-progs
>>>>>> +++ b/config/rootfiles/common/misc-progs
>>>>>> @@ -17,6 +17,7 @@ usr/local/bin/logwatch
>>>>>> #usr/local/bin/mpfirectrl
>>>>>> usr/local/bin/openvpnctrl
>>>>>> usr/local/bin/pakfire
>>>>>> +#usr/local/bin/portredirctrl
>>>>>> usr/local/bin/qosctrl
>>>>>> usr/local/bin/rebuildhosts
>>>>>> usr/local/bin/rebuildroutes
>>>>>> diff --git a/config/rootfiles/packages/portredir
>>>>>> b/config/rootfiles/packages/portredir
>>>>>> new file mode 100644
>>>>>> index 000000000..4b4ba8366
>>>>>> --- /dev/null
>>>>>> +++ b/config/rootfiles/packages/portredir
>>>>>> @@ -0,0 +1,11 @@
>>>>>> +etc/rc.d/init.d/portredir
>>>>>> +etc/rc.d/rc0.d/K77portredir
>>>>>> +etc/rc.d/rc3.d/S23portredir
>>>>>> +etc/rc.d/rc6.d/K77portredir
>>>>>> +srv/web/ipfire/cgi-bin/portredir.cgi
>>>>>> +usr/local/bin/portredirctrl
>>>>>> +var/ipfire/addon-lang/portredir.de.pl
>>>>>> +var/ipfire/addon-lang/portredir.en.pl
>>>>>> +var/ipfire/backup/addons/includes/portredir
>>>>>> +var/ipfire/menu.d/EX-portredir.menu
>>>>>> +var/ipfire/portredir
>>>>>> diff --git a/lfs/portredir b/lfs/portredir
>>>>>> new file mode 100644
>>>>>> index 000000000..a4911f71f
>>>>>> --- /dev/null
>>>>>> +++ b/lfs/portredir
>>>>>> @@ -0,0 +1,85 @@
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# IPFire.org - A linux based
>>>>>> firewall                                         #
>>>>>> +# Copyright (C) 2007-2021  IPFire Team 
>>>>>> <info@ipfire.org>                     #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# This program is free software: you can redistribute it
>>>>>> and/or modify        #
>>>>>> +# it under the terms of the GNU General Public License as
>>>>>> published by        #
>>>>>> +# the Free Software Foundation, either version 3 of the
>>>>>> License, or           #
>>>>>> +# (at your option) any later
>>>>>> version.                                         #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# This program is distributed in the hope that it will be
>>>>>> useful,             #
>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>> of              #
>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>> the               #
>>>>>> +# GNU General Public License for more
>>>>>> details.                                #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +# You should have received a copy of the GNU General Public
>>>>>> License           #
>>>>>> +# along with this program.  If not, see <
>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>> +#                                                             
>>>>>>                 #
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +# Definitions
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +
>>>>>> +include Config
>>>>>> +
>>>>>> +VER        = 1.0
>>>>>> +
>>>>>> +THISAPP    = portredir-$(VER)
>>>>>> +DIR_APP    = $(DIR_SRC)/$(THISAPP)
>>>>>> +TARGET     = $(DIR_INFO)/$(THISAPP)
>>>>>> +PROG       = portredir
>>>>>> +PAK_VER    = 1
>>>>>> +
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +# Top-level Rules
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +
>>>>>> +install : $(TARGET)
>>>>>> +
>>>>>> +check :
>>>>>> +
>>>>>> +download :
>>>>>> +
>>>>>> +md5 :
>>>>>> +
>>>>>> +dist: 
>>>>>> +       @$(PAK)
>>>>>> +
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +# Installation Details
>>>>>> +##############################################################
>>>>>> #################
>>>>>> +
>>>>>> +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
>>>>>> +       @$(PREBUILD)
>>>>>> +       @rm -rf $(DIR_APP) && cd $(DIR_SRC)
>>>>>> +
>>>>>> +       #install cgi 
>>>>>> +       install -v -m 755 $(DIR_CONF)/portredir/portredir.cgi
>>>>>> /srv/web/ipfire/cgi-bin/
>>>>>> +
>>>>>> +       #create configuration dir 
>>>>>> +       -mkdir -pv /var/ipfire/portredir/
>>>>>> +       chown -R nobody:nobody /var/ipfire/portredir/
>>>>>> +
>>>>>> +       # Install include file for backup
>>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/portredir-
>>>>>> backup /var/ipfire/backup/addons/includes/portredir
>>>>>> +
>>>>>> +       # Install menu file
>>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/EX-
>>>>>> portredir.menu /var/ipfire/menu.d/
>>>>>> +       chown nobody:nobody /var/ipfire/menu.d/EX-
>>>>>> portredir.menu
>>>>>> +
>>>>>> +       # Install addon-specific language-files
>>>>>> +       install -v -m 644
>>>>>> $(DIR_CONF)/portredir/lang/portredir.*.pl /var/ipfire/addon-
>>>>>> lang/
>>>>>> +
>>>>>> +       #install initscripts
>>>>>> +       $(call INSTALL_INITSCRIPT,portredir)
>>>>>> +
>>>>>> +       # Create symlinks for runlevel interaction.
>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>> /etc/rc.d/rc3.d/S23portredir
>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>> /etc/rc.d/rc0.d/K77portredir
>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>> /etc/rc.d/rc6.d/K77portredir
>>>>>> +
>>>>>> +       @rm -rf $(DIR_APP)
>>>>>> +       @$(POSTBUILD)
>>>>>> +
>>>>>> diff --git a/make.sh b/make.sh
>>>>>> index fc03ebcd5..ab9fe881a 100755
>>>>>> --- a/make.sh
>>>>>> +++ b/make.sh
>>>>>> @@ -1623,6 +1623,7 @@ buildipfire() {
>>>>>>  lfsmake2 socat
>>>>>>  lfsmake2 libcdada
>>>>>>  lfsmake2 pmacct
>>>>>> +  lfsmake2 portredir
>>>>>> }
>>>>>> 
>>>>>> buildinstaller() {
>>>>>> diff --git a/src/initscripts/packages/portredir
>>>>>> b/src/initscripts/packages/portredir
>>>>>> new file mode 100644
>>>>>> index 000000000..cc57fb9cc
>>>>>> --- /dev/null
>>>>>> +++ b/src/initscripts/packages/portredir
>>>>>> @@ -0,0 +1,191 @@
>>>>>> +#!/bin/sh
>>>>>> +##############################################################
>>>>>> ##########
>>>>>> +# Begin $rc_base/init.d/portredir
>>>>>> +#
>>>>>> +# Description : portredir init script for DNS/NTP and custom 
>>>>>> +#              port redirection rules
>>>>>> +#
>>>>>> +##############################################################
>>>>>> ##########
>>>>>> +
>>>>>> +. /etc/sysconfig/rc
>>>>>> +. ${rc_functions}
>>>>>> +
>>>>>> +IPT="/sbin/iptables";
>>>>>> +parent_chain="PREROUTING";
>>>>>> +chain="PORT_REDIRECT";
>>>>>> +
>>>>>> +confdir="/var/ipfire/portredir";
>>>>>> +settingsfile="${confdir}/settings";
>>>>>> +redirectsfile="${confdir}/redirects";
>>>>>> +SYSLOG="NO";
>>>>>> +VERBOSE="NO";
>>>>>> +
>>>>>> +eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings);
>>>>>> +eval $(/usr/local/bin/readhash ${settingsfile});
>>>>>> +
>>>>>> +logtext() {
>>>>>> +       if [ "${SYSLOG}" = "YES" ]; then logger -t "portredir"
>>>>>> ${1}; fi;
>>>>>> +       if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
>>>>>> +
>>>>>> +create_chain() {
>>>>>> +
>>>>>> +       local line=$(${IPT} -t nat -L ${parent_chain} --line-
>>>>>> numbers |grep "SQUID" |awk '{printf($1)}');
>>>>>> +
>>>>>> +       if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z
>>>>>> "${REDIR_ENABLE_ADDON}" ]]; then
>>>>>> +               logtext "addon not enabled in web
>>>>>> interface...";
>>>>>> +               echo "Portredir addon not enabled in web
>>>>>> interface...";
>>>>>> +               exit 0;
>>>>>> +       fi;
>>>>>> +
>>>>>> +       if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>>> ${chain})" ]; then
>>>>>> +               ${IPT} -t nat -N ${chain};
>>>>>> +
>>>>>> +               if [ ! -z "${line}" ]; then
>>>>>> +                       logtext "create chain ${chain} and link
>>>>>> in ${parent_chain} at position ${line}...";
>>>>>> +                       ${IPT} -t nat -I ${parent_chain}
>>>>>> ${line} -j ${chain};
>>>>>> +               else
>>>>>> +                       logtext "create chain ${chain} and link
>>>>>> in ${parent_chain} at last position...";
>>>>>> +                       ${IPT} -t nat -A ${parent_chain} -j
>>>>>> ${chain};
>>>>>> +               fi
>>>>>> +       else
>>>>>> +               return 1;
>>>>>> +       fi;
>>>>>> +       return 0;
>>>>>> +}
>>>>>> +
>>>>>> +remove_chain() {
>>>>>> +       if [ ! -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>>> ${chain})" ]; then
>>>>>> +               logtext "remove chain ${chain} and link in
>>>>>> ${parent_chain} from system...";
>>>>>> +               ${IPT} -t nat -D "${parent_chain}" -j ${chain};
>>>>>> +               ${IPT} -t nat -F ${chain};
>>>>>> +               ${IPT} -t nat -X ${chain};
>>>>>> +       else
>>>>>> +               return 1;
>>>>>> +       fi;
>>>>>> +       return 0;
>>>>>> +}
>>>>>> +
>>>>>> +activate_custom_redirections() {
>>>>>> +       
>>>>>> +       local array=();
>>>>>> +       local redirects=();
>>>>>> +       local i;
>>>>>> +       index=();
>>>>>> +       iface=();
>>>>>> +       protocol=();
>>>>>> +       port=();
>>>>>> +       targetip=();
>>>>>> +       enabled=();
>>>>>> +
>>>>>> +       IFS=$'\n' read -d '' -ra redirects < ${redirectsfile};
>>>>>> +
>>>>>> +       for i in "${!redirects[@]}"
>>>>>> +       do
>>>>>> +               IFS=$',' read -ra array <<< ${redirects[i]};
>>>>>> +               index[i]=${array[0]};
>>>>>> +               iface[i]=${array[1]};
>>>>>> +               protocol[i]=${array[2]};
>>>>>> +               port[i]=${array[3]};
>>>>>> +               targetip[i]=${array[4]};
>>>>>> +               enabled[i]=${array[5]};
>>>>>> +       done
>>>>>> +
>>>>>> +       for i in "${!index[@]}"
>>>>>> +       do
>>>>>> +               if [[ ! -z "${GREEN_DEV}" &&  "${iface[i]}" =
>>>>>> "green" && "${enabled[i]}" = "enabled" ]]; then
>>>>>> +
>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>> ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>> ${port[i]} ";
>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>> ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>> ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>> ${port[i]} -j REDIRECT;
>>>>>> +               fi
>>>>>> +               if [[ ! -z "${BLUE_DEV}" &&  "${iface[i]}" =
>>>>>> "blue" && "${enabled[i]}" = "enabled" ]]; then
>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>> ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>> ${port[i]} ";
>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>> ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>> ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>> ${port[i]} -j REDIRECT;
>>>>>> +               fi
>>>>>> +               if [[ ! -z "${ORANGE_DEV}" &&  "${iface[i]}" =
>>>>>> "orange" && "${enabled[i]}" = "enabled" ]]; then
>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>> ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>> ${port[i]} ";
>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>> ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>> ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>> ${port[i]} -j REDIRECT;
>>>>>> +               fi
>>>>>> +       done
>>>>>> +       unset array redirects i index iface protocol port
>>>>>> targetip enabled;
>>>>>> +       return 0;
>>>>>> +}
>>>>>> +
>>>>>> +activate_redirections() {
>>>>>> +
>>>>>> +       if ! create_chain; then return 1; fi;
>>>>>> +       
>>>>>> +       # Force DNS REDIRECTs on GREEN (udp, tcp, 53)
>>>>>> +       if [[ "${REDIR_DNS_GREEN}" == "on" && 
>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>> udp -m udp --dport domain -j REDIRECT;
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>> ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>> tcp -m tcp --dport domain -j REDIRECT;
>>>>>> +       fi
>>>>>> +
>>>>>> +       # Force DNS REDIRECTs on BLUE (udp, tcp, 53)
>>>>>> +       if [[ "${REDIR_DNS_BLUE}" == "on" && 
>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>>> -m udp --dport domain -j REDIRECT
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>> ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p tcp
>>>>>> -m tcp --dport domain -j REDIRECT
>>>>>> +       fi
>>>>>> +
>>>>>> +       # Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
>>>>>> +       if [[ "${REDIR_DNS_ORANGE}" == "on" && 
>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>> udp -m udp --dport domain -j REDIRECT
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>> ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>> tcp -m tcp --dport domain -j REDIRECT
>>>>>> +       fi
>>>>>> +
>>>>>> +       # Force NTP REDIRECTs on GREEN (udp, 123)
>>>>>> +       if [[ "${REDIR_NTP_GREEN}" == "on" && 
>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>> +       fi
>>>>>> +
>>>>>> +       # Force NTP REDIRECTs on BLUE (udp, 123)
>>>>>> +       if [[ "${REDIR_NTP_BLUE}" == "on" && 
>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>>> -m udp --dport ntp -j REDIRECT
>>>>>> +       fi
>>>>>> +
>>>>>> +       # Force NTP REDIRECTs on ORANGE (udp, 123)
>>>>>> +       if [[ "${REDIR_NTP_ORANGE}" == "on" && 
>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>> +       fi
>>>>>> +
>>>>>> +       if ! activate_custom_redirections; then return 1; fi;
>>>>>> +
>>>>>> +       return 0;
>>>>>> +}
>>>>>> +
>>>>>> +case "${1}" in
>>>>>> +       start)
>>>>>> +               boot_mesg "Loading port redirections..."
>>>>>> +               activate_redirections;
>>>>>> +               evaluate_retval;
>>>>>> +               ;;
>>>>>> +
>>>>>> +       stop)   
>>>>>> +               boot_mesg "Removing port redirections..."
>>>>>> +               remove_chain;
>>>>>> +               evaluate_retval;
>>>>>> +               ;;
>>>>>> +
>>>>>> +       restart)
>>>>>> +               ${0} stop
>>>>>> +               ${0} start
>>>>>> +               ;;
>>>>>> +
>>>>>> +       *)
>>>>>> +               echo "Usage: ${0} {start|stop|restart}"
>>>>>> +               exit 1
>>>>>> +               ;;
>>>>>> +esac
>>>>>> +
>>>>>> +# End $rc_base/init.d/portredir
>>>>>> diff --git a/src/misc-progs/Makefile b/src/misc-progs/Makefile
>>>>>> index 7c3ef7529..850f8fdcc 100644
>>>>>> --- a/src/misc-progs/Makefile
>>>>>> +++ b/src/misc-progs/Makefile
>>>>>> @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
>>>>>>         wirelessctrl getipstat qosctrl \
>>>>>>         redctrl syslogdctrl extrahdctrl sambactrl \
>>>>>>         smartctrl clamavctrl addonctrl pakfire mpfirectrl
>>>>>> wlanapctrl \
>>>>>> -       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>>> rebuildroutes \
>>>>>> +       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>>> rebuildroutes portredirctrl \
>>>>>>         getconntracktable wirelessclient torctrl ddnsctrl
>>>>>> unboundctrl \
>>>>>>         captivectrl
>>>>>> 
>>>>>> diff --git a/src/misc-progs/portredirctrl.c b/src/misc-
>>>>>> progs/portredirctrl.c
>>>>>> new file mode 100644
>>>>>> index 000000000..7897d711c
>>>>>> --- /dev/null
>>>>>> +++ b/src/misc-progs/portredirctrl.c
>>>>>> @@ -0,0 +1,47 @@
>>>>>> +/* This file is part of the IPFire Firewall.
>>>>>> + *
>>>>>> + * This program is distributed under the terms of the GNU
>>>>>> General Public
>>>>>> + * Licence.  See the file COPYING for details.
>>>>>> + *
>>>>>> + */
>>>>>> +
>>>>>> +#include <stdlib.h>
>>>>>> +#include <stdio.h>
>>>>>> +#include <string.h>
>>>>>> +#include <unistd.h>
>>>>>> +#include <sys/types.h>
>>>>>> +#include <fcntl.h>
>>>>>> +#include "setuid.h"
>>>>>> +
>>>>>> +int main(int argc, char *argv[]) {
>>>>>> +       if (!(initsetuid()))
>>>>>> +               exit(1);
>>>>>> +
>>>>>> +       // Check what command is asked
>>>>>> +        if (argc < 2) {
>>>>>> +                fprintf(stderr, "\nNo argument
>>>>>> given.\n\nportredirctrl
>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>> +                exit(1);
>>>>>> +        }
>>>>>> +
>>>>>> +        if (strcmp(argv[1], "start") == 0) {
>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>> start");
>>>>>> +        } else if (strcmp(argv[1], "stop") == 0) {
>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>> stop");
>>>>>> +        } else if (strcmp(argv[1], "restart") == 0) {
>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>> restart");
>>>>>> +       } else if (strcmp(argv[1], "enable") == 0) {
>>>>>> +               safe_system("touch
>>>>>> /var/ipfire/portredir/enable");
>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>> /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>> /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>> /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
>>>>>> +       } else if (strcmp(argv[1], "disable") == 0) {
>>>>>> +               safe_system("/etc/rc.d/init.d/portredir stop");
>>>>>> +               safe_system("unlink
>>>>>> /var/ipfire/portredir/enable");
>>>>>> +               safe_system("rm -rf /etc/rc.d/rc*.d/*portredir
>>>>>>> /dev/null 2>&1");
>>>>>> +        } else {
>>>>>> +                fprintf(stderr, "\nBad argument
>>>>>> given.\n\nportredirctrl
>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>> +                exit(1);
>>>>>> +        }
>>>>>> +
>>>>>> +       return 0;
>>>>>> +}
>>>>>> diff --git a/src/paks/portredir/install.sh
>>>>>> b/src/paks/portredir/install.sh
>>>>>> new file mode 100644
>>>>>> index 000000000..9f69aeae2
>>>>>> --- /dev/null
>>>>>> +++ b/src/paks/portredir/install.sh
>>>>>> @@ -0,0 +1,32 @@
>>>>>> +#!/bin/bash
>>>>>> +##############################################################
>>>>>> ##############
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# This file is part of the IPFire
>>>>>> Firewall.                                #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>> modify           #
>>>>>> +# it under the terms of the GNU General Public License as
>>>>>> published by     #
>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>> License, or        #
>>>>>> +# (at your option) any later
>>>>>> version.                                      #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>> useful,                #
>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>> of           #
>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>> the            #
>>>>>> +# GNU General Public License for more
>>>>>> details.                             #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# You should have received a copy of the GNU General Public
>>>>>> License        #
>>>>>> +# along with IPFire; if not, write to the Free
>>>>>> Software                    #
>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>> 02111-1307 USA #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# Copyright (C) 2021 IPFire-Team
>>>>>> <info@ipfire.org>.                        #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +##############################################################
>>>>>> ##############
>>>>>> +#
>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>> +extract_files
>>>>>> +restore_backup ${NAME}
>>>>>> +
>>>>>> +/usr/local/bin/update-lang-cache
>>>>>> +
>>>>>> +chown root:nobody /usr/local/bin/portredirctrl
>>>>>> +chmod 4750 /usr/local/bin/portredirctrl
>>>>>> +chmod u+s /usr/local/bin/portredirctrl
>>>>>> diff --git a/src/paks/portredir/uninstall.sh
>>>>>> b/src/paks/portredir/uninstall.sh
>>>>>> new file mode 100644
>>>>>> index 000000000..df9270125
>>>>>> --- /dev/null
>>>>>> +++ b/src/paks/portredir/uninstall.sh
>>>>>> @@ -0,0 +1,28 @@
>>>>>> +#!/bin/bash
>>>>>> +##############################################################
>>>>>> ##############
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# This file is part of the IPFire
>>>>>> Firewall.                                #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>> modify           #
>>>>>> +# it under the terms of the GNU General Public License as
>>>>>> published by     #
>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>> License, or        #
>>>>>> +# (at your option) any later
>>>>>> version.                                      #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>> useful,                #
>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>> of           #
>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>> the            #
>>>>>> +# GNU General Public License for more
>>>>>> details.                             #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# You should have received a copy of the GNU General Public
>>>>>> License        #
>>>>>> +# along with IPFire; if not, write to the Free
>>>>>> Software                    #
>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>> 02111-1307 USA #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>> <info@ipfire.org>.                        #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +##############################################################
>>>>>> ##############
>>>>>> +#
>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>> +make_backup ${NAME}
>>>>>> +remove_files
>>>>>> +
>>>>>> +/usr/local/bin/update-lang-cache
>>>>>> diff --git a/src/paks/portredir/update.sh
>>>>>> b/src/paks/portredir/update.sh
>>>>>> new file mode 100644
>>>>>> index 000000000..89c40d0d7
>>>>>> --- /dev/null
>>>>>> +++ b/src/paks/portredir/update.sh
>>>>>> @@ -0,0 +1,26 @@
>>>>>> +#!/bin/bash
>>>>>> +##############################################################
>>>>>> ##############
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# This file is part of the IPFire
>>>>>> Firewall.                                #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# IPFire is free software; you can redistribute it and/or
>>>>>> modify           #
>>>>>> +# it under the terms of the GNU General Public License as
>>>>>> published by     #
>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>> License, or        #
>>>>>> +# (at your option) any later
>>>>>> version.                                      #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>> useful,                #
>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty
>>>>>> of           #
>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>>>>>> the            #
>>>>>> +# GNU General Public License for more
>>>>>> details.                             #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# You should have received a copy of the GNU General Public
>>>>>> License        #
>>>>>> +# along with IPFire; if not, write to the Free
>>>>>> Software                    #
>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>> 02111-1307 USA #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>> <info@ipfire.org>.                        #
>>>>>> +#                                                             
>>>>>>              #
>>>>>> +##############################################################
>>>>>> ##############
>>>>>> +#
>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>> +./uninstall.sh
>>>>>> +./install.sh
>>>>>> -- 
>>>>>> 2.18.0
>>>>>> 
>>>>> 
>>>> 
>>> 
>> 
>
  
Stefan Schantl Aug. 6, 2021, 3:57 p.m. UTC | #15
Am Donnerstag, dem 05.08.2021 um 22:33 +0200 schrieb Michael Tremer:
> Hello,
> 
> > On 3 Aug 2021, at 18:04, Jon Murphy <jcmurphy26@gmail.com> wrote:
> > 
> > Hello all!
> > 
> > I’m having trouble following the changes to firewall.cgi.  I think
> > there were 4 or 5 patches 
> > submitted to the Dev Mailing List.
> > 
> > Did these get approved?
> 
> That is a good question. AFAIK they are not in c159.
> 
> > 
> > I looked at the Changelog for Core 158 and core 159 (testing) but I
> > didn’t see anything:
> > 
> > https://nightly.ipfire.org/core158/2021-07-21%2015:47:35%20+0000-23498c61/x86_64/changelog.txt
> > 
> > https://nightly.ipfire.org/master/latest/x86_64/changelog.txt
> > 
> > 
> > Can they be added to CU 159?
> > 
> > To be honest I am mainly interested in the DNS redirect (and its
> > cousin NTP redirect).
> > 
> > I’d be happy to help test but I do need some help applying the
> > patches.  
> > Right now I apply them by grabbing a copy of firewall.cgi and
> > manually deleting and then manually adding a patch.
> > Not the smartest way but it works (if I don’t screw-up and make a
> > mistake!).
> 
> @Stefan: What is the status on this?


All firewall related patches, pending on the list, safely can be merged
and tested by a bigger group of testers.

- Stefan
> 
> -Michael
> 
> > Jon
> > 
> > > On Jun 30, 2021, at 2:14 PM, Stefan Schantl <
> > > stefan.schantl@ipfire.org> wrote:
> > > 
> > > Hello Matthias, Hello Michael, Hello Jon, Hello *,
> > > 
> > > I've followed the conversation on this list since the first mail
> > > and
> > > thoughts about forcing DNS traffic to use the local resolver.
> > > 
> > > It was a very long journey and lot of time and work has been
> > > spent to
> > > get to the present point.
> > > 
> > > As Michael requested here, I've digged through the lines of the
> > > perl
> > > script which is responsible for creating the firewall rules and
> > > surprisingly found that everyting which is needed to create
> > > generic
> > > REDIRECT rules already was written in the past - it just did not
> > > work
> > > as designed/expected.
> > > 
> > > Finaly I was able to adjust these lines of code and to repair
> > > that
> > > feature.
> > > 
> > > A redirect rule can be created by picking a single host or group
> > > of
> > > hosts or entire network(s) as source, selecting NAT (DNAT) and
> > > choosing
> > > the Firewall itself as target.
> > > 
> > > The protocol or service or service group which should be
> > > redirected has
> > > to be selected afterwards. If you want to redirect a given port
> > > to
> > > another one it can be specified as "Target port".
> > > 
> > > All created redirect rules are displayed as "input rules".
> > > 
> > > 
> > > The patch directly can be accessed here:
> > > 
> > > https://patchwork.ipfire.org/project/ipfire/patch/20210630184031.7726-1-stefan.schantl@ipfire.org/
> > > 
> > > Best regards,
> > > 
> > > -Stefan
> > > 
> > > > Hello,
> > > > 
> > > > > On 28 Jun 2021, at 18:53, Jon Murphy <jcmurphy26@gmail.com>
> > > > > wrote:
> > > > > 
> > > > > Hi Michael!  Happy Monday!
> > > > > 
> > > > > 
> > > > > > Why do we not extend the firewall UI probably by about 20
> > > > > > lines
> > > > > > of code instead of adding many hundreds of lines?
> > > > > > 
> > > > > > Please can someone elaborate on this more?
> > > > > 
> > > > > Doing a DNS redirect, via the WegBUI, has been an issue since
> > > > > 2015.  I found this quote in the old forum:
> > > > > 
> > > > > "Having investigated a bit more I have concluded that it's
> > > > > not
> > > > > currently possible to create such rules through the WUI.
> > > > > 
> > > > > There are a number of obstacles:
> > > > > 1. It is not allowed to create a rule where source IP and
> > > > > destination nat IP is on the same subnetwork (e.g. GREEN),
> > > > > WUI
> > > > > error message: "Source and destination IP addresses are from
> > > > > the
> > > > > same subnet."
> > > > > 
> > > > > 2. WUI will not allow you to create a rule without a
> > > > > destination
> > > > > (the filtered packet must adhere to a destination, not only a
> > > > > port)
> > > > > and the destination MUST be an IP address of one of the
> > > > > IPFire
> > > > > interfaces, which limits whats possible a great deal." 
> > > > 
> > > > And these cannot be changed?
> > > > 
> > > > > And I found this from 2016:
> > > > > https://bugzilla.ipfire.org/show_bug.cgi?id=11168
> > > > > 
> > > > > So I am guessing that no one has been able to determine a way
> > > > > to
> > > > > extend the WebGUI.  
> > > > 
> > > > Has anyone tried? I do not see any obvious reasons why this
> > > > should
> > > > not be possible.
> > > > 
> > > > > I am curious - Who created the 
> > > > > https://ipfire:444/cgi-bin/firewall.cgi page?  And could they
> > > > > help?
> > > > 
> > > > -Michael
> > > > 
> > > > > Jon
> > > > > 
> > > > > 
> > > > > > On Jun 28, 2021, at 11:04 AM, Michael Tremer <
> > > > > > michael.tremer@ipfire.org> wrote:
> > > > > > 
> > > > > > Hello Matthias,
> > > > > > 
> > > > > > > On 27 Jun 2021, at 14:48, Matthias Fischer <
> > > > > > > matthias.fischer@ipfire.org> wrote:
> > > > > > > 
> > > > > > > From: Marcel Lorenz <marcel.lorenz@ipfire.org>
> > > > > > 
> > > > > > Thank you for sending this patch on Marcel’s behalf, but I
> > > > > > would
> > > > > > much more prefer if he would submit his patches on his own.
> > > > > > I do
> > > > > > not see why that isn’t possible.
> > > > > > 
> > > > > > > Please note:
> > > > > > > This is a new addon written by Marcel Lorenz <
> > > > > > > marcel.lorenz@ipfire.org>.
> > > > > > > 
> > > > > > > It adds a new GUI to IPFire for DNS/NTP *and* user
> > > > > > > specific
> > > > > > > port redirections.
> > > > > > > 
> > > > > > > How its working:
> > > > > > > It has exactly the same functionalities as "Forcing
> > > > > > > DNS/NTP..."  - and some more.
> > > > > > > 
> > > > > > > By setting switches, DNS/NTP requests are automatically
> > > > > > > redirected to the local IPFire DNS/NTP servers.
> > > > > > > 
> > > > > > > Additionally, the user can specify custom redirections.
> > > > > > > 
> > > > > > > These rules are added to a new chain in PREROUTING =>
> > > > > > > PORT_REDIRECT.
> > > > > > > 
> > > > > > > To avoid problems with (e.g.) transparent 'squid'
> > > > > > > configurations,
> > > > > > > redirection rules are added automatically before existing
> > > > > > > 'squid' rules.
> > > > > > 
> > > > > > This message does unfortunately not say why this add-on
> > > > > > would be
> > > > > > useful. I am emphasising this again and again that it is
> > > > > > not very
> > > > > > important how something is done specially. That should be
> > > > > > commented in the code and other implementation details
> > > > > > should
> > > > > > also be documented there.
> > > > > > 
> > > > > > As I have stated on this functionality many times before, I
> > > > > > do
> > > > > > not see why this is necessary at all.
> > > > > > 
> > > > > > Why is this an add-on?
> > > > > > 
> > > > > > Why do we not extend the firewall UI probably by about 20
> > > > > > lines
> > > > > > of code instead of adding many hundreds of lines?
> > > > > > 
> > > > > > Please can someone elaborate on this more?
> > > > > > 
> > > > > > -Michael
> > > > > > 
> > > > > > > Signed-off-by: Matthias Fischer <
> > > > > > > matthias.fischer@ipfire.org>
> > > > > > > ---
> > > > > > > config/portredir/EX-portredir.menu    |   6 +
> > > > > > > config/portredir/lang/portredir.de.pl |  19 +
> > > > > > > config/portredir/lang/portredir.en.pl |  19 +
> > > > > > > config/portredir/portredir-backup     |   1 +
> > > > > > > config/portredir/portredir.cgi        | 525
> > > > > > > ++++++++++++++++++++++++++
> > > > > > > config/rootfiles/common/misc-progs    |   1 +
> > > > > > > config/rootfiles/packages/portredir   |  11 +
> > > > > > > lfs/portredir                         |  85 +++++
> > > > > > > make.sh                               |   1 +
> > > > > > > src/initscripts/packages/portredir    | 191 ++++++++++
> > > > > > > src/misc-progs/Makefile               |   2 +-
> > > > > > > src/misc-progs/portredirctrl.c        |  47 +++
> > > > > > > src/paks/portredir/install.sh         |  32 ++
> > > > > > > src/paks/portredir/uninstall.sh       |  28 ++
> > > > > > > src/paks/portredir/update.sh          |  26 ++
> > > > > > > 15 files changed, 993 insertions(+), 1 deletion(-)
> > > > > > > create mode 100644 config/portredir/EX-portredir.menu
> > > > > > > create mode 100644 config/portredir/lang/portredir.de.pl
> > > > > > > create mode 100644 config/portredir/lang/portredir.en.pl
> > > > > > > create mode 100644 config/portredir/portredir-backup
> > > > > > > create mode 100644 config/portredir/portredir.cgi
> > > > > > > create mode 100644 config/rootfiles/packages/portredir
> > > > > > > create mode 100644 lfs/portredir
> > > > > > > create mode 100644 src/initscripts/packages/portredir
> > > > > > > create mode 100644 src/misc-progs/portredirctrl.c
> > > > > > > create mode 100644 src/paks/portredir/install.sh
> > > > > > > create mode 100644 src/paks/portredir/uninstall.sh
> > > > > > > create mode 100644 src/paks/portredir/update.sh
> > > > > > > 
> > > > > > > diff --git a/config/portredir/EX-portredir.menu
> > > > > > > b/config/portredir/EX-portredir.menu
> > > > > > > new file mode 100644
> > > > > > > index 000000000..8376e8053
> > > > > > > --- /dev/null
> > > > > > > +++ b/config/portredir/EX-portredir.menu
> > > > > > > @@ -0,0 +1,6 @@
> > > > > > > +    $subfirewall->{'95.portredir'} = {
> > > > > > > +                               'caption' =>
> > > > > > > $Lang::tr{'portredir port redirections'},
> > > > > > > +                               'uri' => '/cgi-
> > > > > > > bin/portredir.cgi',
> > > > > > > +                               'title' =>
> > > > > > > "$Lang::tr{'portredir port redirections'}",
> > > > > > > +                               'enabled' => 1
> > > > > > > +                               };
> > > > > > > diff --git a/config/portredir/lang/portredir.de.pl
> > > > > > > b/config/portredir/lang/portredir.de.pl
> > > > > > > new file mode 100644
> > > > > > > index 000000000..b932d4a85
> > > > > > > --- /dev/null
> > > > > > > +++ b/config/portredir/lang/portredir.de.pl
> > > > > > > @@ -0,0 +1,19 @@
> > > > > > > +%tr = (
> > > > > > > +%tr,
> > > > > > > +'portredir enable addon' => 'Addon aktivieren',
> > > > > > > +'portredir common settings' => 'Allgemeine
> > > > > > > Einstellungen',
> > > > > > > +'portredir port redirections' => 'Portumleitungen',
> > > > > > > +'portredir fw for interface' => 'Firewalloptionen für
> > > > > > > das
> > > > > > > Interface',
> > > > > > > +'portredir enable user redirections' => 'Aktiviere
> > > > > > > benutzerdefinierte Portumleitungen',
> > > > > > > +'portredir force local dns' => 'Erzwinge lokale DNS-
> > > > > > > Server',
> > > > > > > +'portredir force local ntp' => 'Erzwinge lokale NTP-
> > > > > > > Server',
> > > > > > > +'portredir custom redirections' => 'Benutzerdefinierte
> > > > > > > Portumleitungen',
> > > > > > > +'portredir remove rule' => 'Entferne Regel',
> > > > > > > +'portredir add rule' => 'Hinzufügen',
> > > > > > > +'portredir no entries' => 'Keine Einträge vorhanden.',
> > > > > > > +'portredir invalid address' => 'Ungültige Host-
> > > > > > > Addresse.',
> > > > > > > +'portredir empty input' => 'Fehlende Angabe: Bitte geben
> > > > > > > Sie
> > > > > > > einen gültigen Host an.',
> > > > > > > +'portredir save to activate' => 'Speichern, um
> > > > > > > Änderungen zu
> > > > > > > aktivieren',
> > > > > > > +);
> > > > > > > +
> > > > > > > +#EOF
> > > > > > > diff --git a/config/portredir/lang/portredir.en.pl
> > > > > > > b/config/portredir/lang/portredir.en.pl
> > > > > > > new file mode 100644
> > > > > > > index 000000000..f442f3eaa
> > > > > > > --- /dev/null
> > > > > > > +++ b/config/portredir/lang/portredir.en.pl
> > > > > > > @@ -0,0 +1,19 @@
> > > > > > > +%tr = (
> > > > > > > +%tr,
> > > > > > > +'portredir enable addon' => 'Enable addon',
> > > > > > > +'portredir common settings' => 'Common settings',
> > > > > > > +'portredir port redirections' => 'Port redirections',
> > > > > > > +'portredir fw for interface' => 'Firewall options for
> > > > > > > interface',
> > > > > > > +'portredir enable user redirections' => 'Enable user
> > > > > > > port
> > > > > > > redirections',
> > > > > > > +'portredir force local dns' => 'Enforce local DNS
> > > > > > > servers',
> > > > > > > +'portredir force local ntp' => 'Enforce local NTP
> > > > > > > servers',
> > > > > > > +'portredir custom redirections' => 'Custom port
> > > > > > > redirections',
> > > > > > > +'portredir remove rule' => 'Remove rule',
> > > > > > > +'portredir add rule' => 'Add new',
> > > > > > > +'portredir no entries' => 'No entries at the moment.',
> > > > > > > +'portredir invalid address' => 'Invalid host address.',
> > > > > > > +'portredir empty input' => 'Empty input: Please enter a
> > > > > > > valid
> > > > > > > host.',
> > > > > > > +'portredir save to activate' => 'Save to activate
> > > > > > > changes',
> > > > > > > +);
> > > > > > > +
> > > > > > > +#EOF
> > > > > > > diff --git a/config/portredir/portredir-backup
> > > > > > > b/config/portredir/portredir-backup
> > > > > > > new file mode 100644
> > > > > > > index 000000000..bd2ada742
> > > > > > > --- /dev/null
> > > > > > > +++ b/config/portredir/portredir-backup
> > > > > > > @@ -0,0 +1 @@
> > > > > > > +/var/ipfire/portredir
> > > > > > > diff --git a/config/portredir/portredir.cgi
> > > > > > > b/config/portredir/portredir.cgi
> > > > > > > new file mode 100644
> > > > > > > index 000000000..4913dda3f
> > > > > > > --- /dev/null
> > > > > > > +++ b/config/portredir/portredir.cgi
> > > > > > > @@ -0,0 +1,525 @@
> > > > > > > +#!/usr/bin/perl
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > #################
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >                 #
> > > > > > > +# IPFire.org - A linux based
> > > > > > > firewall                                         #
> > > > > > > +# Copyright (C) 2021  IPFire Team 
> > > > > > > <info@ipfire.org>                          #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >                 #
> > > > > > > +# This program is free software: you can redistribute it
> > > > > > > and/or modify        #
> > > > > > > +# it under the terms of the GNU General Public License
> > > > > > > as
> > > > > > > published by        #
> > > > > > > +# the Free Software Foundation, either version 3 of the
> > > > > > > License, or           #
> > > > > > > +# (at your option) any later
> > > > > > > version.                                         #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >                 #
> > > > > > > +# This program is distributed in the hope that it will
> > > > > > > be
> > > > > > > useful,             #
> > > > > > > +# but WITHOUT ANY WARRANTY; without even the implied
> > > > > > > warranty
> > > > > > > of              #
> > > > > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
> > > > > > > See
> > > > > > > the               #
> > > > > > > +# GNU General Public License for more
> > > > > > > details.                                #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >                 #
> > > > > > > +# You should have received a copy of the GNU General
> > > > > > > Public
> > > > > > > License           #
> > > > > > > +# along with this program.  If not, see <
> > > > > > > http://www.gnu.org/licenses/>.       #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >                 #
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > #################
> > > > > > > +
> > > > > > > +use strict;
> > > > > > > +
> > > > > > > +# enable only the following on debugging purpose
> > > > > > > +use warnings;
> > > > > > > +use CGI::Carp 'fatalsToBrowser';
> > > > > > > +
> > > > > > > +require '/var/ipfire/general-functions.pl';
> > > > > > > +require "${General::swroot}/lang.pl";
> > > > > > > +require "${General::swroot}/header.pl";
> > > > > > > +
> > > > > > > +# File declarations
> > > > > > > +my $settingsfile =
> > > > > > > "${General::swroot}/portredir/settings";
> > > > > > > +my $redirectsfile =
> > > > > > > "${General::swroot}/portredir/redirects";
> > > > > > > +
> > > > > > > +# Create empty settingsfiles if they does not exist yet
> > > > > > > +unless (-e "$settingsfile") { system ("touch
> > > > > > > $settingsfile");
> > > > > > > }
> > > > > > > +unless (-e "$redirectsfile") { system ("touch
> > > > > > > $redirectsfile"); }
> > > > > > > +
> > > > > > > +# load ipfire settings
> > > > > > > +our %netsettings = ();
> > > > > > > +our %color = ();
> > > > > > > +&General::readhash("${General::swroot}/ethernet/settings
> > > > > > > ",
> > > > > > > \%netsettings);
> > > > > > > +&General::readhash("/srv/web/ipfire/html/themes/ipfire/i
> > > > > > > nclude
> > > > > > > /colors.txt", \%color);
> > > > > > > +
> > > > > > > +my %settings=();
> > > > > > > +my %portredirs=();
> > > > > > > +my %checked=(); # Checkbox manipulations
> > > > > > > +my $errormessage='';
> > > > > > > +my %selected=();
> > > > > > > +our %redirects=();
> > > > > > > +
> > > > > > > +$settings{'ACTION'} = '';
> > > > > > > +$settings{'REDIR_ENABLE_ADDON'}="off";
> > > > > > > +$settings{'REDIR_CUSTOM_GREEN'}="off";
> > > > > > > +$settings{'REDIR_CUSTOM_BLUE'}="off";
> > > > > > > +$settings{'REDIR_CUSTOM_ORANGE'}="off";
> > > > > > > +$settings{'REDIR_DNS_GREEN'}="off";
> > > > > > > +$settings{'REDIR_NTP_GREEN'}="off";
> > > > > > > +$settings{'REDIR_DNS_BLUE'}="off";
> > > > > > > +$settings{'REDIR_NTP_BLUE'}="off";
> > > > > > > +$settings{'REDIR_DNS_ORANGE'}="off";
> > > > > > > +$settings{'REDIR_NTP_ORANGE'}="off";
> > > > > > > +
> > > > > > > +&Header::showhttpheaders();
> > > > > > > +
> > > > > > > +# Get GUI values
> > > > > > > +&Header::getcgihash(\%settings);
> > > > > > > +
> > > > > > > +# Save action
> > > > > > > +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
> > > > > > > +
> > > > > > > +       # If custom rules enabled, deactivate default
> > > > > > > rules on
> > > > > > > interface
> > > > > > > +       if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
> > > > > > > +               $settings{'REDIR_DNS_GREEN'}="off";
> > > > > > > +               $settings{'REDIR_NTP_GREEN'}="off";
> > > > > > > +       }
> > > > > > > +
> > > > > > > +       if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
> > > > > > > +               $settings{'REDIR_DNS_BLUE'}="off";
> > > > > > > +               $settings{'REDIR_NTP_BLUE'}="off";
> > > > > > > +       }
> > > > > > > +
> > > > > > > +       if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
> > > > > > > +               $settings{'REDIR_DNS_ORANGE'}="off";
> > > > > > > +               $settings{'REDIR_NTP_ORANGE'}="off";
> > > > > > > +       }
> > > > > > > +
> > > > > > > +       &General::writehash($settingsfile, \%settings);
> > > > > > > +
> > > > > > > +       if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
> > > > > > > +               system ('/usr/local/bin/portredirctrl
> > > > > > > restart
> > > > > > > > /dev/null 2>&1');
> > > > > > > +               system ('/usr/local/bin/portredirctrl
> > > > > > > enable
> > > > > > > > /dev/null 2>&1');
> > > > > > > +               &General::log('portredir addon: port
> > > > > > > redirections enabled');
> > > > > > > +       }
> > > > > > > +       if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
> > > > > > > +               system ('/usr/local/bin/portredirctrl
> > > > > > > disable
> > > > > > > > /dev/null 2>&1');
> > > > > > > +               system ('/usr/local/bin/portredirctrl
> > > > > > > stop
> > > > > > > > /dev/null 2>&1');
> > > > > > > +               &General::log('portredir addon: port
> > > > > > > redirections disabled');
> > > > > > > +       }
> > > > > > > +
> > > > > > > +# Add/edit an entry to the redirectsfile.
> > > > > > > +
> > > > > > > +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) ||
> > > > > > > ($settings{'ACTION'} eq $Lang::tr{'update'})) {
> > > > > > > +
> > > > > > > +       # Check if any input has been performed.
> > > > > > > +       if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
> > > > > > > +
> > > > > > > +               # Check if the given input is no valid
> > > > > > > IP-
> > > > > > > address, display an error message.
> > > > > > > +               if
> > > > > > > (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
> > > > > > > +                       $errormessage =
> > > > > > > "$Lang::tr{'portredir
> > > > > > > invalid address'}";
> > > > > > > +               }
> > > > > > > +       } else {
> > > > > > > +               $errormessage = "$Lang::tr{'portredir
> > > > > > > empty
> > > > > > > input'}";
> > > > > > > +       }
> > > > > > > +
> > > > > > > +       # Go further if there was no error.
> > > > > > > +       if ($errormessage eq '') {
> > > > > > > +               my %redirects = ();
> > > > > > > +               my $id;
> > > > > > > +               my $status;
> > > > > > > +
> > > > > > > +               # Assign hash values.
> > > > > > > +               my $new_entry_interface =
> > > > > > > $settings{'REDIR_ENTRY_INTERFACE'};
> > > > > > > +               my $new_entry_protocol =
> > > > > > > $settings{'REDIR_ENTRY_PROTOCOL'};
> > > > > > > +               my $new_entry_port =
> > > > > > > $settings{'REDIR_ENTRY_PORT'};
> > > > > > > +               my $new_entry_address =
> > > > > > > $settings{'REDIR_ENTRY_ADDRESS'};
> > > > > > > +               my $new_entry_remark =
> > > > > > > $settings{'REDIR_ENTRY_REMARK'};
> > > > > > > +
> > > > > > > +               # Read-in redirectsfile.
> > > > > > > +               &General::readhasharray($redirectsfile,
> > > > > > > \%redirects);
> > > > > > > +
> > > > > > > +               # Check if we should edit an existing
> > > > > > > entry and
> > > > > > > got an ID.
> > > > > > > +               if (($settings{'ACTION'} eq
> > > > > > > $Lang::tr{'update'}) && ($settings{'ID'})) {
> > > > > > > +                       # Assin the provided id.
> > > > > > > +                       $id = $settings{'ID'};
> > > > > > > +
> > > > > > > +                       # Undef the given ID.
> > > > > > > +                       undef($settings{'ID'});
> > > > > > > +
> > > > > > > +                       # Grab the configured status of
> > > > > > > the
> > > > > > > corresponding entry.
> > > > > > > +                       $status = $redirects{$id}[4];
> > > > > > > +               } else {
> > > > > > > +                       # Each newly added entry
> > > > > > > automatically
> > > > > > > should be enabled.
> > > > > > > +                       $status = "enabled";
> > > > > > > +
> > > > > > > +                       # Generate the ID for the new
> > > > > > > entry.
> > > > > > > +                       #
> > > > > > > +                       # Sort the keys by their ID and
> > > > > > > store
> > > > > > > them in an array.
> > > > > > > +                       my @keys = sort { $a <=> $b }
> > > > > > > keys
> > > > > > > %redirects;
> > > > > > > +
> > > > > > > +                       # Reverse the key array.
> > > > > > > +                       my @reversed = reverse(@keys);
> > > > > > > +
> > > > > > > +                       # Obtain the last used id.
> > > > > > > +                       my $last_id = @reversed[0];
> > > > > > > +
> > > > > > > +                       # Increase the last id by one and
> > > > > > > use
> > > > > > > it as id for the new entry.
> > > > > > > +                       $id = ++$last_id;
> > > > > > > +               }
> > > > > > > +
> > > > > > > +               # Add/Modify the entry to/in the
> > > > > > > redirects
> > > > > > > hash.
> > > > > > > +               $redirects{$id} =
> > > > > > > ["$new_entry_interface",
> > > > > > > "$new_entry_protocol", "$new_entry_port",
> > > > > > > "$new_entry_address","$status", "$new_entry_remark"];
> > > > > > > +
> > > > > > > +               # Write the changed redirects hash to the
> > > > > > > redirects file.
> > > > > > > +               &General::writehasharray($redirectsfile,
> > > > > > > \%redirects);
> > > > > > > +       }
> > > > > > > +
> > > > > > > +# Toggle Enabled/Disabled for an existing entry on the
> > > > > > > redirects list.
> > > > > > > +
> > > > > > > +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable
> > > > > > > disable'}) {
> > > > > > > +       my %redirects = ();
> > > > > > > +
> > > > > > > +       # Only go further, if an ID has been passed.
> > > > > > > +       if ($settings{'ID'}) {
> > > > > > > +               # Assign the given ID.
> > > > > > > +               my $id = $settings{'ID'};
> > > > > > > +
> > > > > > > +               # Undef the given ID.
> > > > > > > +               undef($settings{'ID'});
> > > > > > > +
> > > > > > > +               # Read-in ignoredfile.
> > > > > > > +               &General::readhasharray($redirectsfile,
> > > > > > > \%redirects);
> > > > > > > +
> > > > > > > +               # Grab the configured status of the
> > > > > > > corresponding entry.
> > > > > > > +               my $status = $redirects{$id}[4];
> > > > > > > +
> > > > > > > +               # Switch the status.
> > > > > > > +               if ($status eq "disabled") {
> > > > > > > +                       $status = "enabled";
> > > > > > > +               } else {
> > > > > > > +                       $status = "disabled";
> > > > > > > +               }
> > > > > > > +
> > > > > > > +               # Modify the status of the existing
> > > > > > > entry.
> > > > > > > +               $redirects{$id} = ["$redirects{$id}[0]",
> > > > > > > "$redirects{$id}[1]", "$redirects{$id}[2]",
> > > > > > > "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
> > > > > > > +
> > > > > > > +               # Write the changed ignored hash to the
> > > > > > > redirects file.
> > > > > > > +               &General::writehasharray($redirectsfile,
> > > > > > > \%redirects);
> > > > > > > +       }
> > > > > > > +
> > > > > > > +# Remove entry from redirects list.
> > > > > > > +
> > > > > > > +} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
> > > > > > > +       my %redirects = ();
> > > > > > > +
> > > > > > > +       # Read-in redirectsfile.
> > > > > > > +       &General::readhasharray($redirectsfile,
> > > > > > > \%redirects);
> > > > > > > +
> > > > > > > +       # move data on key up
> > > > > > > +       foreach my $key (sort keys %redirects) {
> > > > > > > +               if ($key >= $settings{'ID'}) {
> > > > > > > +                       my $next = $key + 1;
> > > > > > > +                       if (exists $redirects{$next}) {
> > > > > > > +                               foreach my $i (0 ..
> > > > > > > $#{$redirects{$next}}) { $redirects{$key}[$i] =
> > > > > > > $redirects{$next}[$i]; }
> > > > > > > +                       }
> > > > > > > +               }
> > > > > > > +       }
> > > > > > > +
> > > > > > > +       my $last_key = (sort {$a <=> $b} keys
> > > > > > > %redirects)[-1];
> > > > > > > +       delete $redirects{$last_key};
> > > > > > > +
> > > > > > > +       # Undef the given ID.
> > > > > > > +       undef($settings{'ID'});
> > > > > > > +
> > > > > > > +       # Write the changed redirects hash to file.
> > > > > > > +       &General::writehasharray($redirectsfile,
> > > > > > > \%redirects);
> > > > > > > +}
> > > > > > > +
> > > > > > > +# Load settings from file
> > > > > > > +&General::readhash($settingsfile, \%settings);
> > > > > > > +&General::readhasharray($redirectsfile, \%redirects);
> > > > > > > +
> > > > > > > +# Call functions to generate whole page.
> > > > > > > +&Header::openpage($Lang::tr{'portredir port
> > > > > > > redirections'}, 1,
> > > > > > > '');
> > > > > > > +&Header::openbigbox('100%', 'left', '', $errormessage);
> > > > > > > +
> > > > > > > +if ($errormessage) {
> > > > > > > +        &Header::openbox('100%', 'left',
> > > > > > > $Lang::tr{'warning
> > > > > > > messages'});
> > > > > > > +        print "<font
> > > > > > > color='red'>$errormessage&nbsp;</font>";
> > > > > > > +        &Header::closebox();
> > > > > > > +}
> > > > > > > +
> > > > > > > +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
> > > > > > > +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
> > > > > > > +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_A
> > > > > > > DDON'}
> > > > > > > } = "checked='checked'";
> > > > > > > +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
> > > > > > > +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
> > > > > > > +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_G
> > > > > > > REEN'}
> > > > > > > } = "checked='checked'";
> > > > > > > +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
> > > > > > > +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
> > > > > > > +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BL
> > > > > > > UE'}}
> > > > > > > = "checked='checked'";
> > > > > > > +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
> > > > > > > +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
> > > > > > > +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_
> > > > > > > ORANGE
> > > > > > > '}} = "checked='checked'";
> > > > > > > +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
> > > > > > > +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
> > > > > > > +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}
> > > > > > > } =
> > > > > > > "checked='checked'";
> > > > > > > +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
> > > > > > > +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
> > > > > > > +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}
> > > > > > > } =
> > > > > > > "checked='checked'";
> > > > > > > +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
> > > > > > > +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
> > > > > > > +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}}
> > > > > > > =
> > > > > > > "checked='checked'";
> > > > > > > +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
> > > > > > > +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
> > > > > > > +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}}
> > > > > > > =
> > > > > > > "checked='checked'";
> > > > > > > +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
> > > > > > > +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
> > > > > > > +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE
> > > > > > > '}} =
> > > > > > > "checked='checked'";
> > > > > > > +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
> > > > > > > +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
> > > > > > > +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE
> > > > > > > '}} =
> > > > > > > "checked='checked'";
> > > > > > > +
> > > > > > > +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTR
> > > > > > > Y_INTE
> > > > > > > RFACE'}} = 'selected';
> > > > > > > +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY
> > > > > > > _PROTO
> > > > > > > COL'}} = 'selected';
> > > > > > > +
> > > > > > > +&showMainBox();
> > > > > > > +&showRedirectsBox();
> > > > > > > +
> > > > > > > +&Header::closebigbox();
> > > > > > > +&Header::closepage();
> > > > > > > +
> > > > > > > +# Function to show main settings and options.
> > > > > > > +sub showMainBox() {
> > > > > > > +
> > > > > > > +       &Header::openbox('100%', 'center',
> > > > > > > "$Lang::tr{'settings'}");
> > > > > > > +       print "<form method='post'
> > > > > > > action='$ENV{'SCRIPT_NAME'}'>";
> > > > > > > +
> > > > > > > +print <<END;
> > > > > > > +<table width='80%' cellspacing='0' border='0'>
> > > > > > > +       <tr><td colspan='2' class='base'
> > > > > > > bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir
> > > > > > > common
> > > > > > > settings'}</b></td></tr
> > > > > > > +       <tr>
> > > > > > > +               <td width='25%'
> > > > > > > class='base'>$Lang::tr{'portredir enable addon'}:</td>
> > > > > > > +               <td><input type='checkbox'
> > > > > > > name='REDIR_ENABLE_ADDON'
> > > > > > > $checked{'REDIR_ENABLE_ADDON'}{'on'}
> > > > > > > /></td>
> > > > > > > +       </tr>
> > > > > > > +       <tr><td colspan='2'></td></tr>
> > > > > > > +       <tr><td colspan='2'>&nbsp;</td></tr>
> > > > > > > +END
> > > > > > > +
> > > > > > > +       # create html table with header line 1
> > > > > > > +       print "<table width='80%' cellspacing='0'
> > > > > > > border='0'><tr>";
> > > > > > > +       print "<th class='base' width='40%'
> > > > > > > align='left'><b>$Lang::tr{'portredir fw for
> > > > > > > interface'}</th>";
> > > > > > > +       if ($netsettings{'GREEN_DEV'})  {print "<th
> > > > > > > class='base' width='10%'><b><font
> > > > > > > color=green>$Lang::tr{'green'}</font></th>";
> > > > > > > +               } else {                 print "<th
> > > > > > > class='base' width='10%'></font></th>"; }
> > > > > > > +       if ($netsettings{'BLUE_DEV'})   {print "<th
> > > > > > > class='base' width='10%'><b><font
> > > > > > > color=blue>$Lang::tr{'blue'}</font></th>";
> > > > > > > +               } else {                 print "<th
> > > > > > > class='base' width='10%'></font></th>"; }
> > > > > > > +       if ($netsettings{'ORANGE_DEV'}) {print "<th
> > > > > > > class='base' width='10%'><b><font
> > > > > > > color=orange>$Lang::tr{'orange'}</font></th>";
> > > > > > > +               } else {                 print "<th
> > > > > > > class='base' width='10%'></font></th>"; }
> > > > > > > +
> > > > > > > +       # the empty right row
> > > > > > > +       print "<th class='base'
> > > > > > > width='30%'><td></td></th></tr>";
> > > > > > > +
> > > > > > > +       # line 2
> > > > > > > +       print "<tr><td>$Lang::tr{'portredir force local
> > > > > > > dns'}</td>";
> > > > > > > +       if ($netsettings{'GREEN_DEV'})  {print "<td
> > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > name='REDIR_DNS_GREEN'
> > > > > > > $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print
> > > > > > > "<td></td>";}
> > > > > > > +       if ($netsettings{'BLUE_DEV'})   {print "<td
> > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > name='REDIR_DNS_BLUE'
> > > > > > > $checked{'REDIR_DNS_BLUE'}{'on'}></td>";}
> > > > > > > else { print "<td></td>";}
> > > > > > > +       if ($netsettings{'ORANGE_DEV'}) {print "<td
> > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > name='REDIR_DNS_ORANGE'
> > > > > > > $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print
> > > > > > > "<td></td>";}
> > > > > > > +
> > > > > > > +       # line 3
> > > > > > > +       print "</tr><tr><td>$Lang::tr{'portredir force
> > > > > > > local
> > > > > > > ntp'}</td>";
> > > > > > > +       if ($netsettings{'GREEN_DEV'})  {print "<td
> > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > name='REDIR_NTP_GREEN'
> > > > > > > $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print
> > > > > > > "<td></td>";}
> > > > > > > +       if ($netsettings{'BLUE_DEV'})   {print "<td
> > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > name='REDIR_NTP_BLUE'
> > > > > > > $checked{'REDIR_NTP_BLUE'}{'on'}></td>";}
> > > > > > > else { print "<td></td>";}
> > > > > > > +       if ($netsettings{'ORANGE_DEV'}) {print "<td
> > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > name='REDIR_NTP_ORANGE'
> > > > > > > $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print
> > > > > > > "<td></td>";}
> > > > > > > +
> > > > > > > +       # line 4
> > > > > > > +       print "</tr><tr><td>$Lang::tr{'portredir enable
> > > > > > > user
> > > > > > > redirections'}</td>";
> > > > > > > +       if ($netsettings{'GREEN_DEV'})  {print "<td
> > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > name='REDIR_CUSTOM_GREEN'
> > > > > > > $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else {
> > > > > > > print
> > > > > > > "<td></td>";}
> > > > > > > +       if ($netsettings{'BLUE_DEV'})   {print "<td
> > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > name='REDIR_CUSTOM_BLUE'
> > > > > > > $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print
> > > > > > > "<td></td>";}
> > > > > > > +       if ($netsettings{'ORANGE_DEV'}) {print "<td
> > > > > > > class='base' align='center'><input type='checkbox'
> > > > > > > name='REDIR_CUSTOM_ORANGE'
> > > > > > > $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else {
> > > > > > > print
> > > > > > > "<td></td>";}
> > > > > > > +
> > > > > > > +       print <<END;
> > > > > > > +       </tr></table>
> > > > > > > +       <table width='80%' cellspacing='0' border='0'>
> > > > > > > +               <tr><td colspan='2'>&nbsp;</td></tr>
> > > > > > > +               <tr><td
> > > > > > > align='left'><b>$Lang::tr{'portredir
> > > > > > > save to activate'}</b></td><td width='5%'
> > > > > > > align='center'><input
> > > > > > > type='submit' name='ACTION' value='  $Lang::tr{'save'} 
> > > > > > > '></td></tr>
> > > > > > > +       </table></form>
> > > > > > > +END
> > > > > > > +
> > > > > > > +&Header::closebox();
> > > > > > > +}
> > > > > > > +
> > > > > > > +# Function to show elements of the redirects file and
> > > > > > > allow to
> > > > > > > add or remove single members of it.
> > > > > > > +sub showRedirectsBox() {
> > > > > > > +        &Header::openbox('100%', 'center',
> > > > > > > "$Lang::tr{'portredir custom redirections'}");
> > > > > > > +
> > > > > > > +       print <<END;
> > > > > > > +               <table width='80%' cellspacing='1'
> > > > > > > border='0'>
> > > > > > > +                       <tr>
> > > > > > > +                               <td class='base'
> > > > > > > bgcolor='$color{'color20'}'
> > > > > > > align='center'><b>$Lang::tr{'interface'}</b></td>
> > > > > > > +                               <td class='base'
> > > > > > > bgcolor='$color{'color20'}'
> > > > > > > align='center'><b>$Lang::tr{'protocol'}</b></td>
> > > > > > > +                               <td class='base'
> > > > > > > bgcolor='$color{'color20'}'
> > > > > > > align='center'><b>$Lang::tr{'port'}</b></td>
> > > > > > > +                               <td class='base'
> > > > > > > bgcolor='$color{'color20'}'
> > > > > > > align='center'><b>$Lang::tr{'ip
> > > > > > > address'}</b></td>
> > > > > > > +                               <td class='base'
> > > > > > > bgcolor='$color{'color20'}'
> > > > > > > align='center'><b>$Lang::tr{'remark'}</b></td>
> > > > > > > +                               <td class='base'
> > > > > > > colspan='3'
> > > > > > > bgcolor='$color{'color20'}'></td>
> > > > > > > +                       </tr>
> > > > > > > +END
> > > > > > > +                       # Check if some rules have been
> > > > > > > added
> > > > > > > to be redirects.
> > > > > > > +                       if (keys (%redirects)) {
> > > > > > > +                               my $col = "";
> > > > > > > +
> > > > > > > +                               # List all entries of the
> > > > > > > hash.
> > > > > > > +                               foreach my $key (sort
> > > > > > > keys
> > > > > > > %redirects){
> > > > > > > +
> > > > > > > +                                       # Assign data
> > > > > > > array
> > > > > > > positions to some nice variable names.
> > > > > > > +                                       my $interface =
> > > > > > > $redirects{$key}[0];
> > > > > > > +                                       my $protocol =
> > > > > > > $redirects{$key}[1];
> > > > > > > +                                       my $port  =
> > > > > > > $redirects{$key}[2];
> > > > > > > +                                       my $address =
> > > > > > > $redirects{$key}[3];
> > > > > > > +                                       my $status  =
> > > > > > > $redirects{$key}[4];
> > > > > > > +                                       my $remark  =
> > > > > > > $redirects{$key}[5];
> > > > > > > +
> > > > > > > +                                       # Check if the
> > > > > > > key (id)
> > > > > > > number is even or not.
> > > > > > > +                                       if
> > > > > > > ($settings{'ID'} eq
> > > > > > > $key) {
> > > > > > > +                                              
> > > > > > > $col="bgcolor='
> > > > > > > ${Header::colouryellow}'";
> > > > > > > +                                       } elsif ($key %
> > > > > > > 2) {
> > > > > > > +                                              
> > > > > > > $col="bgcolor='
> > > > > > > $color{'color22'}'";
> > > > > > > +                                       } else {
> > > > > > > +                                              
> > > > > > > $col="bgcolor='
> > > > > > > $color{'color20'}'";
> > > > > > > +                                       }
> > > > > > > +
> > > > > > > +                                       # Choose icon for
> > > > > > > the
> > > > > > > checkbox.
> > > > > > > +                                       my $gif;
> > > > > > > +                                       my $gdesc;
> > > > > > > +
> > > > > > > +                                       # Check if the
> > > > > > > status
> > > > > > > is enabled and select the correct image and description.
> > > > > > > +                                       if ($status eq
> > > > > > > 'enabled' ) {
> > > > > > > +                                               $gif =
> > > > > > > 'on.gif';
> > > > > > > +                                               $gdesc =
> > > > > > > $Lang::tr{'click to disable'};
> > > > > > > +                                       } else {
> > > > > > > +                                               $gif =
> > > > > > > 'off.gif';
> > > > > > > +                                               $gdesc =
> > > > > > > $Lang::tr{'click to enable'};
> > > > > > > +                                       }
> > > > > > > +
> > > > > > > +                                       print <<END;
> > > > > > > +                                       <tr>
> > > > > > > +                                               <td
> > > > > > > width='15%'
> > > > > > > class='base' align='center' $col><b><font
> > > > > > > color=$interface>$Lang::tr{$interface}</font></b></td>
> > > > > > > +                                               <td
> > > > > > > width='10%'
> > > > > > > class='base' align='center' $col>$protocol</td>
> > > > > > > +                                               <td
> > > > > > > width='10%'
> > > > > > > class='base' align='center' $col>$port</td>
> > > > > > > +                                               <td
> > > > > > > width='15%'
> > > > > > > class='base' align='center' $col>&nbsp;$address</td>
> > > > > > > +                                               <td
> > > > > > > width='40%'
> > > > > > > class='base' align='center' $col>&nbsp;$remark</td>
> > > > > > > +                                               <td
> > > > > > > align='center' $col>
> > > > > > > +                                                      
> > > > > > > <form
> > > > > > > method='post' action='$ENV{'SCRIPT_NAME'}'>
> > > > > > > +                                                        
> > > > > > >       
> > > > > > >  <input type='hidden' name='ACTION'
> > > > > > > value='$Lang::tr{'toggle
> > > > > > > enable disable'}' />
> > > > > > > +                                                        
> > > > > > >       
> > > > > > >  <input type='image' name='$Lang::tr{'toggle enable
> > > > > > > disable'}'
> > > > > > > src='/images/$gif' alt='$gdesc' title='$gdesc' />
> > > > > > > +                                                        
> > > > > > >       
> > > > > > >  <input type='hidden' name='ID' value='$key' />
> > > > > > > +                                                      
> > > > > > > </form>
> > > > > > > +                                               </td>
> > > > > > > +                                               <td
> > > > > > > align='center' $col>
> > > > > > > +                                                      
> > > > > > > <form
> > > > > > > method='post' action='$ENV{'SCRIPT_NAME'}'>
> > > > > > > +                                                        
> > > > > > >       
> > > > > > >  <input type='hidden' name='ACTION'
> > > > > > > value='$Lang::tr{'edit'}'
> > > > > > > />
> > > > > > > +                                                        
> > > > > > >       
> > > > > > >  <input type='image' name='$Lang::tr{'edit'}'
> > > > > > > src='/images/edit.gif' alt='$Lang::tr{'edit'}'
> > > > > > > title='$Lang::tr{'edit'}' />
> > > > > > > +                                                        
> > > > > > >       
> > > > > > >  <input type='hidden' name='ID' value='$key' />
> > > > > > > +                                                      
> > > > > > > </form>
> > > > > > > +                                               </td>
> > > > > > > +                                               <td
> > > > > > > align='center' $col>
> > > > > > > +                                                      
> > > > > > > <form
> > > > > > > method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
> > > > > > > +                                                        
> > > > > > >       
> > > > > > >  <input type='image' name='$Lang::tr{'remove'}'
> > > > > > > src='/images/delete.gif' title='$Lang::tr{'remove'}'
> > > > > > > alt='$Lang::tr{'remove'}'>
> > > > > > > +                                                        
> > > > > > >       
> > > > > > >  <input type='hidden' name='ID' value='$key'>
> > > > > > > +                                                        
> > > > > > >       
> > > > > > >  <input type='hidden' name='ACTION'
> > > > > > > value='$Lang::tr{'remove'}'>
> > > > > > > +                                                      
> > > > > > > </form>
> > > > > > > +                                               </td>
> > > > > > > +                                       </tr>
> > > > > > > +END
> > > > > > > +                               }
> > > > > > > +                       } else {
> > > > > > > +                               # Print notice that
> > > > > > > currently
> > > > > > > no ports are redirected.
> > > > > > > +                               print "<tr>\n";
> > > > > > > +                               print "<td class='base'
> > > > > > > colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
> > > > > > > +                               print "</tr>\n";
> > > > > > > +                       }
> > > > > > > +
> > > > > > > +               print "</table>\n";
> > > > > > > +
> > > > > > > +       # Section to add new elements or edit existing
> > > > > > > ones.
> > > > > > > +       print <<END;
> > > > > > > +       <br>
> > > > > > > +       <hr>
> > > > > > > +       <br>
> > > > > > > +       <div align='center'>
> > > > > > > +               <table width='100%' cellspacing='0'
> > > > > > > border='0'>
> > > > > > > +END
> > > > > > > +
> > > > > > > +       # Assign correct headline and button text.
> > > > > > > +       my $buttontext;
> > > > > > > +       my $entry_interface;
> > > > > > > +       my $entry_protocol;
> > > > > > > +       my $entry_port;
> > > > > > > +       my $entry_address;
> > > > > > > +       my $entry_remark;
> > > > > > > +
> > > > > > > +       # Check if an ID (key) has been given, in this
> > > > > > > case an
> > > > > > > existing entry should be edited.
> > > > > > > +       if ($settings{'ID'} ne '') {
> > > > > > > +               $buttontext = $Lang::tr{'update'};
> > > > > > > +               print "<tr><td class='boldbase'
> > > > > > > colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
> > > > > > > +
> > > > > > > +               # Grab address and remark for the given
> > > > > > > key.
> > > > > > > +               $entry_interface =
> > > > > > > $redirects{$settings{'ID'}}[0];
> > > > > > > +               $entry_protocol =
> > > > > > > $redirects{$settings{'ID'}}[1];
> > > > > > > +               $entry_port =
> > > > > > > $redirects{$settings{'ID'}}[2];
> > > > > > > +               $entry_address =
> > > > > > > $redirects{$settings{'ID'}}[3];
> > > > > > > +               $entry_remark =
> > > > > > > $redirects{$settings{'ID'}}[5];
> > > > > > > +
> > > > > > > +       } else {
> > > > > > > +               $buttontext = $Lang::tr{'add'};
> > > > > > > +               print "<tr><td class='boldbase'
> > > > > > > colspan='11'><b>$Lang::tr{'dnsforward add a new
> > > > > > > entry'}</b></td></tr>\n";
> > > > > > > +               print "<tr><td>&nbsp</td></tr>\n";
> > > > > > > +       }
> > > > > > > +
> > > > > > > +       print <<END;
> > > > > > > +                       <tr>
> > > > > > > +                               <td class='base'
> > > > > > > width='1%' 
> > > > > > > bgcolor='$color{'color22'}'></td>
> > > > > > > +                               <td class='base'
> > > > > > > width='15%'
> > > > > > > bgcolor='$color{'color22'}'
> > > > > > > align='left'><b>$Lang::tr{'interface'}</b></td>
> > > > > > > +                               <td class='base'
> > > > > > > width='10%'
> > > > > > > bgcolor='$color{'color22'}'
> > > > > > > align='left'><b>$Lang::tr{'protocol'}</b></td>
> > > > > > > +                               <td class='base'
> > > > > > > width='10%'
> > > > > > > bgcolor='$color{'color22'}'
> > > > > > > align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
> > > > > > > +                               <td class='base'
> > > > > > > width='13%'
> > > > > > > bgcolor='$color{'color22'}'
> > > > > > > align='left'>&nbsp;<b>$Lang::tr{'ip
> > > > > > > address'}</b></td>
> > > > > > > +                               <td class='base'
> > > > > > > width='30%'
> > > > > > > bgcolor='$color{'color22'}'
> > > > > > > align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
> > > > > > > +                               <td class='base'
> > > > > > > width='15%'
> > > > > > > bgcolor='$color{'color22'}'></td>
> > > > > > > +                               <td class='base'
> > > > > > > width='1%' 
> > > > > > > bgcolor='$color{'color22'}'></td>
> > > > > > > +                       </tr>
> > > > > > > +
> > > > > > > +                       <form method='post'
> > > > > > > action='$ENV{'SCRIPT_NAME'}'>
> > > > > > > +                       <input type='hidden' name='ID'
> > > > > > > value='$settings{'ID'}'>
> > > > > > > +                       <tr>
> > > > > > > +                               <td class='base'></td>
> > > > > > > +                               <td><select
> > > > > > > style='width:90px;'
> > > > > > > id='interface' name='REDIR_ENTRY_INTERFACE'>
> > > > > > > +END
> > > > > > > +                               if
> > > > > > > ($netsettings{'GREEN_DEV'})
> > > > > > > {
> > > > > > > +                                       if
> > > > > > > ($entry_interface eq
> > > > > > > "green") {
> > > > > > > +                                               print
> > > > > > > "<option
> > > > > > > value='green' selected='selected'
> > > > > > > {'green'}>$Lang::tr{'green'}</option>";
> > > > > > > +                                       } else { print
> > > > > > > "<option
> > > > > > > value='green' {'green'}>$Lang::tr{'green'}</option>";}
> > > > > > > +                               }
> > > > > > > +                               if
> > > > > > > ($netsettings{'BLUE_DEV'}) {
> > > > > > > +                                       if
> > > > > > > ($entry_interface eq
> > > > > > > "blue") { 
> > > > > > > +                                               print
> > > > > > > "<option
> > > > > > > value='blue' selected='selected'
> > > > > > > {'blue'}>$Lang::tr{'blue'}</option>";
> > > > > > > +                                       } else { print
> > > > > > > "<option
> > > > > > > value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
> > > > > > > +                               }
> > > > > > > +                               if
> > > > > > > ($netsettings{'ORANGE_DEV'})
> > > > > > > {
> > > > > > > +                                       if
> > > > > > > ($entry_interface eq
> > > > > > > "orange") { 
> > > > > > > +                                               print
> > > > > > > "<option
> > > > > > > value='orange' selected='selected'
> > > > > > > {'orange'}>$Lang::tr{'orange'}</option>";
> > > > > > > +                                       } else { print
> > > > > > > "<option
> > > > > > > value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
> > > > > > > +                               }
> > > > > > > +
> > > > > > > +                       print "</select><td><select
> > > > > > > style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
> > > > > > > +                       if ((!$entry_protocol) ||
> > > > > > > ($entry_protocol eq "tcp")) {
> > > > > > > +                               print "<option
> > > > > > > selected='selected' id='protocol' value='tcp'
> > > > > > > {'tcp'}>tcp</option>";
> > > > > > > +                               print "<option
> > > > > > > value='udp'
> > > > > > > {'udp'}>udp</option>";
> > > > > > > +                       } elsif ($entry_protocol eq
> > > > > > > "udp") {
> > > > > > > +                               print "<option
> > > > > > > value='tcp'
> > > > > > > {'tcp'}>tcp</option>";
> > > > > > > +                               print "<option
> > > > > > > selected='selected' value='udp' {'udp'}>udp</option>";
> > > > > > > +                       }
> > > > > > > +       print <<END;
> > > > > > > +                               </select></td>
> > > > > > > +                               <td><input type='text'
> > > > > > > name='REDIR_ENTRY_PORT'    value='$entry_port'   
> > > > > > > size='4'></td>
> > > > > > > +                               <td><input type='text'
> > > > > > > name='REDIR_ENTRY_ADDRESS' value='$entry_address'
> > > > > > > size='14'></td>
> > > > > > > +                               <td><input type='text'
> > > > > > > name='REDIR_ENTRY_REMARK'  value='$entry_remark' 
> > > > > > > size='35'></td>
> > > > > > > +                               <td width='10%'
> > > > > > > align='center'><input type='submit' name='ACTION' value='
> > > > > > > $buttontext  '></td>
> > > > > > > +                               <td class='base'></td>
> > > > > > > +                       </tr>
> > > > > > > +                       </form>
> > > > > > > +               </table>
> > > > > > > +       </div>
> > > > > > > +END
> > > > > > > +       &Header::closebox();
> > > > > > > +}
> > > > > > > diff --git a/config/rootfiles/common/misc-progs
> > > > > > > b/config/rootfiles/common/misc-progs
> > > > > > > index d6594b3f8..fbad2af8b 100644
> > > > > > > --- a/config/rootfiles/common/misc-progs
> > > > > > > +++ b/config/rootfiles/common/misc-progs
> > > > > > > @@ -17,6 +17,7 @@ usr/local/bin/logwatch
> > > > > > > #usr/local/bin/mpfirectrl
> > > > > > > usr/local/bin/openvpnctrl
> > > > > > > usr/local/bin/pakfire
> > > > > > > +#usr/local/bin/portredirctrl
> > > > > > > usr/local/bin/qosctrl
> > > > > > > usr/local/bin/rebuildhosts
> > > > > > > usr/local/bin/rebuildroutes
> > > > > > > diff --git a/config/rootfiles/packages/portredir
> > > > > > > b/config/rootfiles/packages/portredir
> > > > > > > new file mode 100644
> > > > > > > index 000000000..4b4ba8366
> > > > > > > --- /dev/null
> > > > > > > +++ b/config/rootfiles/packages/portredir
> > > > > > > @@ -0,0 +1,11 @@
> > > > > > > +etc/rc.d/init.d/portredir
> > > > > > > +etc/rc.d/rc0.d/K77portredir
> > > > > > > +etc/rc.d/rc3.d/S23portredir
> > > > > > > +etc/rc.d/rc6.d/K77portredir
> > > > > > > +srv/web/ipfire/cgi-bin/portredir.cgi
> > > > > > > +usr/local/bin/portredirctrl
> > > > > > > +var/ipfire/addon-lang/portredir.de.pl
> > > > > > > +var/ipfire/addon-lang/portredir.en.pl
> > > > > > > +var/ipfire/backup/addons/includes/portredir
> > > > > > > +var/ipfire/menu.d/EX-portredir.menu
> > > > > > > +var/ipfire/portredir
> > > > > > > diff --git a/lfs/portredir b/lfs/portredir
> > > > > > > new file mode 100644
> > > > > > > index 000000000..a4911f71f
> > > > > > > --- /dev/null
> > > > > > > +++ b/lfs/portredir
> > > > > > > @@ -0,0 +1,85 @@
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > #################
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >                 #
> > > > > > > +# IPFire.org - A linux based
> > > > > > > firewall                                         #
> > > > > > > +# Copyright (C) 2007-2021  IPFire Team 
> > > > > > > <info@ipfire.org>                     #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >                 #
> > > > > > > +# This program is free software: you can redistribute it
> > > > > > > and/or modify        #
> > > > > > > +# it under the terms of the GNU General Public License
> > > > > > > as
> > > > > > > published by        #
> > > > > > > +# the Free Software Foundation, either version 3 of the
> > > > > > > License, or           #
> > > > > > > +# (at your option) any later
> > > > > > > version.                                         #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >                 #
> > > > > > > +# This program is distributed in the hope that it will
> > > > > > > be
> > > > > > > useful,             #
> > > > > > > +# but WITHOUT ANY WARRANTY; without even the implied
> > > > > > > warranty
> > > > > > > of              #
> > > > > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
> > > > > > > See
> > > > > > > the               #
> > > > > > > +# GNU General Public License for more
> > > > > > > details.                                #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >                 #
> > > > > > > +# You should have received a copy of the GNU General
> > > > > > > Public
> > > > > > > License           #
> > > > > > > +# along with this program.  If not, see <
> > > > > > > http://www.gnu.org/licenses/>.       #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >                 #
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > #################
> > > > > > > +
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > #################
> > > > > > > +# Definitions
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > #################
> > > > > > > +
> > > > > > > +include Config
> > > > > > > +
> > > > > > > +VER        = 1.0
> > > > > > > +
> > > > > > > +THISAPP    = portredir-$(VER)
> > > > > > > +DIR_APP    = $(DIR_SRC)/$(THISAPP)
> > > > > > > +TARGET     = $(DIR_INFO)/$(THISAPP)
> > > > > > > +PROG       = portredir
> > > > > > > +PAK_VER    = 1
> > > > > > > +
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > #################
> > > > > > > +# Top-level Rules
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > #################
> > > > > > > +
> > > > > > > +install : $(TARGET)
> > > > > > > +
> > > > > > > +check :
> > > > > > > +
> > > > > > > +download :
> > > > > > > +
> > > > > > > +md5 :
> > > > > > > +
> > > > > > > +dist: 
> > > > > > > +       @$(PAK)
> > > > > > > +
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > #################
> > > > > > > +# Installation Details
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > #################
> > > > > > > +
> > > > > > > +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
> > > > > > > +       @$(PREBUILD)
> > > > > > > +       @rm -rf $(DIR_APP) && cd $(DIR_SRC)
> > > > > > > +
> > > > > > > +       #install cgi 
> > > > > > > +       install -v -m 755
> > > > > > > $(DIR_CONF)/portredir/portredir.cgi
> > > > > > > /srv/web/ipfire/cgi-bin/
> > > > > > > +
> > > > > > > +       #create configuration dir 
> > > > > > > +       -mkdir -pv /var/ipfire/portredir/
> > > > > > > +       chown -R nobody:nobody /var/ipfire/portredir/
> > > > > > > +
> > > > > > > +       # Install include file for backup
> > > > > > > +       install -v -m 644
> > > > > > > $(DIR_CONF)/portredir/portredir-
> > > > > > > backup /var/ipfire/backup/addons/includes/portredir
> > > > > > > +
> > > > > > > +       # Install menu file
> > > > > > > +       install -v -m 644 $(DIR_CONF)/portredir/EX-
> > > > > > > portredir.menu /var/ipfire/menu.d/
> > > > > > > +       chown nobody:nobody /var/ipfire/menu.d/EX-
> > > > > > > portredir.menu
> > > > > > > +
> > > > > > > +       # Install addon-specific language-files
> > > > > > > +       install -v -m 644
> > > > > > > $(DIR_CONF)/portredir/lang/portredir.*.pl
> > > > > > > /var/ipfire/addon-
> > > > > > > lang/
> > > > > > > +
> > > > > > > +       #install initscripts
> > > > > > > +       $(call INSTALL_INITSCRIPT,portredir)
> > > > > > > +
> > > > > > > +       # Create symlinks for runlevel interaction.
> > > > > > > +       ln -svf /etc/rc.d/init.d/portredir
> > > > > > > /etc/rc.d/rc3.d/S23portredir
> > > > > > > +       ln -svf /etc/rc.d/init.d/portredir
> > > > > > > /etc/rc.d/rc0.d/K77portredir
> > > > > > > +       ln -svf /etc/rc.d/init.d/portredir
> > > > > > > /etc/rc.d/rc6.d/K77portredir
> > > > > > > +
> > > > > > > +       @rm -rf $(DIR_APP)
> > > > > > > +       @$(POSTBUILD)
> > > > > > > +
> > > > > > > diff --git a/make.sh b/make.sh
> > > > > > > index fc03ebcd5..ab9fe881a 100755
> > > > > > > --- a/make.sh
> > > > > > > +++ b/make.sh
> > > > > > > @@ -1623,6 +1623,7 @@ buildipfire() {
> > > > > > >  lfsmake2 socat
> > > > > > >  lfsmake2 libcdada
> > > > > > >  lfsmake2 pmacct
> > > > > > > +  lfsmake2 portredir
> > > > > > > }
> > > > > > > 
> > > > > > > buildinstaller() {
> > > > > > > diff --git a/src/initscripts/packages/portredir
> > > > > > > b/src/initscripts/packages/portredir
> > > > > > > new file mode 100644
> > > > > > > index 000000000..cc57fb9cc
> > > > > > > --- /dev/null
> > > > > > > +++ b/src/initscripts/packages/portredir
> > > > > > > @@ -0,0 +1,191 @@
> > > > > > > +#!/bin/sh
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > ##########
> > > > > > > +# Begin $rc_base/init.d/portredir
> > > > > > > +#
> > > > > > > +# Description : portredir init script for DNS/NTP and
> > > > > > > custom 
> > > > > > > +#              port redirection rules
> > > > > > > +#
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > ##########
> > > > > > > +
> > > > > > > +. /etc/sysconfig/rc
> > > > > > > +. ${rc_functions}
> > > > > > > +
> > > > > > > +IPT="/sbin/iptables";
> > > > > > > +parent_chain="PREROUTING";
> > > > > > > +chain="PORT_REDIRECT";
> > > > > > > +
> > > > > > > +confdir="/var/ipfire/portredir";
> > > > > > > +settingsfile="${confdir}/settings";
> > > > > > > +redirectsfile="${confdir}/redirects";
> > > > > > > +SYSLOG="NO";
> > > > > > > +VERBOSE="NO";
> > > > > > > +
> > > > > > > +eval $(/usr/local/bin/readhash
> > > > > > > /var/ipfire/ethernet/settings);
> > > > > > > +eval $(/usr/local/bin/readhash ${settingsfile});
> > > > > > > +
> > > > > > > +logtext() {
> > > > > > > +       if [ "${SYSLOG}" = "YES" ]; then logger -t
> > > > > > > "portredir"
> > > > > > > ${1}; fi;
> > > > > > > +       if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
> > > > > > > +
> > > > > > > +create_chain() {
> > > > > > > +
> > > > > > > +       local line=$(${IPT} -t nat -L ${parent_chain} --
> > > > > > > line-
> > > > > > > numbers |grep "SQUID" |awk '{printf($1)}');
> > > > > > > +
> > > > > > > +       if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z
> > > > > > > "${REDIR_ENABLE_ADDON}" ]]; then
> > > > > > > +               logtext "addon not enabled in web
> > > > > > > interface...";
> > > > > > > +               echo "Portredir addon not enabled in web
> > > > > > > interface...";
> > > > > > > +               exit 0;
> > > > > > > +       fi;
> > > > > > > +
> > > > > > > +       if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep
> > > > > > > ${chain})" ]; then
> > > > > > > +               ${IPT} -t nat -N ${chain};
> > > > > > > +
> > > > > > > +               if [ ! -z "${line}" ]; then
> > > > > > > +                       logtext "create chain ${chain}
> > > > > > > and link
> > > > > > > in ${parent_chain} at position ${line}...";
> > > > > > > +                       ${IPT} -t nat -I ${parent_chain}
> > > > > > > ${line} -j ${chain};
> > > > > > > +               else
> > > > > > > +                       logtext "create chain ${chain}
> > > > > > > and link
> > > > > > > in ${parent_chain} at last position...";
> > > > > > > +                       ${IPT} -t nat -A ${parent_chain}
> > > > > > > -j
> > > > > > > ${chain};
> > > > > > > +               fi
> > > > > > > +       else
> > > > > > > +               return 1;
> > > > > > > +       fi;
> > > > > > > +       return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +remove_chain() {
> > > > > > > +       if [ ! -z "$(${IPT} -t nat -L ${parent_chain}
> > > > > > > |grep
> > > > > > > ${chain})" ]; then
> > > > > > > +               logtext "remove chain ${chain} and link
> > > > > > > in
> > > > > > > ${parent_chain} from system...";
> > > > > > > +               ${IPT} -t nat -D "${parent_chain}" -j
> > > > > > > ${chain};
> > > > > > > +               ${IPT} -t nat -F ${chain};
> > > > > > > +               ${IPT} -t nat -X ${chain};
> > > > > > > +       else
> > > > > > > +               return 1;
> > > > > > > +       fi;
> > > > > > > +       return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +activate_custom_redirections() {
> > > > > > > +       
> > > > > > > +       local array=();
> > > > > > > +       local redirects=();
> > > > > > > +       local i;
> > > > > > > +       index=();
> > > > > > > +       iface=();
> > > > > > > +       protocol=();
> > > > > > > +       port=();
> > > > > > > +       targetip=();
> > > > > > > +       enabled=();
> > > > > > > +
> > > > > > > +       IFS=$'\n' read -d '' -ra redirects <
> > > > > > > ${redirectsfile};
> > > > > > > +
> > > > > > > +       for i in "${!redirects[@]}"
> > > > > > > +       do
> > > > > > > +               IFS=$',' read -ra array <<<
> > > > > > > ${redirects[i]};
> > > > > > > +               index[i]=${array[0]};
> > > > > > > +               iface[i]=${array[1]};
> > > > > > > +               protocol[i]=${array[2]};
> > > > > > > +               port[i]=${array[3]};
> > > > > > > +               targetip[i]=${array[4]};
> > > > > > > +               enabled[i]=${array[5]};
> > > > > > > +       done
> > > > > > > +
> > > > > > > +       for i in "${!index[@]}"
> > > > > > > +       do
> > > > > > > +               if [[ ! -z "${GREEN_DEV}" && 
> > > > > > > "${iface[i]}" =
> > > > > > > "green" && "${enabled[i]}" = "enabled" ]]; then
> > > > > > > +
> > > > > > > +                       logtext "add redirect in ${chain}
> > > > > > > on
> > > > > > > ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]}
> > > > > > > port
> > > > > > > ${port[i]} ";
> > > > > > > +                       ${IPT} -t nat -A ${chain} -i
> > > > > > > ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
> > > > > > > ${protocol[i]} --dport ${port[i]} -j RETURN;
> > > > > > > +                       ${IPT} -t nat -A ${chain} -i
> > > > > > > ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
> > > > > > > ${port[i]} -j REDIRECT;
> > > > > > > +               fi
> > > > > > > +               if [[ ! -z "${BLUE_DEV}" && 
> > > > > > > "${iface[i]}" =
> > > > > > > "blue" && "${enabled[i]}" = "enabled" ]]; then
> > > > > > > +                       logtext "add redirect in ${chain}
> > > > > > > on
> > > > > > > ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]}
> > > > > > > port
> > > > > > > ${port[i]} ";
> > > > > > > +                       ${IPT} -t nat -A ${chain} -i
> > > > > > > ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
> > > > > > > ${protocol[i]} --dport ${port[i]} -j RETURN;
> > > > > > > +                       ${IPT} -t nat -A ${chain} -i
> > > > > > > ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
> > > > > > > ${port[i]} -j REDIRECT;
> > > > > > > +               fi
> > > > > > > +               if [[ ! -z "${ORANGE_DEV}" && 
> > > > > > > "${iface[i]}" =
> > > > > > > "orange" && "${enabled[i]}" = "enabled" ]]; then
> > > > > > > +                       logtext "add redirect in ${chain}
> > > > > > > on
> > > > > > > ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]}
> > > > > > > port
> > > > > > > ${port[i]} ";
> > > > > > > +                       ${IPT} -t nat -A ${chain} -i
> > > > > > > ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
> > > > > > > ${protocol[i]} --dport ${port[i]} -j RETURN;
> > > > > > > +                       ${IPT} -t nat -A ${chain} -i
> > > > > > > ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
> > > > > > > ${port[i]} -j REDIRECT;
> > > > > > > +               fi
> > > > > > > +       done
> > > > > > > +       unset array redirects i index iface protocol port
> > > > > > > targetip enabled;
> > > > > > > +       return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +activate_redirections() {
> > > > > > > +
> > > > > > > +       if ! create_chain; then return 1; fi;
> > > > > > > +       
> > > > > > > +       # Force DNS REDIRECTs on GREEN (udp, tcp, 53)
> > > > > > > +       if [[ "${REDIR_DNS_GREEN}" == "on" && 
> > > > > > > "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
> > > > > > > +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV}
> > > > > > > -d
> > > > > > > ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
> > > > > > > +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV}
> > > > > > > -p
> > > > > > > udp -m udp --dport domain -j REDIRECT;
> > > > > > > +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV}
> > > > > > > -d
> > > > > > > ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
> > > > > > > +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV}
> > > > > > > -p
> > > > > > > tcp -m tcp --dport domain -j REDIRECT;
> > > > > > > +       fi
> > > > > > > +
> > > > > > > +       # Force DNS REDIRECTs on BLUE (udp, tcp, 53)
> > > > > > > +       if [[ "${REDIR_DNS_BLUE}" == "on" && 
> > > > > > > "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
> > > > > > > +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV}
> > > > > > > -d
> > > > > > > ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
> > > > > > > +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV}
> > > > > > > -p udp
> > > > > > > -m udp --dport domain -j REDIRECT
> > > > > > > +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV}
> > > > > > > -d
> > > > > > > ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
> > > > > > > +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV}
> > > > > > > -p tcp
> > > > > > > -m tcp --dport domain -j REDIRECT
> > > > > > > +       fi
> > > > > > > +
> > > > > > > +       # Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
> > > > > > > +       if [[ "${REDIR_DNS_ORANGE}" == "on" && 
> > > > > > > "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
> > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > ${ORANGE_DEV} -d
> > > > > > > ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
> > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > ${ORANGE_DEV} -p
> > > > > > > udp -m udp --dport domain -j REDIRECT
> > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > ${ORANGE_DEV} -d
> > > > > > > ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
> > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > ${ORANGE_DEV} -p
> > > > > > > tcp -m tcp --dport domain -j REDIRECT
> > > > > > > +       fi
> > > > > > > +
> > > > > > > +       # Force NTP REDIRECTs on GREEN (udp, 123)
> > > > > > > +       if [[ "${REDIR_NTP_GREEN}" == "on" && 
> > > > > > > "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
> > > > > > > +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV}
> > > > > > > -d
> > > > > > > ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
> > > > > > > +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV}
> > > > > > > -p
> > > > > > > udp -m udp --dport ntp -j REDIRECT
> > > > > > > +       fi
> > > > > > > +
> > > > > > > +       # Force NTP REDIRECTs on BLUE (udp, 123)
> > > > > > > +       if [[ "${REDIR_NTP_BLUE}" == "on" && 
> > > > > > > "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
> > > > > > > +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV}
> > > > > > > -d
> > > > > > > ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
> > > > > > > +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV}
> > > > > > > -p udp
> > > > > > > -m udp --dport ntp -j REDIRECT
> > > > > > > +       fi
> > > > > > > +
> > > > > > > +       # Force NTP REDIRECTs on ORANGE (udp, 123)
> > > > > > > +       if [[ "${REDIR_NTP_ORANGE}" == "on" && 
> > > > > > > "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
> > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > ${ORANGE_DEV} -d
> > > > > > > ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
> > > > > > > +               ${IPT} -t nat -A ${chain} -i
> > > > > > > ${ORANGE_DEV} -p
> > > > > > > udp -m udp --dport ntp -j REDIRECT
> > > > > > > +       fi
> > > > > > > +
> > > > > > > +       if ! activate_custom_redirections; then return 1;
> > > > > > > fi;
> > > > > > > +
> > > > > > > +       return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +case "${1}" in
> > > > > > > +       start)
> > > > > > > +               boot_mesg "Loading port redirections..."
> > > > > > > +               activate_redirections;
> > > > > > > +               evaluate_retval;
> > > > > > > +               ;;
> > > > > > > +
> > > > > > > +       stop)   
> > > > > > > +               boot_mesg "Removing port redirections..."
> > > > > > > +               remove_chain;
> > > > > > > +               evaluate_retval;
> > > > > > > +               ;;
> > > > > > > +
> > > > > > > +       restart)
> > > > > > > +               ${0} stop
> > > > > > > +               ${0} start
> > > > > > > +               ;;
> > > > > > > +
> > > > > > > +       *)
> > > > > > > +               echo "Usage: ${0} {start|stop|restart}"
> > > > > > > +               exit 1
> > > > > > > +               ;;
> > > > > > > +esac
> > > > > > > +
> > > > > > > +# End $rc_base/init.d/portredir
> > > > > > > diff --git a/src/misc-progs/Makefile b/src/misc-
> > > > > > > progs/Makefile
> > > > > > > index 7c3ef7529..850f8fdcc 100644
> > > > > > > --- a/src/misc-progs/Makefile
> > > > > > > +++ b/src/misc-progs/Makefile
> > > > > > > @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl
> > > > > > > ipfirereboot \
> > > > > > >         wirelessctrl getipstat qosctrl \
> > > > > > >         redctrl syslogdctrl extrahdctrl sambactrl \
> > > > > > >         smartctrl clamavctrl addonctrl pakfire mpfirectrl
> > > > > > > wlanapctrl \
> > > > > > > -       setaliases urlfilterctrl updxlratorctrl
> > > > > > > fireinfoctrl
> > > > > > > rebuildroutes \
> > > > > > > +       setaliases urlfilterctrl updxlratorctrl
> > > > > > > fireinfoctrl
> > > > > > > rebuildroutes portredirctrl \
> > > > > > >         getconntracktable wirelessclient torctrl ddnsctrl
> > > > > > > unboundctrl \
> > > > > > >         captivectrl
> > > > > > > 
> > > > > > > diff --git a/src/misc-progs/portredirctrl.c b/src/misc-
> > > > > > > progs/portredirctrl.c
> > > > > > > new file mode 100644
> > > > > > > index 000000000..7897d711c
> > > > > > > --- /dev/null
> > > > > > > +++ b/src/misc-progs/portredirctrl.c
> > > > > > > @@ -0,0 +1,47 @@
> > > > > > > +/* This file is part of the IPFire Firewall.
> > > > > > > + *
> > > > > > > + * This program is distributed under the terms of the
> > > > > > > GNU
> > > > > > > General Public
> > > > > > > + * Licence.  See the file COPYING for details.
> > > > > > > + *
> > > > > > > + */
> > > > > > > +
> > > > > > > +#include <stdlib.h>
> > > > > > > +#include <stdio.h>
> > > > > > > +#include <string.h>
> > > > > > > +#include <unistd.h>
> > > > > > > +#include <sys/types.h>
> > > > > > > +#include <fcntl.h>
> > > > > > > +#include "setuid.h"
> > > > > > > +
> > > > > > > +int main(int argc, char *argv[]) {
> > > > > > > +       if (!(initsetuid()))
> > > > > > > +               exit(1);
> > > > > > > +
> > > > > > > +       // Check what command is asked
> > > > > > > +        if (argc < 2) {
> > > > > > > +                fprintf(stderr, "\nNo argument
> > > > > > > given.\n\nportredirctrl
> > > > > > > (start|stop|restart|enable|disable)\n\n");
> > > > > > > +                exit(1);
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        if (strcmp(argv[1], "start") == 0) {
> > > > > > > +                safe_system("/etc/rc.d/init.d/portredir
> > > > > > > start");
> > > > > > > +        } else if (strcmp(argv[1], "stop") == 0) {
> > > > > > > +                safe_system("/etc/rc.d/init.d/portredir
> > > > > > > stop");
> > > > > > > +        } else if (strcmp(argv[1], "restart") == 0) {
> > > > > > > +                safe_system("/etc/rc.d/init.d/portredir
> > > > > > > restart");
> > > > > > > +       } else if (strcmp(argv[1], "enable") == 0) {
> > > > > > > +               safe_system("touch
> > > > > > > /var/ipfire/portredir/enable");
> > > > > > > +               safe_system("ln -snf
> > > > > > > /etc/rc.d/init.d/portredir
> > > > > > > /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
> > > > > > > +               safe_system("ln -snf
> > > > > > > /etc/rc.d/init.d/portredir
> > > > > > > /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
> > > > > > > +               safe_system("ln -snf
> > > > > > > /etc/rc.d/init.d/portredir
> > > > > > > /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
> > > > > > > +       } else if (strcmp(argv[1], "disable") == 0) {
> > > > > > > +               safe_system("/etc/rc.d/init.d/portredir
> > > > > > > stop");
> > > > > > > +               safe_system("unlink
> > > > > > > /var/ipfire/portredir/enable");
> > > > > > > +               safe_system("rm -rf
> > > > > > > /etc/rc.d/rc*.d/*portredir
> > > > > > > > /dev/null 2>&1");
> > > > > > > +        } else {
> > > > > > > +                fprintf(stderr, "\nBad argument
> > > > > > > given.\n\nportredirctrl
> > > > > > > (start|stop|restart|enable|disable)\n\n");
> > > > > > > +                exit(1);
> > > > > > > +        }
> > > > > > > +
> > > > > > > +       return 0;
> > > > > > > +}
> > > > > > > diff --git a/src/paks/portredir/install.sh
> > > > > > > b/src/paks/portredir/install.sh
> > > > > > > new file mode 100644
> > > > > > > index 000000000..9f69aeae2
> > > > > > > --- /dev/null
> > > > > > > +++ b/src/paks/portredir/install.sh
> > > > > > > @@ -0,0 +1,32 @@
> > > > > > > +#!/bin/bash
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > ##############
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +# This file is part of the IPFire
> > > > > > > Firewall.                                #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +# IPFire is free software; you can redistribute it
> > > > > > > and/or
> > > > > > > modify           #
> > > > > > > +# it under the terms of the GNU General Public License
> > > > > > > as
> > > > > > > published by     #
> > > > > > > +# the Free Software Foundation; either version 2 of the
> > > > > > > License, or        #
> > > > > > > +# (at your option) any later
> > > > > > > version.                                      #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +# IPFire is distributed in the hope that it will be
> > > > > > > useful,                #
> > > > > > > +# but WITHOUT ANY WARRANTY; without even the implied
> > > > > > > warranty
> > > > > > > of           #
> > > > > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
> > > > > > > See
> > > > > > > the            #
> > > > > > > +# GNU General Public License for more
> > > > > > > details.                             #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +# You should have received a copy of the GNU General
> > > > > > > Public
> > > > > > > License        #
> > > > > > > +# along with IPFire; if not, write to the Free
> > > > > > > Software                    #
> > > > > > > +# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> > > > > > > MA 
> > > > > > > 02111-1307 USA #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +# Copyright (C) 2021 IPFire-Team
> > > > > > > <info@ipfire.org>.                        #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > ##############
> > > > > > > +#
> > > > > > > +. /opt/pakfire/lib/functions.sh
> > > > > > > +extract_files
> > > > > > > +restore_backup ${NAME}
> > > > > > > +
> > > > > > > +/usr/local/bin/update-lang-cache
> > > > > > > +
> > > > > > > +chown root:nobody /usr/local/bin/portredirctrl
> > > > > > > +chmod 4750 /usr/local/bin/portredirctrl
> > > > > > > +chmod u+s /usr/local/bin/portredirctrl
> > > > > > > diff --git a/src/paks/portredir/uninstall.sh
> > > > > > > b/src/paks/portredir/uninstall.sh
> > > > > > > new file mode 100644
> > > > > > > index 000000000..df9270125
> > > > > > > --- /dev/null
> > > > > > > +++ b/src/paks/portredir/uninstall.sh
> > > > > > > @@ -0,0 +1,28 @@
> > > > > > > +#!/bin/bash
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > ##############
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +# This file is part of the IPFire
> > > > > > > Firewall.                                #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +# IPFire is free software; you can redistribute it
> > > > > > > and/or
> > > > > > > modify           #
> > > > > > > +# it under the terms of the GNU General Public License
> > > > > > > as
> > > > > > > published by     #
> > > > > > > +# the Free Software Foundation; either version 2 of the
> > > > > > > License, or        #
> > > > > > > +# (at your option) any later
> > > > > > > version.                                      #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +# IPFire is distributed in the hope that it will be
> > > > > > > useful,                #
> > > > > > > +# but WITHOUT ANY WARRANTY; without even the implied
> > > > > > > warranty
> > > > > > > of           #
> > > > > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
> > > > > > > See
> > > > > > > the            #
> > > > > > > +# GNU General Public License for more
> > > > > > > details.                             #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +# You should have received a copy of the GNU General
> > > > > > > Public
> > > > > > > License        #
> > > > > > > +# along with IPFire; if not, write to the Free
> > > > > > > Software                    #
> > > > > > > +# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> > > > > > > MA 
> > > > > > > 02111-1307 USA #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +# Copyright (C) 2007 IPFire-Team
> > > > > > > <info@ipfire.org>.                        #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > ##############
> > > > > > > +#
> > > > > > > +. /opt/pakfire/lib/functions.sh
> > > > > > > +make_backup ${NAME}
> > > > > > > +remove_files
> > > > > > > +
> > > > > > > +/usr/local/bin/update-lang-cache
> > > > > > > diff --git a/src/paks/portredir/update.sh
> > > > > > > b/src/paks/portredir/update.sh
> > > > > > > new file mode 100644
> > > > > > > index 000000000..89c40d0d7
> > > > > > > --- /dev/null
> > > > > > > +++ b/src/paks/portredir/update.sh
> > > > > > > @@ -0,0 +1,26 @@
> > > > > > > +#!/bin/bash
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > ##############
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +# This file is part of the IPFire
> > > > > > > Firewall.                                #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +# IPFire is free software; you can redistribute it
> > > > > > > and/or
> > > > > > > modify           #
> > > > > > > +# it under the terms of the GNU General Public License
> > > > > > > as
> > > > > > > published by     #
> > > > > > > +# the Free Software Foundation; either version 2 of the
> > > > > > > License, or        #
> > > > > > > +# (at your option) any later
> > > > > > > version.                                      #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +# IPFire is distributed in the hope that it will be
> > > > > > > useful,                #
> > > > > > > +# but WITHOUT ANY WARRANTY; without even the implied
> > > > > > > warranty
> > > > > > > of           #
> > > > > > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
> > > > > > > See
> > > > > > > the            #
> > > > > > > +# GNU General Public License for more
> > > > > > > details.                             #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +# You should have received a copy of the GNU General
> > > > > > > Public
> > > > > > > License        #
> > > > > > > +# along with IPFire; if not, write to the Free
> > > > > > > Software                    #
> > > > > > > +# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> > > > > > > MA 
> > > > > > > 02111-1307 USA #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +# Copyright (C) 2007 IPFire-Team
> > > > > > > <info@ipfire.org>.                        #
> > > > > > > +#                                                       
> > > > > > >       
> > > > > > >              #
> > > > > > > +########################################################
> > > > > > > ######
> > > > > > > ##############
> > > > > > > +#
> > > > > > > +. /opt/pakfire/lib/functions.sh
> > > > > > > +./uninstall.sh
> > > > > > > +./install.sh
> > > > > > > -- 
> > > > > > > 2.18.0
> > > > > > > 
> > > > > > 
> > > > > 
> > > > 
> > > 
> > 
>
  
Jon Murphy Aug. 6, 2021, 10:02 p.m. UTC | #16
Hi Michael / Stefan - 

> On Aug 6, 2021, at 10:57 AM, Stefan Schantl <stefan.schantl@ipfire.org> wrote:
> 
> Am Donnerstag, dem 05.08.2021 um 22:33 +0200 schrieb Michael Tremer:
>> Hello,
>> 
>>> On 3 Aug 2021, at 18:04, Jon Murphy <jcmurphy26@gmail.com> wrote:
>>> 
>>> Hello all!
>>> 
>>> I’m having trouble following the changes to firewall.cgi.  I think
>>> there were 4 or 5 patches 
>>> submitted to the Dev Mailing List.
>>> 
>>> Did these get approved?
>> 
>> That is a good question. AFAIK they are not in c159.
>> 
>>> 
>>> I looked at the Changelog for Core 158 and core 159 (testing) but I
>>> didn’t see anything:
>>> 
>>> https://nightly.ipfire.org/core158/2021-07-21%2015:47:35%20+0000-23498c61/x86_64/changelog.txt
>>> 
>>> https://nightly.ipfire.org/master/latest/x86_64/changelog.txt
>>> 
>>> 
>>> Can they be added to CU 159?
>>> 
>>> To be honest I am mainly interested in the DNS redirect (and its
>>> cousin NTP redirect).
>>> 
>>> I’d be happy to help test but I do need some help applying the
>>> patches.  
>>> Right now I apply them by grabbing a copy of firewall.cgi and
>>> manually deleting and then manually adding a patch.
>>> Not the smartest way but it works (if I don’t screw-up and make a
>>> mistake!).
>> 
>> @Stefan: What is the status on this?
> 
> 
> All firewall related patches, pending on the list, safely can be merged
> and tested by a bigger group of testers.



These are the patches I found related to firewall.cgi.  I am not sure if 
these are all DNS/NTP redirect patches or if they are related to other firewall.cgi issues.

[PATCH] firewall.cgi: Bring back check for single IP when using DNAT. <https://lists.ipfire.org/pipermail/development/2021-August/010985.html> - submitted by SS and reviewed by MT.

[PATCH 1/3] firewall.cgi: Remove faulty assignments for selected hash. <https://lists.ipfire.org/pipermail/development/2021-July/010776.html> - submitted by SS
[PATCH 2/3] firewall.cgi: Fix multiple usage of configured net or interface. <https://lists.ipfire.org/pipermail/development/2021-July/010777.html> - submitted by SS
[PATCH 3/3] firewall.cgi: Make checked and selected hashes private for each single function. <https://lists.ipfire.org/pipermail/development/2021-July/010778.html> - submitted by SS

[PATCH] firewall.cgi: Allow to creating input rules from Orange to another zone. <https://lists.ipfire.org/pipermail/development/2021-July/010850.html> - submitted by SS and reviewed by MT.

[PATCH] firewall.cgi: Map rule if manual target address belongs to IPFire <https://lists.ipfire.org/pipermail/development/2021-July/010851.html> - submitted by SS and reviewed by MT.

[PATCH] firewall.cgi: Bring back check for single IP when using DNAT. <https://lists.ipfire.org/pipermail/development/2021-July/010852.html>


I think I found everything…

Jon


PS - I did not add other info below...


> 
> - Stefan
>> 
>> -Michael
>> 
>>> Jon
>>> 
>>>> On Jun 30, 2021, at 2:14 PM, Stefan Schantl <
>>>> stefan.schantl@ipfire.org> wrote:
>>>> 
>>>> Hello Matthias, Hello Michael, Hello Jon, Hello *,
>>>> 
>>>> I've followed the conversation on this list since the first mail
>>>> and
>>>> thoughts about forcing DNS traffic to use the local resolver.
>>>> 
>>>> It was a very long journey and lot of time and work has been
>>>> spent to
>>>> get to the present point.
>>>> 
>>>> As Michael requested here, I've digged through the lines of the
>>>> perl
>>>> script which is responsible for creating the firewall rules and
>>>> surprisingly found that everyting which is needed to create
>>>> generic
>>>> REDIRECT rules already was written in the past - it just did not
>>>> work
>>>> as designed/expected.
>>>> 
>>>> Finaly I was able to adjust these lines of code and to repair
>>>> that
>>>> feature.
>>>> 
>>>> A redirect rule can be created by picking a single host or group
>>>> of
>>>> hosts or entire network(s) as source, selecting NAT (DNAT) and
>>>> choosing
>>>> the Firewall itself as target.
>>>> 
>>>> The protocol or service or service group which should be
>>>> redirected has
>>>> to be selected afterwards. If you want to redirect a given port
>>>> to
>>>> another one it can be specified as "Target port".
>>>> 
>>>> All created redirect rules are displayed as "input rules".
>>>> 
>>>> 
>>>> The patch directly can be accessed here:
>>>> 
>>>> https://patchwork.ipfire.org/project/ipfire/patch/20210630184031.7726-1-stefan.schantl@ipfire.org/
>>>> 
>>>> Best regards,
>>>> 
>>>> -Stefan
>>>> 
>>>>> Hello,
>>>>> 
>>>>>> On 28 Jun 2021, at 18:53, Jon Murphy <jcmurphy26@gmail.com>
>>>>>> wrote:
>>>>>> 
>>>>>> Hi Michael!  Happy Monday!
>>>>>> 
>>>>>> 
>>>>>>> Why do we not extend the firewall UI probably by about 20
>>>>>>> lines
>>>>>>> of code instead of adding many hundreds of lines?
>>>>>>> 
>>>>>>> Please can someone elaborate on this more?
>>>>>> 
>>>>>> Doing a DNS redirect, via the WegBUI, has been an issue since
>>>>>> 2015.  I found this quote in the old forum:
>>>>>> 
>>>>>> "Having investigated a bit more I have concluded that it's
>>>>>> not
>>>>>> currently possible to create such rules through the WUI.
>>>>>> 
>>>>>> There are a number of obstacles:
>>>>>> 1. It is not allowed to create a rule where source IP and
>>>>>> destination nat IP is on the same subnetwork (e.g. GREEN),
>>>>>> WUI
>>>>>> error message: "Source and destination IP addresses are from
>>>>>> the
>>>>>> same subnet."
>>>>>> 
>>>>>> 2. WUI will not allow you to create a rule without a
>>>>>> destination
>>>>>> (the filtered packet must adhere to a destination, not only a
>>>>>> port)
>>>>>> and the destination MUST be an IP address of one of the
>>>>>> IPFire
>>>>>> interfaces, which limits whats possible a great deal." 
>>>>> 
>>>>> And these cannot be changed?
>>>>> 
>>>>>> And I found this from 2016:
>>>>>> https://bugzilla.ipfire.org/show_bug.cgi?id=11168
>>>>>> 
>>>>>> So I am guessing that no one has been able to determine a way
>>>>>> to
>>>>>> extend the WebGUI.  
>>>>> 
>>>>> Has anyone tried? I do not see any obvious reasons why this
>>>>> should
>>>>> not be possible.
>>>>> 
>>>>>> I am curious - Who created the 
>>>>>> https://ipfire:444/cgi-bin/firewall.cgi page?  And could they
>>>>>> help?
>>>>> 
>>>>> -Michael
>>>>> 
>>>>>> Jon
>>>>>> 
>>>>>> 
>>>>>>> On Jun 28, 2021, at 11:04 AM, Michael Tremer <
>>>>>>> michael.tremer@ipfire.org> wrote:
>>>>>>> 
>>>>>>> Hello Matthias,
>>>>>>> 
>>>>>>>> On 27 Jun 2021, at 14:48, Matthias Fischer <
>>>>>>>> matthias.fischer@ipfire.org> wrote:
>>>>>>>> 
>>>>>>>> From: Marcel Lorenz <marcel.lorenz@ipfire.org>
>>>>>>> 
>>>>>>> Thank you for sending this patch on Marcel’s behalf, but I
>>>>>>> would
>>>>>>> much more prefer if he would submit his patches on his own.
>>>>>>> I do
>>>>>>> not see why that isn’t possible.
>>>>>>> 
>>>>>>>> Please note:
>>>>>>>> This is a new addon written by Marcel Lorenz <
>>>>>>>> marcel.lorenz@ipfire.org>.
>>>>>>>> 
>>>>>>>> It adds a new GUI to IPFire for DNS/NTP *and* user
>>>>>>>> specific
>>>>>>>> port redirections.
>>>>>>>> 
>>>>>>>> How its working:
>>>>>>>> It has exactly the same functionalities as "Forcing
>>>>>>>> DNS/NTP..."  - and some more.
>>>>>>>> 
>>>>>>>> By setting switches, DNS/NTP requests are automatically
>>>>>>>> redirected to the local IPFire DNS/NTP servers.
>>>>>>>> 
>>>>>>>> Additionally, the user can specify custom redirections.
>>>>>>>> 
>>>>>>>> These rules are added to a new chain in PREROUTING =>
>>>>>>>> PORT_REDIRECT.
>>>>>>>> 
>>>>>>>> To avoid problems with (e.g.) transparent 'squid'
>>>>>>>> configurations,
>>>>>>>> redirection rules are added automatically before existing
>>>>>>>> 'squid' rules.
>>>>>>> 
>>>>>>> This message does unfortunately not say why this add-on
>>>>>>> would be
>>>>>>> useful. I am emphasising this again and again that it is
>>>>>>> not very
>>>>>>> important how something is done specially. That should be
>>>>>>> commented in the code and other implementation details
>>>>>>> should
>>>>>>> also be documented there.
>>>>>>> 
>>>>>>> As I have stated on this functionality many times before, I
>>>>>>> do
>>>>>>> not see why this is necessary at all.
>>>>>>> 
>>>>>>> Why is this an add-on?
>>>>>>> 
>>>>>>> Why do we not extend the firewall UI probably by about 20
>>>>>>> lines
>>>>>>> of code instead of adding many hundreds of lines?
>>>>>>> 
>>>>>>> Please can someone elaborate on this more?
>>>>>>> 
>>>>>>> -Michael
>>>>>>> 
>>>>>>>> Signed-off-by: Matthias Fischer <
>>>>>>>> matthias.fischer@ipfire.org>
>>>>>>>> ---
>>>>>>>> config/portredir/EX-portredir.menu    |   6 +
>>>>>>>> config/portredir/lang/portredir.de.pl |  19 +
>>>>>>>> config/portredir/lang/portredir.en.pl |  19 +
>>>>>>>> config/portredir/portredir-backup     |   1 +
>>>>>>>> config/portredir/portredir.cgi        | 525
>>>>>>>> ++++++++++++++++++++++++++
>>>>>>>> config/rootfiles/common/misc-progs    |   1 +
>>>>>>>> config/rootfiles/packages/portredir   |  11 +
>>>>>>>> lfs/portredir                         |  85 +++++
>>>>>>>> make.sh                               |   1 +
>>>>>>>> src/initscripts/packages/portredir    | 191 ++++++++++
>>>>>>>> src/misc-progs/Makefile               |   2 +-
>>>>>>>> src/misc-progs/portredirctrl.c        |  47 +++
>>>>>>>> src/paks/portredir/install.sh         |  32 ++
>>>>>>>> src/paks/portredir/uninstall.sh       |  28 ++
>>>>>>>> src/paks/portredir/update.sh          |  26 ++
>>>>>>>> 15 files changed, 993 insertions(+), 1 deletion(-)
>>>>>>>> create mode 100644 config/portredir/EX-portredir.menu
>>>>>>>> create mode 100644 config/portredir/lang/portredir.de.pl
>>>>>>>> create mode 100644 config/portredir/lang/portredir.en.pl
>>>>>>>> create mode 100644 config/portredir/portredir-backup
>>>>>>>> create mode 100644 config/portredir/portredir.cgi
>>>>>>>> create mode 100644 config/rootfiles/packages/portredir
>>>>>>>> create mode 100644 lfs/portredir
>>>>>>>> create mode 100644 src/initscripts/packages/portredir
>>>>>>>> create mode 100644 src/misc-progs/portredirctrl.c
>>>>>>>> create mode 100644 src/paks/portredir/install.sh
>>>>>>>> create mode 100644 src/paks/portredir/uninstall.sh
>>>>>>>> create mode 100644 src/paks/portredir/update.sh
>>>>>>>> 
>>>>>>>> diff --git a/config/portredir/EX-portredir.menu
>>>>>>>> b/config/portredir/EX-portredir.menu
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..8376e8053
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/portredir/EX-portredir.menu
>>>>>>>> @@ -0,0 +1,6 @@
>>>>>>>> +    $subfirewall->{'95.portredir'} = {
>>>>>>>> +                               'caption' =>
>>>>>>>> $Lang::tr{'portredir port redirections'},
>>>>>>>> +                               'uri' => '/cgi-
>>>>>>>> bin/portredir.cgi',
>>>>>>>> +                               'title' =>
>>>>>>>> "$Lang::tr{'portredir port redirections'}",
>>>>>>>> +                               'enabled' => 1
>>>>>>>> +                               };
>>>>>>>> diff --git a/config/portredir/lang/portredir.de.pl
>>>>>>>> b/config/portredir/lang/portredir.de.pl
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..b932d4a85
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/portredir/lang/portredir.de.pl
>>>>>>>> @@ -0,0 +1,19 @@
>>>>>>>> +%tr = (
>>>>>>>> +%tr,
>>>>>>>> +'portredir enable addon' => 'Addon aktivieren',
>>>>>>>> +'portredir common settings' => 'Allgemeine
>>>>>>>> Einstellungen',
>>>>>>>> +'portredir port redirections' => 'Portumleitungen',
>>>>>>>> +'portredir fw for interface' => 'Firewalloptionen für
>>>>>>>> das
>>>>>>>> Interface',
>>>>>>>> +'portredir enable user redirections' => 'Aktiviere
>>>>>>>> benutzerdefinierte Portumleitungen',
>>>>>>>> +'portredir force local dns' => 'Erzwinge lokale DNS-
>>>>>>>> Server',
>>>>>>>> +'portredir force local ntp' => 'Erzwinge lokale NTP-
>>>>>>>> Server',
>>>>>>>> +'portredir custom redirections' => 'Benutzerdefinierte
>>>>>>>> Portumleitungen',
>>>>>>>> +'portredir remove rule' => 'Entferne Regel',
>>>>>>>> +'portredir add rule' => 'Hinzufügen',
>>>>>>>> +'portredir no entries' => 'Keine Einträge vorhanden.',
>>>>>>>> +'portredir invalid address' => 'Ungültige Host-
>>>>>>>> Addresse.',
>>>>>>>> +'portredir empty input' => 'Fehlende Angabe: Bitte geben
>>>>>>>> Sie
>>>>>>>> einen gültigen Host an.',
>>>>>>>> +'portredir save to activate' => 'Speichern, um
>>>>>>>> Änderungen zu
>>>>>>>> aktivieren',
>>>>>>>> +);
>>>>>>>> +
>>>>>>>> +#EOF
>>>>>>>> diff --git a/config/portredir/lang/portredir.en.pl
>>>>>>>> b/config/portredir/lang/portredir.en.pl
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..f442f3eaa
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/portredir/lang/portredir.en.pl
>>>>>>>> @@ -0,0 +1,19 @@
>>>>>>>> +%tr = (
>>>>>>>> +%tr,
>>>>>>>> +'portredir enable addon' => 'Enable addon',
>>>>>>>> +'portredir common settings' => 'Common settings',
>>>>>>>> +'portredir port redirections' => 'Port redirections',
>>>>>>>> +'portredir fw for interface' => 'Firewall options for
>>>>>>>> interface',
>>>>>>>> +'portredir enable user redirections' => 'Enable user
>>>>>>>> port
>>>>>>>> redirections',
>>>>>>>> +'portredir force local dns' => 'Enforce local DNS
>>>>>>>> servers',
>>>>>>>> +'portredir force local ntp' => 'Enforce local NTP
>>>>>>>> servers',
>>>>>>>> +'portredir custom redirections' => 'Custom port
>>>>>>>> redirections',
>>>>>>>> +'portredir remove rule' => 'Remove rule',
>>>>>>>> +'portredir add rule' => 'Add new',
>>>>>>>> +'portredir no entries' => 'No entries at the moment.',
>>>>>>>> +'portredir invalid address' => 'Invalid host address.',
>>>>>>>> +'portredir empty input' => 'Empty input: Please enter a
>>>>>>>> valid
>>>>>>>> host.',
>>>>>>>> +'portredir save to activate' => 'Save to activate
>>>>>>>> changes',
>>>>>>>> +);
>>>>>>>> +
>>>>>>>> +#EOF
>>>>>>>> diff --git a/config/portredir/portredir-backup
>>>>>>>> b/config/portredir/portredir-backup
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..bd2ada742
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/portredir/portredir-backup
>>>>>>>> @@ -0,0 +1 @@
>>>>>>>> +/var/ipfire/portredir
>>>>>>>> diff --git a/config/portredir/portredir.cgi
>>>>>>>> b/config/portredir/portredir.cgi
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..4913dda3f
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/portredir/portredir.cgi
>>>>>>>> @@ -0,0 +1,525 @@
>>>>>>>> +#!/usr/bin/perl
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> #################
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>                 #
>>>>>>>> +# IPFire.org - A linux based
>>>>>>>> firewall                                         #
>>>>>>>> +# Copyright (C) 2021  IPFire Team 
>>>>>>>> <info@ipfire.org>                          #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>                 #
>>>>>>>> +# This program is free software: you can redistribute it
>>>>>>>> and/or modify        #
>>>>>>>> +# it under the terms of the GNU General Public License
>>>>>>>> as
>>>>>>>> published by        #
>>>>>>>> +# the Free Software Foundation, either version 3 of the
>>>>>>>> License, or           #
>>>>>>>> +# (at your option) any later
>>>>>>>> version.                                         #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>                 #
>>>>>>>> +# This program is distributed in the hope that it will
>>>>>>>> be
>>>>>>>> useful,             #
>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied
>>>>>>>> warranty
>>>>>>>> of              #
>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
>>>>>>>> See
>>>>>>>> the               #
>>>>>>>> +# GNU General Public License for more
>>>>>>>> details.                                #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>                 #
>>>>>>>> +# You should have received a copy of the GNU General
>>>>>>>> Public
>>>>>>>> License           #
>>>>>>>> +# along with this program.  If not, see <
>>>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>                 #
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> #################
>>>>>>>> +
>>>>>>>> +use strict;
>>>>>>>> +
>>>>>>>> +# enable only the following on debugging purpose
>>>>>>>> +use warnings;
>>>>>>>> +use CGI::Carp 'fatalsToBrowser';
>>>>>>>> +
>>>>>>>> +require '/var/ipfire/general-functions.pl';
>>>>>>>> +require "${General::swroot}/lang.pl";
>>>>>>>> +require "${General::swroot}/header.pl";
>>>>>>>> +
>>>>>>>> +# File declarations
>>>>>>>> +my $settingsfile =
>>>>>>>> "${General::swroot}/portredir/settings";
>>>>>>>> +my $redirectsfile =
>>>>>>>> "${General::swroot}/portredir/redirects";
>>>>>>>> +
>>>>>>>> +# Create empty settingsfiles if they does not exist yet
>>>>>>>> +unless (-e "$settingsfile") { system ("touch
>>>>>>>> $settingsfile");
>>>>>>>> }
>>>>>>>> +unless (-e "$redirectsfile") { system ("touch
>>>>>>>> $redirectsfile"); }
>>>>>>>> +
>>>>>>>> +# load ipfire settings
>>>>>>>> +our %netsettings = ();
>>>>>>>> +our %color = ();
>>>>>>>> +&General::readhash("${General::swroot}/ethernet/settings
>>>>>>>> ",
>>>>>>>> \%netsettings);
>>>>>>>> +&General::readhash("/srv/web/ipfire/html/themes/ipfire/i
>>>>>>>> nclude
>>>>>>>> /colors.txt", \%color);
>>>>>>>> +
>>>>>>>> +my %settings=();
>>>>>>>> +my %portredirs=();
>>>>>>>> +my %checked=(); # Checkbox manipulations
>>>>>>>> +my $errormessage='';
>>>>>>>> +my %selected=();
>>>>>>>> +our %redirects=();
>>>>>>>> +
>>>>>>>> +$settings{'ACTION'} = '';
>>>>>>>> +$settings{'REDIR_ENABLE_ADDON'}="off";
>>>>>>>> +$settings{'REDIR_CUSTOM_GREEN'}="off";
>>>>>>>> +$settings{'REDIR_CUSTOM_BLUE'}="off";
>>>>>>>> +$settings{'REDIR_CUSTOM_ORANGE'}="off";
>>>>>>>> +$settings{'REDIR_DNS_GREEN'}="off";
>>>>>>>> +$settings{'REDIR_NTP_GREEN'}="off";
>>>>>>>> +$settings{'REDIR_DNS_BLUE'}="off";
>>>>>>>> +$settings{'REDIR_NTP_BLUE'}="off";
>>>>>>>> +$settings{'REDIR_DNS_ORANGE'}="off";
>>>>>>>> +$settings{'REDIR_NTP_ORANGE'}="off";
>>>>>>>> +
>>>>>>>> +&Header::showhttpheaders();
>>>>>>>> +
>>>>>>>> +# Get GUI values
>>>>>>>> +&Header::getcgihash(\%settings);
>>>>>>>> +
>>>>>>>> +# Save action
>>>>>>>> +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
>>>>>>>> +
>>>>>>>> +       # If custom rules enabled, deactivate default
>>>>>>>> rules on
>>>>>>>> interface
>>>>>>>> +       if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
>>>>>>>> +               $settings{'REDIR_DNS_GREEN'}="off";
>>>>>>>> +               $settings{'REDIR_NTP_GREEN'}="off";
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
>>>>>>>> +               $settings{'REDIR_DNS_BLUE'}="off";
>>>>>>>> +               $settings{'REDIR_NTP_BLUE'}="off";
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
>>>>>>>> +               $settings{'REDIR_DNS_ORANGE'}="off";
>>>>>>>> +               $settings{'REDIR_NTP_ORANGE'}="off";
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       &General::writehash($settingsfile, \%settings);
>>>>>>>> +
>>>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
>>>>>>>> +               system ('/usr/local/bin/portredirctrl
>>>>>>>> restart
>>>>>>>>> /dev/null 2>&1');
>>>>>>>> +               system ('/usr/local/bin/portredirctrl
>>>>>>>> enable
>>>>>>>>> /dev/null 2>&1');
>>>>>>>> +               &General::log('portredir addon: port
>>>>>>>> redirections enabled');
>>>>>>>> +       }
>>>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
>>>>>>>> +               system ('/usr/local/bin/portredirctrl
>>>>>>>> disable
>>>>>>>>> /dev/null 2>&1');
>>>>>>>> +               system ('/usr/local/bin/portredirctrl
>>>>>>>> stop
>>>>>>>>> /dev/null 2>&1');
>>>>>>>> +               &General::log('portredir addon: port
>>>>>>>> redirections disabled');
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +# Add/edit an entry to the redirectsfile.
>>>>>>>> +
>>>>>>>> +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) ||
>>>>>>>> ($settings{'ACTION'} eq $Lang::tr{'update'})) {
>>>>>>>> +
>>>>>>>> +       # Check if any input has been performed.
>>>>>>>> +       if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
>>>>>>>> +
>>>>>>>> +               # Check if the given input is no valid
>>>>>>>> IP-
>>>>>>>> address, display an error message.
>>>>>>>> +               if
>>>>>>>> (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
>>>>>>>> +                       $errormessage =
>>>>>>>> "$Lang::tr{'portredir
>>>>>>>> invalid address'}";
>>>>>>>> +               }
>>>>>>>> +       } else {
>>>>>>>> +               $errormessage = "$Lang::tr{'portredir
>>>>>>>> empty
>>>>>>>> input'}";
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       # Go further if there was no error.
>>>>>>>> +       if ($errormessage eq '') {
>>>>>>>> +               my %redirects = ();
>>>>>>>> +               my $id;
>>>>>>>> +               my $status;
>>>>>>>> +
>>>>>>>> +               # Assign hash values.
>>>>>>>> +               my $new_entry_interface =
>>>>>>>> $settings{'REDIR_ENTRY_INTERFACE'};
>>>>>>>> +               my $new_entry_protocol =
>>>>>>>> $settings{'REDIR_ENTRY_PROTOCOL'};
>>>>>>>> +               my $new_entry_port =
>>>>>>>> $settings{'REDIR_ENTRY_PORT'};
>>>>>>>> +               my $new_entry_address =
>>>>>>>> $settings{'REDIR_ENTRY_ADDRESS'};
>>>>>>>> +               my $new_entry_remark =
>>>>>>>> $settings{'REDIR_ENTRY_REMARK'};
>>>>>>>> +
>>>>>>>> +               # Read-in redirectsfile.
>>>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>>>> \%redirects);
>>>>>>>> +
>>>>>>>> +               # Check if we should edit an existing
>>>>>>>> entry and
>>>>>>>> got an ID.
>>>>>>>> +               if (($settings{'ACTION'} eq
>>>>>>>> $Lang::tr{'update'}) && ($settings{'ID'})) {
>>>>>>>> +                       # Assin the provided id.
>>>>>>>> +                       $id = $settings{'ID'};
>>>>>>>> +
>>>>>>>> +                       # Undef the given ID.
>>>>>>>> +                       undef($settings{'ID'});
>>>>>>>> +
>>>>>>>> +                       # Grab the configured status of
>>>>>>>> the
>>>>>>>> corresponding entry.
>>>>>>>> +                       $status = $redirects{$id}[4];
>>>>>>>> +               } else {
>>>>>>>> +                       # Each newly added entry
>>>>>>>> automatically
>>>>>>>> should be enabled.
>>>>>>>> +                       $status = "enabled";
>>>>>>>> +
>>>>>>>> +                       # Generate the ID for the new
>>>>>>>> entry.
>>>>>>>> +                       #
>>>>>>>> +                       # Sort the keys by their ID and
>>>>>>>> store
>>>>>>>> them in an array.
>>>>>>>> +                       my @keys = sort { $a <=> $b }
>>>>>>>> keys
>>>>>>>> %redirects;
>>>>>>>> +
>>>>>>>> +                       # Reverse the key array.
>>>>>>>> +                       my @reversed = reverse(@keys);
>>>>>>>> +
>>>>>>>> +                       # Obtain the last used id.
>>>>>>>> +                       my $last_id = @reversed[0];
>>>>>>>> +
>>>>>>>> +                       # Increase the last id by one and
>>>>>>>> use
>>>>>>>> it as id for the new entry.
>>>>>>>> +                       $id = ++$last_id;
>>>>>>>> +               }
>>>>>>>> +
>>>>>>>> +               # Add/Modify the entry to/in the
>>>>>>>> redirects
>>>>>>>> hash.
>>>>>>>> +               $redirects{$id} =
>>>>>>>> ["$new_entry_interface",
>>>>>>>> "$new_entry_protocol", "$new_entry_port",
>>>>>>>> "$new_entry_address","$status", "$new_entry_remark"];
>>>>>>>> +
>>>>>>>> +               # Write the changed redirects hash to the
>>>>>>>> redirects file.
>>>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>>>> \%redirects);
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +# Toggle Enabled/Disabled for an existing entry on the
>>>>>>>> redirects list.
>>>>>>>> +
>>>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable
>>>>>>>> disable'}) {
>>>>>>>> +       my %redirects = ();
>>>>>>>> +
>>>>>>>> +       # Only go further, if an ID has been passed.
>>>>>>>> +       if ($settings{'ID'}) {
>>>>>>>> +               # Assign the given ID.
>>>>>>>> +               my $id = $settings{'ID'};
>>>>>>>> +
>>>>>>>> +               # Undef the given ID.
>>>>>>>> +               undef($settings{'ID'});
>>>>>>>> +
>>>>>>>> +               # Read-in ignoredfile.
>>>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>>>> \%redirects);
>>>>>>>> +
>>>>>>>> +               # Grab the configured status of the
>>>>>>>> corresponding entry.
>>>>>>>> +               my $status = $redirects{$id}[4];
>>>>>>>> +
>>>>>>>> +               # Switch the status.
>>>>>>>> +               if ($status eq "disabled") {
>>>>>>>> +                       $status = "enabled";
>>>>>>>> +               } else {
>>>>>>>> +                       $status = "disabled";
>>>>>>>> +               }
>>>>>>>> +
>>>>>>>> +               # Modify the status of the existing
>>>>>>>> entry.
>>>>>>>> +               $redirects{$id} = ["$redirects{$id}[0]",
>>>>>>>> "$redirects{$id}[1]", "$redirects{$id}[2]",
>>>>>>>> "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
>>>>>>>> +
>>>>>>>> +               # Write the changed ignored hash to the
>>>>>>>> redirects file.
>>>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>>>> \%redirects);
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +# Remove entry from redirects list.
>>>>>>>> +
>>>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
>>>>>>>> +       my %redirects = ();
>>>>>>>> +
>>>>>>>> +       # Read-in redirectsfile.
>>>>>>>> +       &General::readhasharray($redirectsfile,
>>>>>>>> \%redirects);
>>>>>>>> +
>>>>>>>> +       # move data on key up
>>>>>>>> +       foreach my $key (sort keys %redirects) {
>>>>>>>> +               if ($key >= $settings{'ID'}) {
>>>>>>>> +                       my $next = $key + 1;
>>>>>>>> +                       if (exists $redirects{$next}) {
>>>>>>>> +                               foreach my $i (0 ..
>>>>>>>> $#{$redirects{$next}}) { $redirects{$key}[$i] =
>>>>>>>> $redirects{$next}[$i]; }
>>>>>>>> +                       }
>>>>>>>> +               }
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       my $last_key = (sort {$a <=> $b} keys
>>>>>>>> %redirects)[-1];
>>>>>>>> +       delete $redirects{$last_key};
>>>>>>>> +
>>>>>>>> +       # Undef the given ID.
>>>>>>>> +       undef($settings{'ID'});
>>>>>>>> +
>>>>>>>> +       # Write the changed redirects hash to file.
>>>>>>>> +       &General::writehasharray($redirectsfile,
>>>>>>>> \%redirects);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +# Load settings from file
>>>>>>>> +&General::readhash($settingsfile, \%settings);
>>>>>>>> +&General::readhasharray($redirectsfile, \%redirects);
>>>>>>>> +
>>>>>>>> +# Call functions to generate whole page.
>>>>>>>> +&Header::openpage($Lang::tr{'portredir port
>>>>>>>> redirections'}, 1,
>>>>>>>> '');
>>>>>>>> +&Header::openbigbox('100%', 'left', '', $errormessage);
>>>>>>>> +
>>>>>>>> +if ($errormessage) {
>>>>>>>> +        &Header::openbox('100%', 'left',
>>>>>>>> $Lang::tr{'warning
>>>>>>>> messages'});
>>>>>>>> +        print "<font
>>>>>>>> color='red'>$errormessage&nbsp;</font>";
>>>>>>>> +        &Header::closebox();
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_A
>>>>>>>> DDON'}
>>>>>>>> } = "checked='checked'";
>>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_G
>>>>>>>> REEN'}
>>>>>>>> } = "checked='checked'";
>>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BL
>>>>>>>> UE'}}
>>>>>>>> = "checked='checked'";
>>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_
>>>>>>>> ORANGE
>>>>>>>> '}} = "checked='checked'";
>>>>>>>> +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}
>>>>>>>> } =
>>>>>>>> "checked='checked'";
>>>>>>>> +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}
>>>>>>>> } =
>>>>>>>> "checked='checked'";
>>>>>>>> +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}}
>>>>>>>> =
>>>>>>>> "checked='checked'";
>>>>>>>> +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}}
>>>>>>>> =
>>>>>>>> "checked='checked'";
>>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE
>>>>>>>> '}} =
>>>>>>>> "checked='checked'";
>>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE
>>>>>>>> '}} =
>>>>>>>> "checked='checked'";
>>>>>>>> +
>>>>>>>> +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTR
>>>>>>>> Y_INTE
>>>>>>>> RFACE'}} = 'selected';
>>>>>>>> +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY
>>>>>>>> _PROTO
>>>>>>>> COL'}} = 'selected';
>>>>>>>> +
>>>>>>>> +&showMainBox();
>>>>>>>> +&showRedirectsBox();
>>>>>>>> +
>>>>>>>> +&Header::closebigbox();
>>>>>>>> +&Header::closepage();
>>>>>>>> +
>>>>>>>> +# Function to show main settings and options.
>>>>>>>> +sub showMainBox() {
>>>>>>>> +
>>>>>>>> +       &Header::openbox('100%', 'center',
>>>>>>>> "$Lang::tr{'settings'}");
>>>>>>>> +       print "<form method='post'
>>>>>>>> action='$ENV{'SCRIPT_NAME'}'>";
>>>>>>>> +
>>>>>>>> +print <<END;
>>>>>>>> +<table width='80%' cellspacing='0' border='0'>
>>>>>>>> +       <tr><td colspan='2' class='base'
>>>>>>>> bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir
>>>>>>>> common
>>>>>>>> settings'}</b></td></tr
>>>>>>>> +       <tr>
>>>>>>>> +               <td width='25%'
>>>>>>>> class='base'>$Lang::tr{'portredir enable addon'}:</td>
>>>>>>>> +               <td><input type='checkbox'
>>>>>>>> name='REDIR_ENABLE_ADDON'
>>>>>>>> $checked{'REDIR_ENABLE_ADDON'}{'on'}
>>>>>>>> /></td>
>>>>>>>> +       </tr>
>>>>>>>> +       <tr><td colspan='2'></td></tr>
>>>>>>>> +       <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>>>> +END
>>>>>>>> +
>>>>>>>> +       # create html table with header line 1
>>>>>>>> +       print "<table width='80%' cellspacing='0'
>>>>>>>> border='0'><tr>";
>>>>>>>> +       print "<th class='base' width='40%'
>>>>>>>> align='left'><b>$Lang::tr{'portredir fw for
>>>>>>>> interface'}</th>";
>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<th
>>>>>>>> class='base' width='10%'><b><font
>>>>>>>> color=green>$Lang::tr{'green'}</font></th>";
>>>>>>>> +               } else {                 print "<th
>>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<th
>>>>>>>> class='base' width='10%'><b><font
>>>>>>>> color=blue>$Lang::tr{'blue'}</font></th>";
>>>>>>>> +               } else {                 print "<th
>>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<th
>>>>>>>> class='base' width='10%'><b><font
>>>>>>>> color=orange>$Lang::tr{'orange'}</font></th>";
>>>>>>>> +               } else {                 print "<th
>>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>>> +
>>>>>>>> +       # the empty right row
>>>>>>>> +       print "<th class='base'
>>>>>>>> width='30%'><td></td></th></tr>";
>>>>>>>> +
>>>>>>>> +       # line 2
>>>>>>>> +       print "<tr><td>$Lang::tr{'portredir force local
>>>>>>>> dns'}</td>";
>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_DNS_GREEN'
>>>>>>>> $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_DNS_BLUE'
>>>>>>>> $checked{'REDIR_DNS_BLUE'}{'on'}></td>";}
>>>>>>>> else { print "<td></td>";}
>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_DNS_ORANGE'
>>>>>>>> $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +
>>>>>>>> +       # line 3
>>>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir force
>>>>>>>> local
>>>>>>>> ntp'}</td>";
>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_NTP_GREEN'
>>>>>>>> $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_NTP_BLUE'
>>>>>>>> $checked{'REDIR_NTP_BLUE'}{'on'}></td>";}
>>>>>>>> else { print "<td></td>";}
>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_NTP_ORANGE'
>>>>>>>> $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +
>>>>>>>> +       # line 4
>>>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir enable
>>>>>>>> user
>>>>>>>> redirections'}</td>";
>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_CUSTOM_GREEN'
>>>>>>>> $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else {
>>>>>>>> print
>>>>>>>> "<td></td>";}
>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_CUSTOM_BLUE'
>>>>>>>> $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_CUSTOM_ORANGE'
>>>>>>>> $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else {
>>>>>>>> print
>>>>>>>> "<td></td>";}
>>>>>>>> +
>>>>>>>> +       print <<END;
>>>>>>>> +       </tr></table>
>>>>>>>> +       <table width='80%' cellspacing='0' border='0'>
>>>>>>>> +               <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>>>> +               <tr><td
>>>>>>>> align='left'><b>$Lang::tr{'portredir
>>>>>>>> save to activate'}</b></td><td width='5%'
>>>>>>>> align='center'><input
>>>>>>>> type='submit' name='ACTION' value='  $Lang::tr{'save'} 
>>>>>>>> '></td></tr>
>>>>>>>> +       </table></form>
>>>>>>>> +END
>>>>>>>> +
>>>>>>>> +&Header::closebox();
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +# Function to show elements of the redirects file and
>>>>>>>> allow to
>>>>>>>> add or remove single members of it.
>>>>>>>> +sub showRedirectsBox() {
>>>>>>>> +        &Header::openbox('100%', 'center',
>>>>>>>> "$Lang::tr{'portredir custom redirections'}");
>>>>>>>> +
>>>>>>>> +       print <<END;
>>>>>>>> +               <table width='80%' cellspacing='1'
>>>>>>>> border='0'>
>>>>>>>> +                       <tr>
>>>>>>>> +                               <td class='base'
>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>> align='center'><b>$Lang::tr{'interface'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>> align='center'><b>$Lang::tr{'protocol'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>> align='center'><b>$Lang::tr{'port'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>> align='center'><b>$Lang::tr{'ip
>>>>>>>> address'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>> align='center'><b>$Lang::tr{'remark'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> colspan='3'
>>>>>>>> bgcolor='$color{'color20'}'></td>
>>>>>>>> +                       </tr>
>>>>>>>> +END
>>>>>>>> +                       # Check if some rules have been
>>>>>>>> added
>>>>>>>> to be redirects.
>>>>>>>> +                       if (keys (%redirects)) {
>>>>>>>> +                               my $col = "";
>>>>>>>> +
>>>>>>>> +                               # List all entries of the
>>>>>>>> hash.
>>>>>>>> +                               foreach my $key (sort
>>>>>>>> keys
>>>>>>>> %redirects){
>>>>>>>> +
>>>>>>>> +                                       # Assign data
>>>>>>>> array
>>>>>>>> positions to some nice variable names.
>>>>>>>> +                                       my $interface =
>>>>>>>> $redirects{$key}[0];
>>>>>>>> +                                       my $protocol =
>>>>>>>> $redirects{$key}[1];
>>>>>>>> +                                       my $port  =
>>>>>>>> $redirects{$key}[2];
>>>>>>>> +                                       my $address =
>>>>>>>> $redirects{$key}[3];
>>>>>>>> +                                       my $status  =
>>>>>>>> $redirects{$key}[4];
>>>>>>>> +                                       my $remark  =
>>>>>>>> $redirects{$key}[5];
>>>>>>>> +
>>>>>>>> +                                       # Check if the
>>>>>>>> key (id)
>>>>>>>> number is even or not.
>>>>>>>> +                                       if
>>>>>>>> ($settings{'ID'} eq
>>>>>>>> $key) {
>>>>>>>> +                                              
>>>>>>>> $col="bgcolor='
>>>>>>>> ${Header::colouryellow}'";
>>>>>>>> +                                       } elsif ($key %
>>>>>>>> 2) {
>>>>>>>> +                                              
>>>>>>>> $col="bgcolor='
>>>>>>>> $color{'color22'}'";
>>>>>>>> +                                       } else {
>>>>>>>> +                                              
>>>>>>>> $col="bgcolor='
>>>>>>>> $color{'color20'}'";
>>>>>>>> +                                       }
>>>>>>>> +
>>>>>>>> +                                       # Choose icon for
>>>>>>>> the
>>>>>>>> checkbox.
>>>>>>>> +                                       my $gif;
>>>>>>>> +                                       my $gdesc;
>>>>>>>> +
>>>>>>>> +                                       # Check if the
>>>>>>>> status
>>>>>>>> is enabled and select the correct image and description.
>>>>>>>> +                                       if ($status eq
>>>>>>>> 'enabled' ) {
>>>>>>>> +                                               $gif =
>>>>>>>> 'on.gif';
>>>>>>>> +                                               $gdesc =
>>>>>>>> $Lang::tr{'click to disable'};
>>>>>>>> +                                       } else {
>>>>>>>> +                                               $gif =
>>>>>>>> 'off.gif';
>>>>>>>> +                                               $gdesc =
>>>>>>>> $Lang::tr{'click to enable'};
>>>>>>>> +                                       }
>>>>>>>> +
>>>>>>>> +                                       print <<END;
>>>>>>>> +                                       <tr>
>>>>>>>> +                                               <td
>>>>>>>> width='15%'
>>>>>>>> class='base' align='center' $col><b><font
>>>>>>>> color=$interface>$Lang::tr{$interface}</font></b></td>
>>>>>>>> +                                               <td
>>>>>>>> width='10%'
>>>>>>>> class='base' align='center' $col>$protocol</td>
>>>>>>>> +                                               <td
>>>>>>>> width='10%'
>>>>>>>> class='base' align='center' $col>$port</td>
>>>>>>>> +                                               <td
>>>>>>>> width='15%'
>>>>>>>> class='base' align='center' $col>&nbsp;$address</td>
>>>>>>>> +                                               <td
>>>>>>>> width='40%'
>>>>>>>> class='base' align='center' $col>&nbsp;$remark</td>
>>>>>>>> +                                               <td
>>>>>>>> align='center' $col>
>>>>>>>> +                                                      
>>>>>>>> <form
>>>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>> +                                                        
>>>>>>>>       
>>>>>>>>  <input type='hidden' name='ACTION'
>>>>>>>> value='$Lang::tr{'toggle
>>>>>>>> enable disable'}' />
>>>>>>>> +                                                        
>>>>>>>>       
>>>>>>>>  <input type='image' name='$Lang::tr{'toggle enable
>>>>>>>> disable'}'
>>>>>>>> src='/images/$gif' alt='$gdesc' title='$gdesc' />
>>>>>>>> +                                                        
>>>>>>>>       
>>>>>>>>  <input type='hidden' name='ID' value='$key' />
>>>>>>>> +                                                      
>>>>>>>> </form>
>>>>>>>> +                                               </td>
>>>>>>>> +                                               <td
>>>>>>>> align='center' $col>
>>>>>>>> +                                                      
>>>>>>>> <form
>>>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>> +                                                        
>>>>>>>>       
>>>>>>>>  <input type='hidden' name='ACTION'
>>>>>>>> value='$Lang::tr{'edit'}'
>>>>>>>> />
>>>>>>>> +                                                        
>>>>>>>>       
>>>>>>>>  <input type='image' name='$Lang::tr{'edit'}'
>>>>>>>> src='/images/edit.gif' alt='$Lang::tr{'edit'}'
>>>>>>>> title='$Lang::tr{'edit'}' />
>>>>>>>> +                                                        
>>>>>>>>       
>>>>>>>>  <input type='hidden' name='ID' value='$key' />
>>>>>>>> +                                                      
>>>>>>>> </form>
>>>>>>>> +                                               </td>
>>>>>>>> +                                               <td
>>>>>>>> align='center' $col>
>>>>>>>> +                                                      
>>>>>>>> <form
>>>>>>>> method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>> +                                                        
>>>>>>>>       
>>>>>>>>  <input type='image' name='$Lang::tr{'remove'}'
>>>>>>>> src='/images/delete.gif' title='$Lang::tr{'remove'}'
>>>>>>>> alt='$Lang::tr{'remove'}'>
>>>>>>>> +                                                        
>>>>>>>>       
>>>>>>>>  <input type='hidden' name='ID' value='$key'>
>>>>>>>> +                                                        
>>>>>>>>       
>>>>>>>>  <input type='hidden' name='ACTION'
>>>>>>>> value='$Lang::tr{'remove'}'>
>>>>>>>> +                                                      
>>>>>>>> </form>
>>>>>>>> +                                               </td>
>>>>>>>> +                                       </tr>
>>>>>>>> +END
>>>>>>>> +                               }
>>>>>>>> +                       } else {
>>>>>>>> +                               # Print notice that
>>>>>>>> currently
>>>>>>>> no ports are redirected.
>>>>>>>> +                               print "<tr>\n";
>>>>>>>> +                               print "<td class='base'
>>>>>>>> colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
>>>>>>>> +                               print "</tr>\n";
>>>>>>>> +                       }
>>>>>>>> +
>>>>>>>> +               print "</table>\n";
>>>>>>>> +
>>>>>>>> +       # Section to add new elements or edit existing
>>>>>>>> ones.
>>>>>>>> +       print <<END;
>>>>>>>> +       <br>
>>>>>>>> +       <hr>
>>>>>>>> +       <br>
>>>>>>>> +       <div align='center'>
>>>>>>>> +               <table width='100%' cellspacing='0'
>>>>>>>> border='0'>
>>>>>>>> +END
>>>>>>>> +
>>>>>>>> +       # Assign correct headline and button text.
>>>>>>>> +       my $buttontext;
>>>>>>>> +       my $entry_interface;
>>>>>>>> +       my $entry_protocol;
>>>>>>>> +       my $entry_port;
>>>>>>>> +       my $entry_address;
>>>>>>>> +       my $entry_remark;
>>>>>>>> +
>>>>>>>> +       # Check if an ID (key) has been given, in this
>>>>>>>> case an
>>>>>>>> existing entry should be edited.
>>>>>>>> +       if ($settings{'ID'} ne '') {
>>>>>>>> +               $buttontext = $Lang::tr{'update'};
>>>>>>>> +               print "<tr><td class='boldbase'
>>>>>>>> colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
>>>>>>>> +
>>>>>>>> +               # Grab address and remark for the given
>>>>>>>> key.
>>>>>>>> +               $entry_interface =
>>>>>>>> $redirects{$settings{'ID'}}[0];
>>>>>>>> +               $entry_protocol =
>>>>>>>> $redirects{$settings{'ID'}}[1];
>>>>>>>> +               $entry_port =
>>>>>>>> $redirects{$settings{'ID'}}[2];
>>>>>>>> +               $entry_address =
>>>>>>>> $redirects{$settings{'ID'}}[3];
>>>>>>>> +               $entry_remark =
>>>>>>>> $redirects{$settings{'ID'}}[5];
>>>>>>>> +
>>>>>>>> +       } else {
>>>>>>>> +               $buttontext = $Lang::tr{'add'};
>>>>>>>> +               print "<tr><td class='boldbase'
>>>>>>>> colspan='11'><b>$Lang::tr{'dnsforward add a new
>>>>>>>> entry'}</b></td></tr>\n";
>>>>>>>> +               print "<tr><td>&nbsp</td></tr>\n";
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       print <<END;
>>>>>>>> +                       <tr>
>>>>>>>> +                               <td class='base'
>>>>>>>> width='1%' 
>>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> width='15%'
>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>> align='left'><b>$Lang::tr{'interface'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> width='10%'
>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>> align='left'><b>$Lang::tr{'protocol'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> width='10%'
>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>> align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> width='13%'
>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>> align='left'>&nbsp;<b>$Lang::tr{'ip
>>>>>>>> address'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> width='30%'
>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>> align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> width='15%'
>>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> width='1%' 
>>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>>> +                       </tr>
>>>>>>>> +
>>>>>>>> +                       <form method='post'
>>>>>>>> action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>> +                       <input type='hidden' name='ID'
>>>>>>>> value='$settings{'ID'}'>
>>>>>>>> +                       <tr>
>>>>>>>> +                               <td class='base'></td>
>>>>>>>> +                               <td><select
>>>>>>>> style='width:90px;'
>>>>>>>> id='interface' name='REDIR_ENTRY_INTERFACE'>
>>>>>>>> +END
>>>>>>>> +                               if
>>>>>>>> ($netsettings{'GREEN_DEV'})
>>>>>>>> {
>>>>>>>> +                                       if
>>>>>>>> ($entry_interface eq
>>>>>>>> "green") {
>>>>>>>> +                                               print
>>>>>>>> "<option
>>>>>>>> value='green' selected='selected'
>>>>>>>> {'green'}>$Lang::tr{'green'}</option>";
>>>>>>>> +                                       } else { print
>>>>>>>> "<option
>>>>>>>> value='green' {'green'}>$Lang::tr{'green'}</option>";}
>>>>>>>> +                               }
>>>>>>>> +                               if
>>>>>>>> ($netsettings{'BLUE_DEV'}) {
>>>>>>>> +                                       if
>>>>>>>> ($entry_interface eq
>>>>>>>> "blue") { 
>>>>>>>> +                                               print
>>>>>>>> "<option
>>>>>>>> value='blue' selected='selected'
>>>>>>>> {'blue'}>$Lang::tr{'blue'}</option>";
>>>>>>>> +                                       } else { print
>>>>>>>> "<option
>>>>>>>> value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
>>>>>>>> +                               }
>>>>>>>> +                               if
>>>>>>>> ($netsettings{'ORANGE_DEV'})
>>>>>>>> {
>>>>>>>> +                                       if
>>>>>>>> ($entry_interface eq
>>>>>>>> "orange") { 
>>>>>>>> +                                               print
>>>>>>>> "<option
>>>>>>>> value='orange' selected='selected'
>>>>>>>> {'orange'}>$Lang::tr{'orange'}</option>";
>>>>>>>> +                                       } else { print
>>>>>>>> "<option
>>>>>>>> value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
>>>>>>>> +                               }
>>>>>>>> +
>>>>>>>> +                       print "</select><td><select
>>>>>>>> style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
>>>>>>>> +                       if ((!$entry_protocol) ||
>>>>>>>> ($entry_protocol eq "tcp")) {
>>>>>>>> +                               print "<option
>>>>>>>> selected='selected' id='protocol' value='tcp'
>>>>>>>> {'tcp'}>tcp</option>";
>>>>>>>> +                               print "<option
>>>>>>>> value='udp'
>>>>>>>> {'udp'}>udp</option>";
>>>>>>>> +                       } elsif ($entry_protocol eq
>>>>>>>> "udp") {
>>>>>>>> +                               print "<option
>>>>>>>> value='tcp'
>>>>>>>> {'tcp'}>tcp</option>";
>>>>>>>> +                               print "<option
>>>>>>>> selected='selected' value='udp' {'udp'}>udp</option>";
>>>>>>>> +                       }
>>>>>>>> +       print <<END;
>>>>>>>> +                               </select></td>
>>>>>>>> +                               <td><input type='text'
>>>>>>>> name='REDIR_ENTRY_PORT'    value='$entry_port'   
>>>>>>>> size='4'></td>
>>>>>>>> +                               <td><input type='text'
>>>>>>>> name='REDIR_ENTRY_ADDRESS' value='$entry_address'
>>>>>>>> size='14'></td>
>>>>>>>> +                               <td><input type='text'
>>>>>>>> name='REDIR_ENTRY_REMARK'  value='$entry_remark' 
>>>>>>>> size='35'></td>
>>>>>>>> +                               <td width='10%'
>>>>>>>> align='center'><input type='submit' name='ACTION' value='
>>>>>>>> $buttontext  '></td>
>>>>>>>> +                               <td class='base'></td>
>>>>>>>> +                       </tr>
>>>>>>>> +                       </form>
>>>>>>>> +               </table>
>>>>>>>> +       </div>
>>>>>>>> +END
>>>>>>>> +       &Header::closebox();
>>>>>>>> +}
>>>>>>>> diff --git a/config/rootfiles/common/misc-progs
>>>>>>>> b/config/rootfiles/common/misc-progs
>>>>>>>> index d6594b3f8..fbad2af8b 100644
>>>>>>>> --- a/config/rootfiles/common/misc-progs
>>>>>>>> +++ b/config/rootfiles/common/misc-progs
>>>>>>>> @@ -17,6 +17,7 @@ usr/local/bin/logwatch
>>>>>>>> #usr/local/bin/mpfirectrl
>>>>>>>> usr/local/bin/openvpnctrl
>>>>>>>> usr/local/bin/pakfire
>>>>>>>> +#usr/local/bin/portredirctrl
>>>>>>>> usr/local/bin/qosctrl
>>>>>>>> usr/local/bin/rebuildhosts
>>>>>>>> usr/local/bin/rebuildroutes
>>>>>>>> diff --git a/config/rootfiles/packages/portredir
>>>>>>>> b/config/rootfiles/packages/portredir
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..4b4ba8366
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/rootfiles/packages/portredir
>>>>>>>> @@ -0,0 +1,11 @@
>>>>>>>> +etc/rc.d/init.d/portredir
>>>>>>>> +etc/rc.d/rc0.d/K77portredir
>>>>>>>> +etc/rc.d/rc3.d/S23portredir
>>>>>>>> +etc/rc.d/rc6.d/K77portredir
>>>>>>>> +srv/web/ipfire/cgi-bin/portredir.cgi
>>>>>>>> +usr/local/bin/portredirctrl
>>>>>>>> +var/ipfire/addon-lang/portredir.de.pl
>>>>>>>> +var/ipfire/addon-lang/portredir.en.pl
>>>>>>>> +var/ipfire/backup/addons/includes/portredir
>>>>>>>> +var/ipfire/menu.d/EX-portredir.menu
>>>>>>>> +var/ipfire/portredir
>>>>>>>> diff --git a/lfs/portredir b/lfs/portredir
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..a4911f71f
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/lfs/portredir
>>>>>>>> @@ -0,0 +1,85 @@
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> #################
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>                 #
>>>>>>>> +# IPFire.org - A linux based
>>>>>>>> firewall                                         #
>>>>>>>> +# Copyright (C) 2007-2021  IPFire Team 
>>>>>>>> <info@ipfire.org>                     #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>                 #
>>>>>>>> +# This program is free software: you can redistribute it
>>>>>>>> and/or modify        #
>>>>>>>> +# it under the terms of the GNU General Public License
>>>>>>>> as
>>>>>>>> published by        #
>>>>>>>> +# the Free Software Foundation, either version 3 of the
>>>>>>>> License, or           #
>>>>>>>> +# (at your option) any later
>>>>>>>> version.                                         #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>                 #
>>>>>>>> +# This program is distributed in the hope that it will
>>>>>>>> be
>>>>>>>> useful,             #
>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied
>>>>>>>> warranty
>>>>>>>> of              #
>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
>>>>>>>> See
>>>>>>>> the               #
>>>>>>>> +# GNU General Public License for more
>>>>>>>> details.                                #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>                 #
>>>>>>>> +# You should have received a copy of the GNU General
>>>>>>>> Public
>>>>>>>> License           #
>>>>>>>> +# along with this program.  If not, see <
>>>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>                 #
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> #################
>>>>>>>> +
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> #################
>>>>>>>> +# Definitions
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> #################
>>>>>>>> +
>>>>>>>> +include Config
>>>>>>>> +
>>>>>>>> +VER        = 1.0
>>>>>>>> +
>>>>>>>> +THISAPP    = portredir-$(VER)
>>>>>>>> +DIR_APP    = $(DIR_SRC)/$(THISAPP)
>>>>>>>> +TARGET     = $(DIR_INFO)/$(THISAPP)
>>>>>>>> +PROG       = portredir
>>>>>>>> +PAK_VER    = 1
>>>>>>>> +
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> #################
>>>>>>>> +# Top-level Rules
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> #################
>>>>>>>> +
>>>>>>>> +install : $(TARGET)
>>>>>>>> +
>>>>>>>> +check :
>>>>>>>> +
>>>>>>>> +download :
>>>>>>>> +
>>>>>>>> +md5 :
>>>>>>>> +
>>>>>>>> +dist: 
>>>>>>>> +       @$(PAK)
>>>>>>>> +
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> #################
>>>>>>>> +# Installation Details
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> #################
>>>>>>>> +
>>>>>>>> +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
>>>>>>>> +       @$(PREBUILD)
>>>>>>>> +       @rm -rf $(DIR_APP) && cd $(DIR_SRC)
>>>>>>>> +
>>>>>>>> +       #install cgi 
>>>>>>>> +       install -v -m 755
>>>>>>>> $(DIR_CONF)/portredir/portredir.cgi
>>>>>>>> /srv/web/ipfire/cgi-bin/
>>>>>>>> +
>>>>>>>> +       #create configuration dir 
>>>>>>>> +       -mkdir -pv /var/ipfire/portredir/
>>>>>>>> +       chown -R nobody:nobody /var/ipfire/portredir/
>>>>>>>> +
>>>>>>>> +       # Install include file for backup
>>>>>>>> +       install -v -m 644
>>>>>>>> $(DIR_CONF)/portredir/portredir-
>>>>>>>> backup /var/ipfire/backup/addons/includes/portredir
>>>>>>>> +
>>>>>>>> +       # Install menu file
>>>>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/EX-
>>>>>>>> portredir.menu /var/ipfire/menu.d/
>>>>>>>> +       chown nobody:nobody /var/ipfire/menu.d/EX-
>>>>>>>> portredir.menu
>>>>>>>> +
>>>>>>>> +       # Install addon-specific language-files
>>>>>>>> +       install -v -m 644
>>>>>>>> $(DIR_CONF)/portredir/lang/portredir.*.pl
>>>>>>>> /var/ipfire/addon-
>>>>>>>> lang/
>>>>>>>> +
>>>>>>>> +       #install initscripts
>>>>>>>> +       $(call INSTALL_INITSCRIPT,portredir)
>>>>>>>> +
>>>>>>>> +       # Create symlinks for runlevel interaction.
>>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc3.d/S23portredir
>>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc0.d/K77portredir
>>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc6.d/K77portredir
>>>>>>>> +
>>>>>>>> +       @rm -rf $(DIR_APP)
>>>>>>>> +       @$(POSTBUILD)
>>>>>>>> +
>>>>>>>> diff --git a/make.sh b/make.sh
>>>>>>>> index fc03ebcd5..ab9fe881a 100755
>>>>>>>> --- a/make.sh
>>>>>>>> +++ b/make.sh
>>>>>>>> @@ -1623,6 +1623,7 @@ buildipfire() {
>>>>>>>>  lfsmake2 socat
>>>>>>>>  lfsmake2 libcdada
>>>>>>>>  lfsmake2 pmacct
>>>>>>>> +  lfsmake2 portredir
>>>>>>>> }
>>>>>>>> 
>>>>>>>> buildinstaller() {
>>>>>>>> diff --git a/src/initscripts/packages/portredir
>>>>>>>> b/src/initscripts/packages/portredir
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..cc57fb9cc
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/src/initscripts/packages/portredir
>>>>>>>> @@ -0,0 +1,191 @@
>>>>>>>> +#!/bin/sh
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> ##########
>>>>>>>> +# Begin $rc_base/init.d/portredir
>>>>>>>> +#
>>>>>>>> +# Description : portredir init script for DNS/NTP and
>>>>>>>> custom 
>>>>>>>> +#              port redirection rules
>>>>>>>> +#
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> ##########
>>>>>>>> +
>>>>>>>> +. /etc/sysconfig/rc
>>>>>>>> +. ${rc_functions}
>>>>>>>> +
>>>>>>>> +IPT="/sbin/iptables";
>>>>>>>> +parent_chain="PREROUTING";
>>>>>>>> +chain="PORT_REDIRECT";
>>>>>>>> +
>>>>>>>> +confdir="/var/ipfire/portredir";
>>>>>>>> +settingsfile="${confdir}/settings";
>>>>>>>> +redirectsfile="${confdir}/redirects";
>>>>>>>> +SYSLOG="NO";
>>>>>>>> +VERBOSE="NO";
>>>>>>>> +
>>>>>>>> +eval $(/usr/local/bin/readhash
>>>>>>>> /var/ipfire/ethernet/settings);
>>>>>>>> +eval $(/usr/local/bin/readhash ${settingsfile});
>>>>>>>> +
>>>>>>>> +logtext() {
>>>>>>>> +       if [ "${SYSLOG}" = "YES" ]; then logger -t
>>>>>>>> "portredir"
>>>>>>>> ${1}; fi;
>>>>>>>> +       if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
>>>>>>>> +
>>>>>>>> +create_chain() {
>>>>>>>> +
>>>>>>>> +       local line=$(${IPT} -t nat -L ${parent_chain} --
>>>>>>>> line-
>>>>>>>> numbers |grep "SQUID" |awk '{printf($1)}');
>>>>>>>> +
>>>>>>>> +       if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z
>>>>>>>> "${REDIR_ENABLE_ADDON}" ]]; then
>>>>>>>> +               logtext "addon not enabled in web
>>>>>>>> interface...";
>>>>>>>> +               echo "Portredir addon not enabled in web
>>>>>>>> interface...";
>>>>>>>> +               exit 0;
>>>>>>>> +       fi;
>>>>>>>> +
>>>>>>>> +       if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>>>>> ${chain})" ]; then
>>>>>>>> +               ${IPT} -t nat -N ${chain};
>>>>>>>> +
>>>>>>>> +               if [ ! -z "${line}" ]; then
>>>>>>>> +                       logtext "create chain ${chain}
>>>>>>>> and link
>>>>>>>> in ${parent_chain} at position ${line}...";
>>>>>>>> +                       ${IPT} -t nat -I ${parent_chain}
>>>>>>>> ${line} -j ${chain};
>>>>>>>> +               else
>>>>>>>> +                       logtext "create chain ${chain}
>>>>>>>> and link
>>>>>>>> in ${parent_chain} at last position...";
>>>>>>>> +                       ${IPT} -t nat -A ${parent_chain}
>>>>>>>> -j
>>>>>>>> ${chain};
>>>>>>>> +               fi
>>>>>>>> +       else
>>>>>>>> +               return 1;
>>>>>>>> +       fi;
>>>>>>>> +       return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +remove_chain() {
>>>>>>>> +       if [ ! -z "$(${IPT} -t nat -L ${parent_chain}
>>>>>>>> |grep
>>>>>>>> ${chain})" ]; then
>>>>>>>> +               logtext "remove chain ${chain} and link
>>>>>>>> in
>>>>>>>> ${parent_chain} from system...";
>>>>>>>> +               ${IPT} -t nat -D "${parent_chain}" -j
>>>>>>>> ${chain};
>>>>>>>> +               ${IPT} -t nat -F ${chain};
>>>>>>>> +               ${IPT} -t nat -X ${chain};
>>>>>>>> +       else
>>>>>>>> +               return 1;
>>>>>>>> +       fi;
>>>>>>>> +       return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +activate_custom_redirections() {
>>>>>>>> +       
>>>>>>>> +       local array=();
>>>>>>>> +       local redirects=();
>>>>>>>> +       local i;
>>>>>>>> +       index=();
>>>>>>>> +       iface=();
>>>>>>>> +       protocol=();
>>>>>>>> +       port=();
>>>>>>>> +       targetip=();
>>>>>>>> +       enabled=();
>>>>>>>> +
>>>>>>>> +       IFS=$'\n' read -d '' -ra redirects <
>>>>>>>> ${redirectsfile};
>>>>>>>> +
>>>>>>>> +       for i in "${!redirects[@]}"
>>>>>>>> +       do
>>>>>>>> +               IFS=$',' read -ra array <<<
>>>>>>>> ${redirects[i]};
>>>>>>>> +               index[i]=${array[0]};
>>>>>>>> +               iface[i]=${array[1]};
>>>>>>>> +               protocol[i]=${array[2]};
>>>>>>>> +               port[i]=${array[3]};
>>>>>>>> +               targetip[i]=${array[4]};
>>>>>>>> +               enabled[i]=${array[5]};
>>>>>>>> +       done
>>>>>>>> +
>>>>>>>> +       for i in "${!index[@]}"
>>>>>>>> +       do
>>>>>>>> +               if [[ ! -z "${GREEN_DEV}" && 
>>>>>>>> "${iface[i]}" =
>>>>>>>> "green" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>>> +
>>>>>>>> +                       logtext "add redirect in ${chain}
>>>>>>>> on
>>>>>>>> ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]}
>>>>>>>> port
>>>>>>>> ${port[i]} ";
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>>> +               fi
>>>>>>>> +               if [[ ! -z "${BLUE_DEV}" && 
>>>>>>>> "${iface[i]}" =
>>>>>>>> "blue" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>>> +                       logtext "add redirect in ${chain}
>>>>>>>> on
>>>>>>>> ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]}
>>>>>>>> port
>>>>>>>> ${port[i]} ";
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>>> +               fi
>>>>>>>> +               if [[ ! -z "${ORANGE_DEV}" && 
>>>>>>>> "${iface[i]}" =
>>>>>>>> "orange" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>>> +                       logtext "add redirect in ${chain}
>>>>>>>> on
>>>>>>>> ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]}
>>>>>>>> port
>>>>>>>> ${port[i]} ";
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>>> +               fi
>>>>>>>> +       done
>>>>>>>> +       unset array redirects i index iface protocol port
>>>>>>>> targetip enabled;
>>>>>>>> +       return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +activate_redirections() {
>>>>>>>> +
>>>>>>>> +       if ! create_chain; then return 1; fi;
>>>>>>>> +       
>>>>>>>> +       # Force DNS REDIRECTs on GREEN (udp, tcp, 53)
>>>>>>>> +       if [[ "${REDIR_DNS_GREEN}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV}
>>>>>>>> -d
>>>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV}
>>>>>>>> -p
>>>>>>>> udp -m udp --dport domain -j REDIRECT;
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV}
>>>>>>>> -d
>>>>>>>> ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV}
>>>>>>>> -p
>>>>>>>> tcp -m tcp --dport domain -j REDIRECT;
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       # Force DNS REDIRECTs on BLUE (udp, tcp, 53)
>>>>>>>> +       if [[ "${REDIR_DNS_BLUE}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV}
>>>>>>>> -d
>>>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV}
>>>>>>>> -p udp
>>>>>>>> -m udp --dport domain -j REDIRECT
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV}
>>>>>>>> -d
>>>>>>>> ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV}
>>>>>>>> -p tcp
>>>>>>>> -m tcp --dport domain -j REDIRECT
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       # Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
>>>>>>>> +       if [[ "${REDIR_DNS_ORANGE}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${ORANGE_DEV} -d
>>>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${ORANGE_DEV} -p
>>>>>>>> udp -m udp --dport domain -j REDIRECT
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${ORANGE_DEV} -d
>>>>>>>> ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${ORANGE_DEV} -p
>>>>>>>> tcp -m tcp --dport domain -j REDIRECT
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       # Force NTP REDIRECTs on GREEN (udp, 123)
>>>>>>>> +       if [[ "${REDIR_NTP_GREEN}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV}
>>>>>>>> -d
>>>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV}
>>>>>>>> -p
>>>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       # Force NTP REDIRECTs on BLUE (udp, 123)
>>>>>>>> +       if [[ "${REDIR_NTP_BLUE}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV}
>>>>>>>> -d
>>>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV}
>>>>>>>> -p udp
>>>>>>>> -m udp --dport ntp -j REDIRECT
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       # Force NTP REDIRECTs on ORANGE (udp, 123)
>>>>>>>> +       if [[ "${REDIR_NTP_ORANGE}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${ORANGE_DEV} -d
>>>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${ORANGE_DEV} -p
>>>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       if ! activate_custom_redirections; then return 1;
>>>>>>>> fi;
>>>>>>>> +
>>>>>>>> +       return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +case "${1}" in
>>>>>>>> +       start)
>>>>>>>> +               boot_mesg "Loading port redirections..."
>>>>>>>> +               activate_redirections;
>>>>>>>> +               evaluate_retval;
>>>>>>>> +               ;;
>>>>>>>> +
>>>>>>>> +       stop)   
>>>>>>>> +               boot_mesg "Removing port redirections..."
>>>>>>>> +               remove_chain;
>>>>>>>> +               evaluate_retval;
>>>>>>>> +               ;;
>>>>>>>> +
>>>>>>>> +       restart)
>>>>>>>> +               ${0} stop
>>>>>>>> +               ${0} start
>>>>>>>> +               ;;
>>>>>>>> +
>>>>>>>> +       *)
>>>>>>>> +               echo "Usage: ${0} {start|stop|restart}"
>>>>>>>> +               exit 1
>>>>>>>> +               ;;
>>>>>>>> +esac
>>>>>>>> +
>>>>>>>> +# End $rc_base/init.d/portredir
>>>>>>>> diff --git a/src/misc-progs/Makefile b/src/misc-
>>>>>>>> progs/Makefile
>>>>>>>> index 7c3ef7529..850f8fdcc 100644
>>>>>>>> --- a/src/misc-progs/Makefile
>>>>>>>> +++ b/src/misc-progs/Makefile
>>>>>>>> @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl
>>>>>>>> ipfirereboot \
>>>>>>>>         wirelessctrl getipstat qosctrl \
>>>>>>>>         redctrl syslogdctrl extrahdctrl sambactrl \
>>>>>>>>         smartctrl clamavctrl addonctrl pakfire mpfirectrl
>>>>>>>> wlanapctrl \
>>>>>>>> -       setaliases urlfilterctrl updxlratorctrl
>>>>>>>> fireinfoctrl
>>>>>>>> rebuildroutes \
>>>>>>>> +       setaliases urlfilterctrl updxlratorctrl
>>>>>>>> fireinfoctrl
>>>>>>>> rebuildroutes portredirctrl \
>>>>>>>>         getconntracktable wirelessclient torctrl ddnsctrl
>>>>>>>> unboundctrl \
>>>>>>>>         captivectrl
>>>>>>>> 
>>>>>>>> diff --git a/src/misc-progs/portredirctrl.c b/src/misc-
>>>>>>>> progs/portredirctrl.c
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..7897d711c
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/src/misc-progs/portredirctrl.c
>>>>>>>> @@ -0,0 +1,47 @@
>>>>>>>> +/* This file is part of the IPFire Firewall.
>>>>>>>> + *
>>>>>>>> + * This program is distributed under the terms of the
>>>>>>>> GNU
>>>>>>>> General Public
>>>>>>>> + * Licence.  See the file COPYING for details.
>>>>>>>> + *
>>>>>>>> + */
>>>>>>>> +
>>>>>>>> +#include <stdlib.h>
>>>>>>>> +#include <stdio.h>
>>>>>>>> +#include <string.h>
>>>>>>>> +#include <unistd.h>
>>>>>>>> +#include <sys/types.h>
>>>>>>>> +#include <fcntl.h>
>>>>>>>> +#include "setuid.h"
>>>>>>>> +
>>>>>>>> +int main(int argc, char *argv[]) {
>>>>>>>> +       if (!(initsetuid()))
>>>>>>>> +               exit(1);
>>>>>>>> +
>>>>>>>> +       // Check what command is asked
>>>>>>>> +        if (argc < 2) {
>>>>>>>> +                fprintf(stderr, "\nNo argument
>>>>>>>> given.\n\nportredirctrl
>>>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>>>> +                exit(1);
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>> +        if (strcmp(argv[1], "start") == 0) {
>>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>>> start");
>>>>>>>> +        } else if (strcmp(argv[1], "stop") == 0) {
>>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>>> stop");
>>>>>>>> +        } else if (strcmp(argv[1], "restart") == 0) {
>>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>>> restart");
>>>>>>>> +       } else if (strcmp(argv[1], "enable") == 0) {
>>>>>>>> +               safe_system("touch
>>>>>>>> /var/ipfire/portredir/enable");
>>>>>>>> +               safe_system("ln -snf
>>>>>>>> /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
>>>>>>>> +               safe_system("ln -snf
>>>>>>>> /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
>>>>>>>> +               safe_system("ln -snf
>>>>>>>> /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
>>>>>>>> +       } else if (strcmp(argv[1], "disable") == 0) {
>>>>>>>> +               safe_system("/etc/rc.d/init.d/portredir
>>>>>>>> stop");
>>>>>>>> +               safe_system("unlink
>>>>>>>> /var/ipfire/portredir/enable");
>>>>>>>> +               safe_system("rm -rf
>>>>>>>> /etc/rc.d/rc*.d/*portredir
>>>>>>>>> /dev/null 2>&1");
>>>>>>>> +        } else {
>>>>>>>> +                fprintf(stderr, "\nBad argument
>>>>>>>> given.\n\nportredirctrl
>>>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>>>> +                exit(1);
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>> +       return 0;
>>>>>>>> +}
>>>>>>>> diff --git a/src/paks/portredir/install.sh
>>>>>>>> b/src/paks/portredir/install.sh
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..9f69aeae2
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/src/paks/portredir/install.sh
>>>>>>>> @@ -0,0 +1,32 @@
>>>>>>>> +#!/bin/bash
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> ##############
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +# This file is part of the IPFire
>>>>>>>> Firewall.                                #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +# IPFire is free software; you can redistribute it
>>>>>>>> and/or
>>>>>>>> modify           #
>>>>>>>> +# it under the terms of the GNU General Public License
>>>>>>>> as
>>>>>>>> published by     #
>>>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>>>> License, or        #
>>>>>>>> +# (at your option) any later
>>>>>>>> version.                                      #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>>>> useful,                #
>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied
>>>>>>>> warranty
>>>>>>>> of           #
>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
>>>>>>>> See
>>>>>>>> the            #
>>>>>>>> +# GNU General Public License for more
>>>>>>>> details.                             #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +# You should have received a copy of the GNU General
>>>>>>>> Public
>>>>>>>> License        #
>>>>>>>> +# along with IPFire; if not, write to the Free
>>>>>>>> Software                    #
>>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
>>>>>>>> MA 
>>>>>>>> 02111-1307 USA #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +# Copyright (C) 2021 IPFire-Team
>>>>>>>> <info@ipfire.org>.                        #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> ##############
>>>>>>>> +#
>>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>>> +extract_files
>>>>>>>> +restore_backup ${NAME}
>>>>>>>> +
>>>>>>>> +/usr/local/bin/update-lang-cache
>>>>>>>> +
>>>>>>>> +chown root:nobody /usr/local/bin/portredirctrl
>>>>>>>> +chmod 4750 /usr/local/bin/portredirctrl
>>>>>>>> +chmod u+s /usr/local/bin/portredirctrl
>>>>>>>> diff --git a/src/paks/portredir/uninstall.sh
>>>>>>>> b/src/paks/portredir/uninstall.sh
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..df9270125
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/src/paks/portredir/uninstall.sh
>>>>>>>> @@ -0,0 +1,28 @@
>>>>>>>> +#!/bin/bash
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> ##############
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +# This file is part of the IPFire
>>>>>>>> Firewall.                                #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +# IPFire is free software; you can redistribute it
>>>>>>>> and/or
>>>>>>>> modify           #
>>>>>>>> +# it under the terms of the GNU General Public License
>>>>>>>> as
>>>>>>>> published by     #
>>>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>>>> License, or        #
>>>>>>>> +# (at your option) any later
>>>>>>>> version.                                      #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>>>> useful,                #
>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied
>>>>>>>> warranty
>>>>>>>> of           #
>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
>>>>>>>> See
>>>>>>>> the            #
>>>>>>>> +# GNU General Public License for more
>>>>>>>> details.                             #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +# You should have received a copy of the GNU General
>>>>>>>> Public
>>>>>>>> License        #
>>>>>>>> +# along with IPFire; if not, write to the Free
>>>>>>>> Software                    #
>>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
>>>>>>>> MA 
>>>>>>>> 02111-1307 USA #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>>>> <info@ipfire.org>.                        #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> ##############
>>>>>>>> +#
>>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>>> +make_backup ${NAME}
>>>>>>>> +remove_files
>>>>>>>> +
>>>>>>>> +/usr/local/bin/update-lang-cache
>>>>>>>> diff --git a/src/paks/portredir/update.sh
>>>>>>>> b/src/paks/portredir/update.sh
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..89c40d0d7
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/src/paks/portredir/update.sh
>>>>>>>> @@ -0,0 +1,26 @@
>>>>>>>> +#!/bin/bash
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> ##############
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +# This file is part of the IPFire
>>>>>>>> Firewall.                                #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +# IPFire is free software; you can redistribute it
>>>>>>>> and/or
>>>>>>>> modify           #
>>>>>>>> +# it under the terms of the GNU General Public License
>>>>>>>> as
>>>>>>>> published by     #
>>>>>>>> +# the Free Software Foundation; either version 2 of the
>>>>>>>> License, or        #
>>>>>>>> +# (at your option) any later
>>>>>>>> version.                                      #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +# IPFire is distributed in the hope that it will be
>>>>>>>> useful,                #
>>>>>>>> +# but WITHOUT ANY WARRANTY; without even the implied
>>>>>>>> warranty
>>>>>>>> of           #
>>>>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
>>>>>>>> See
>>>>>>>> the            #
>>>>>>>> +# GNU General Public License for more
>>>>>>>> details.                             #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +# You should have received a copy of the GNU General
>>>>>>>> Public
>>>>>>>> License        #
>>>>>>>> +# along with IPFire; if not, write to the Free
>>>>>>>> Software                    #
>>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
>>>>>>>> MA 
>>>>>>>> 02111-1307 USA #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>>>> <info@ipfire.org>.                        #
>>>>>>>> +#                                                       
>>>>>>>>       
>>>>>>>>              #
>>>>>>>> +########################################################
>>>>>>>> ######
>>>>>>>> ##############
>>>>>>>> +#
>>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>>> +./uninstall.sh
>>>>>>>> +./install.sh
>>>>>>>> -- 
>>>>>>>> 2.18.0
>>>>>>>> 
>>>>>>> 
>>>>>> 
>>>>> 
>>>> 
>>> 
>> 
>
  

Patch

diff --git a/config/portredir/EX-portredir.menu b/config/portredir/EX-portredir.menu
new file mode 100644
index 000000000..8376e8053
--- /dev/null
+++ b/config/portredir/EX-portredir.menu
@@ -0,0 +1,6 @@ 
+    $subfirewall->{'95.portredir'} = {
+				'caption' => $Lang::tr{'portredir port redirections'},
+				'uri' => '/cgi-bin/portredir.cgi',
+				'title' => "$Lang::tr{'portredir port redirections'}",
+				'enabled' => 1
+				};
diff --git a/config/portredir/lang/portredir.de.pl b/config/portredir/lang/portredir.de.pl
new file mode 100644
index 000000000..b932d4a85
--- /dev/null
+++ b/config/portredir/lang/portredir.de.pl
@@ -0,0 +1,19 @@ 
+%tr = (
+%tr,
+'portredir enable addon' => 'Addon aktivieren',
+'portredir common settings' => 'Allgemeine Einstellungen',
+'portredir port redirections' => 'Portumleitungen',
+'portredir fw for interface' => 'Firewalloptionen für das Interface',
+'portredir enable user redirections' => 'Aktiviere benutzerdefinierte Portumleitungen',
+'portredir force local dns' => 'Erzwinge lokale DNS-Server',
+'portredir force local ntp' => 'Erzwinge lokale NTP-Server',
+'portredir custom redirections' => 'Benutzerdefinierte Portumleitungen',
+'portredir remove rule' => 'Entferne Regel',
+'portredir add rule' => 'Hinzufügen',
+'portredir no entries' => 'Keine Einträge vorhanden.',
+'portredir invalid address' => 'Ungültige Host-Addresse.',
+'portredir empty input' => 'Fehlende Angabe: Bitte geben Sie einen gültigen Host an.',
+'portredir save to activate' => 'Speichern, um Änderungen zu aktivieren',
+);
+
+#EOF
diff --git a/config/portredir/lang/portredir.en.pl b/config/portredir/lang/portredir.en.pl
new file mode 100644
index 000000000..f442f3eaa
--- /dev/null
+++ b/config/portredir/lang/portredir.en.pl
@@ -0,0 +1,19 @@ 
+%tr = (
+%tr,
+'portredir enable addon' => 'Enable addon',
+'portredir common settings' => 'Common settings',
+'portredir port redirections' => 'Port redirections',
+'portredir fw for interface' => 'Firewall options for interface',
+'portredir enable user redirections' => 'Enable user port redirections',
+'portredir force local dns' => 'Enforce local DNS servers',
+'portredir force local ntp' => 'Enforce local NTP servers',
+'portredir custom redirections' => 'Custom port redirections',
+'portredir remove rule' => 'Remove rule',
+'portredir add rule' => 'Add new',
+'portredir no entries' => 'No entries at the moment.',
+'portredir invalid address' => 'Invalid host address.',
+'portredir empty input' => 'Empty input: Please enter a valid host.',
+'portredir save to activate' => 'Save to activate changes',
+);
+
+#EOF
diff --git a/config/portredir/portredir-backup b/config/portredir/portredir-backup
new file mode 100644
index 000000000..bd2ada742
--- /dev/null
+++ b/config/portredir/portredir-backup
@@ -0,0 +1 @@ 
+/var/ipfire/portredir
diff --git a/config/portredir/portredir.cgi b/config/portredir/portredir.cgi
new file mode 100644
index 000000000..4913dda3f
--- /dev/null
+++ b/config/portredir/portredir.cgi
@@ -0,0 +1,525 @@ 
+#!/usr/bin/perl
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2021  IPFire Team  <info@ipfire.org>                          #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+use strict;
+
+# enable only the following on debugging purpose
+use warnings;
+use CGI::Carp 'fatalsToBrowser';
+
+require '/var/ipfire/general-functions.pl';
+require "${General::swroot}/lang.pl";
+require "${General::swroot}/header.pl";
+
+# File declarations
+my $settingsfile = "${General::swroot}/portredir/settings";
+my $redirectsfile = "${General::swroot}/portredir/redirects";
+
+# Create empty settingsfiles if they does not exist yet
+unless (-e "$settingsfile") { system ("touch $settingsfile"); }
+unless (-e "$redirectsfile") { system ("touch $redirectsfile"); }
+
+# load ipfire settings
+our %netsettings = ();
+our %color = ();
+&General::readhash("${General::swroot}/ethernet/settings", \%netsettings);
+&General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color);
+
+my %settings=();
+my %portredirs=();
+my %checked=(); # Checkbox manipulations
+my $errormessage='';
+my %selected=();
+our %redirects=();
+
+$settings{'ACTION'} = '';
+$settings{'REDIR_ENABLE_ADDON'}="off";
+$settings{'REDIR_CUSTOM_GREEN'}="off";
+$settings{'REDIR_CUSTOM_BLUE'}="off";
+$settings{'REDIR_CUSTOM_ORANGE'}="off";
+$settings{'REDIR_DNS_GREEN'}="off";
+$settings{'REDIR_NTP_GREEN'}="off";
+$settings{'REDIR_DNS_BLUE'}="off";
+$settings{'REDIR_NTP_BLUE'}="off";
+$settings{'REDIR_DNS_ORANGE'}="off";
+$settings{'REDIR_NTP_ORANGE'}="off";
+
+&Header::showhttpheaders();
+
+# Get GUI values
+&Header::getcgihash(\%settings);
+
+# Save action
+if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
+
+	# If custom rules enabled, deactivate default rules on interface
+	if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
+		$settings{'REDIR_DNS_GREEN'}="off";
+		$settings{'REDIR_NTP_GREEN'}="off";
+	}
+
+	if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
+		$settings{'REDIR_DNS_BLUE'}="off";
+		$settings{'REDIR_NTP_BLUE'}="off";
+	}
+
+	if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
+		$settings{'REDIR_DNS_ORANGE'}="off";
+		$settings{'REDIR_NTP_ORANGE'}="off";
+	}
+
+	&General::writehash($settingsfile, \%settings);
+
+	if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
+		system ('/usr/local/bin/portredirctrl restart >/dev/null 2>&1');
+		system ('/usr/local/bin/portredirctrl enable >/dev/null 2>&1');
+		&General::log('portredir addon: port redirections enabled');
+	}
+	if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
+		system ('/usr/local/bin/portredirctrl disable >/dev/null 2>&1');
+		system ('/usr/local/bin/portredirctrl stop >/dev/null 2>&1');
+		&General::log('portredir addon: port redirections disabled');
+	}
+
+# Add/edit an entry to the redirectsfile.
+
+} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) || ($settings{'ACTION'} eq $Lang::tr{'update'})) {
+
+	# Check if any input has been performed.
+	if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
+
+		# Check if the given input is no valid IP-address, display an error message.
+		if (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
+			$errormessage = "$Lang::tr{'portredir invalid address'}";
+		}
+	} else {
+		$errormessage = "$Lang::tr{'portredir empty input'}";
+	}
+
+	# Go further if there was no error.
+	if ($errormessage eq '') {
+		my %redirects = ();
+		my $id;
+		my $status;
+
+		# Assign hash values.
+		my $new_entry_interface = $settings{'REDIR_ENTRY_INTERFACE'};
+		my $new_entry_protocol = $settings{'REDIR_ENTRY_PROTOCOL'};
+		my $new_entry_port = $settings{'REDIR_ENTRY_PORT'};
+		my $new_entry_address = $settings{'REDIR_ENTRY_ADDRESS'};
+		my $new_entry_remark = $settings{'REDIR_ENTRY_REMARK'};
+
+		# Read-in redirectsfile.
+		&General::readhasharray($redirectsfile, \%redirects);
+
+		# Check if we should edit an existing entry and got an ID.
+		if (($settings{'ACTION'} eq $Lang::tr{'update'}) && ($settings{'ID'})) {
+			# Assin the provided id.
+			$id = $settings{'ID'};
+
+			# Undef the given ID.
+			undef($settings{'ID'});
+
+			# Grab the configured status of the corresponding entry.
+			$status = $redirects{$id}[4];
+		} else {
+			# Each newly added entry automatically should be enabled.
+			$status = "enabled";
+
+			# Generate the ID for the new entry.
+			#
+			# Sort the keys by their ID and store them in an array.
+			my @keys = sort { $a <=> $b } keys %redirects;
+
+			# Reverse the key array.
+			my @reversed = reverse(@keys);
+
+			# Obtain the last used id.
+			my $last_id = @reversed[0];
+
+			# Increase the last id by one and use it as id for the new entry.
+			$id = ++$last_id;
+		}
+
+		# Add/Modify the entry to/in the redirects hash.
+		$redirects{$id} = ["$new_entry_interface", "$new_entry_protocol", "$new_entry_port", "$new_entry_address","$status", "$new_entry_remark"];
+
+		# Write the changed redirects hash to the redirects file.
+		&General::writehasharray($redirectsfile, \%redirects);
+	}
+
+# Toggle Enabled/Disabled for an existing entry on the redirects list.
+
+} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable disable'}) {
+	my %redirects = ();
+
+	# Only go further, if an ID has been passed.
+	if ($settings{'ID'}) {
+		# Assign the given ID.
+		my $id = $settings{'ID'};
+
+		# Undef the given ID.
+		undef($settings{'ID'});
+
+		# Read-in ignoredfile.
+		&General::readhasharray($redirectsfile, \%redirects);
+
+		# Grab the configured status of the corresponding entry.
+		my $status = $redirects{$id}[4];
+
+		# Switch the status.
+		if ($status eq "disabled") {
+			$status = "enabled";
+		} else {
+			$status = "disabled";
+		}
+
+		# Modify the status of the existing entry.
+		$redirects{$id} = ["$redirects{$id}[0]", "$redirects{$id}[1]", "$redirects{$id}[2]", "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
+
+		# Write the changed ignored hash to the redirects file.
+		&General::writehasharray($redirectsfile, \%redirects);
+	}
+
+# Remove entry from redirects list.
+
+} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
+	my %redirects = ();
+
+	# Read-in redirectsfile.
+	&General::readhasharray($redirectsfile, \%redirects);
+
+	# move data on key up
+	foreach my $key (sort keys %redirects) {
+		if ($key >= $settings{'ID'}) {
+			my $next = $key + 1;
+			if (exists $redirects{$next}) {
+				foreach my $i (0 .. $#{$redirects{$next}}) { $redirects{$key}[$i] = $redirects{$next}[$i]; }
+			}
+		}
+	}
+
+	my $last_key = (sort {$a <=> $b} keys %redirects)[-1];
+	delete $redirects{$last_key};
+
+	# Undef the given ID.
+	undef($settings{'ID'});
+
+	# Write the changed redirects hash to file.
+	&General::writehasharray($redirectsfile, \%redirects);
+}
+
+# Load settings from file
+&General::readhash($settingsfile, \%settings);
+&General::readhasharray($redirectsfile, \%redirects);
+
+# Call functions to generate whole page.
+&Header::openpage($Lang::tr{'portredir port redirections'}, 1, '');
+&Header::openbigbox('100%', 'left', '', $errormessage);
+
+if ($errormessage) {
+        &Header::openbox('100%', 'left', $Lang::tr{'warning messages'});
+        print "<font color='red'>$errormessage&nbsp;</font>";
+        &Header::closebox();
+}
+
+$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
+$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
+$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_ADDON'}} = "checked='checked'";
+$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
+$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
+$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_GREEN'}} = "checked='checked'";
+$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
+$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
+$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BLUE'}} = "checked='checked'";
+$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
+$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
+$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_ORANGE'}} = "checked='checked'";
+$checked{'REDIR_DNS_GREEN'}{'off'} = '';
+$checked{'REDIR_DNS_GREEN'}{'on'} = '';
+$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}} = "checked='checked'";
+$checked{'REDIR_NTP_GREEN'}{'off'} = '';
+$checked{'REDIR_NTP_GREEN'}{'on'} = '';
+$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}} = "checked='checked'";
+$checked{'REDIR_DNS_BLUE'}{'off'} = '';
+$checked{'REDIR_DNS_BLUE'}{'on'} = '';
+$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}} = "checked='checked'";
+$checked{'REDIR_NTP_BLUE'}{'off'} = '';
+$checked{'REDIR_NTP_BLUE'}{'on'} = '';
+$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}} = "checked='checked'";
+$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
+$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
+$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE'}} = "checked='checked'";
+$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
+$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
+$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE'}} = "checked='checked'";
+
+$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTRY_INTERFACE'}} = 'selected';
+$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY_PROTOCOL'}} = 'selected';
+
+&showMainBox();
+&showRedirectsBox();
+
+&Header::closebigbox();
+&Header::closepage();
+
+# Function to show main settings and options.
+sub showMainBox() {
+
+	&Header::openbox('100%', 'center', "$Lang::tr{'settings'}");
+	print "<form method='post' action='$ENV{'SCRIPT_NAME'}'>";
+
+print <<END;
+<table width='80%' cellspacing='0' border='0'>
+	<tr><td colspan='2' class='base' bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir common settings'}</b></td></tr
+	<tr>
+		<td width='25%' class='base'>$Lang::tr{'portredir enable addon'}:</td>
+		<td><input type='checkbox' name='REDIR_ENABLE_ADDON' $checked{'REDIR_ENABLE_ADDON'}{'on'} /></td>
+	</tr>
+	<tr><td colspan='2'></td></tr>
+	<tr><td colspan='2'>&nbsp;</td></tr>
+END
+
+	# create html table with header line 1
+	print "<table width='80%' cellspacing='0' border='0'><tr>";
+	print "<th class='base' width='40%' align='left'><b>$Lang::tr{'portredir fw for interface'}</th>";
+	if ($netsettings{'GREEN_DEV'})  {print "<th class='base' width='10%'><b><font color=green>$Lang::tr{'green'}</font></th>";
+		} else {		 print "<th class='base' width='10%'></font></th>"; }
+	if ($netsettings{'BLUE_DEV'})   {print "<th class='base' width='10%'><b><font color=blue>$Lang::tr{'blue'}</font></th>";
+		} else {		 print "<th class='base' width='10%'></font></th>"; }
+	if ($netsettings{'ORANGE_DEV'}) {print "<th class='base' width='10%'><b><font color=orange>$Lang::tr{'orange'}</font></th>";
+		} else {		 print "<th class='base' width='10%'></font></th>"; }
+
+	# the empty right row
+	print "<th class='base' width='30%'><td></td></th></tr>";
+
+	# line 2
+	print "<tr><td>$Lang::tr{'portredir force local dns'}</td>";
+	if ($netsettings{'GREEN_DEV'})  {print "<td class='base' align='center'><input type='checkbox' name='REDIR_DNS_GREEN' $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print "<td></td>";}
+	if ($netsettings{'BLUE_DEV'})   {print "<td class='base' align='center'><input type='checkbox' name='REDIR_DNS_BLUE' $checked{'REDIR_DNS_BLUE'}{'on'}></td>";} else { print "<td></td>";}
+	if ($netsettings{'ORANGE_DEV'}) {print "<td class='base' align='center'><input type='checkbox' name='REDIR_DNS_ORANGE' $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print "<td></td>";}
+
+	# line 3
+	print "</tr><tr><td>$Lang::tr{'portredir force local ntp'}</td>";
+	if ($netsettings{'GREEN_DEV'})  {print "<td class='base' align='center'><input type='checkbox' name='REDIR_NTP_GREEN' $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print "<td></td>";}
+	if ($netsettings{'BLUE_DEV'})   {print "<td class='base' align='center'><input type='checkbox' name='REDIR_NTP_BLUE' $checked{'REDIR_NTP_BLUE'}{'on'}></td>";} else { print "<td></td>";}
+	if ($netsettings{'ORANGE_DEV'}) {print "<td class='base' align='center'><input type='checkbox' name='REDIR_NTP_ORANGE' $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print "<td></td>";}
+
+	# line 4
+	print "</tr><tr><td>$Lang::tr{'portredir enable user redirections'}</td>";
+	if ($netsettings{'GREEN_DEV'})  {print "<td class='base' align='center'><input type='checkbox' name='REDIR_CUSTOM_GREEN' $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else { print "<td></td>";}
+	if ($netsettings{'BLUE_DEV'})   {print "<td class='base' align='center'><input type='checkbox' name='REDIR_CUSTOM_BLUE' $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print "<td></td>";}
+	if ($netsettings{'ORANGE_DEV'}) {print "<td class='base' align='center'><input type='checkbox' name='REDIR_CUSTOM_ORANGE' $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else { print "<td></td>";}
+
+	print <<END;
+	</tr></table>
+	<table width='80%' cellspacing='0' border='0'>
+		<tr><td colspan='2'>&nbsp;</td></tr>
+		<tr><td align='left'><b>$Lang::tr{'portredir save to activate'}</b></td><td width='5%' align='center'><input type='submit' name='ACTION' value='  $Lang::tr{'save'}  '></td></tr>
+	</table></form>
+END
+
+&Header::closebox();
+}
+
+# Function to show elements of the redirects file and allow to add or remove single members of it.
+sub showRedirectsBox() {
+        &Header::openbox('100%', 'center', "$Lang::tr{'portredir custom redirections'}");
+
+	print <<END;
+		<table width='80%' cellspacing='1' border='0'>
+			<tr>
+				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'interface'}</b></td>
+				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'protocol'}</b></td>
+				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'port'}</b></td>
+				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'ip address'}</b></td>
+				<td class='base' bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'remark'}</b></td>
+				<td class='base' colspan='3' bgcolor='$color{'color20'}'></td>
+			</tr>
+END
+			# Check if some rules have been added to be redirects.
+			if (keys (%redirects)) {
+				my $col = "";
+
+				# List all entries of the hash.
+				foreach my $key (sort keys %redirects){
+
+					# Assign data array positions to some nice variable names.
+					my $interface = $redirects{$key}[0];
+					my $protocol = $redirects{$key}[1];
+					my $port  = $redirects{$key}[2];
+					my $address = $redirects{$key}[3];
+					my $status  = $redirects{$key}[4];
+					my $remark  = $redirects{$key}[5];
+
+					# Check if the key (id) number is even or not.
+					if ($settings{'ID'} eq $key) {
+						$col="bgcolor='${Header::colouryellow}'";
+					} elsif ($key % 2) {
+						$col="bgcolor='$color{'color22'}'";
+					} else {
+						$col="bgcolor='$color{'color20'}'";
+					}
+
+					# Choose icon for the checkbox.
+					my $gif;
+					my $gdesc;
+
+					# Check if the status is enabled and select the correct image and description.
+					if ($status eq 'enabled' ) {
+						$gif = 'on.gif';
+						$gdesc = $Lang::tr{'click to disable'};
+					} else {
+						$gif = 'off.gif';
+						$gdesc = $Lang::tr{'click to enable'};
+					}
+
+					print <<END;
+					<tr>
+						<td width='15%' class='base' align='center' $col><b><font color=$interface>$Lang::tr{$interface}</font></b></td>
+						<td width='10%' class='base' align='center' $col>$protocol</td>
+						<td width='10%' class='base' align='center' $col>$port</td>
+						<td width='15%' class='base' align='center' $col>&nbsp;$address</td>
+						<td width='40%' class='base' align='center' $col>&nbsp;$remark</td>
+						<td align='center' $col>
+							<form method='post' action='$ENV{'SCRIPT_NAME'}'>
+								<input type='hidden' name='ACTION' value='$Lang::tr{'toggle enable disable'}' />
+								<input type='image' name='$Lang::tr{'toggle enable disable'}' src='/images/$gif' alt='$gdesc' title='$gdesc' />
+								<input type='hidden' name='ID' value='$key' />
+							</form>
+						</td>
+						<td align='center' $col>
+							<form method='post' action='$ENV{'SCRIPT_NAME'}'>
+								<input type='hidden' name='ACTION' value='$Lang::tr{'edit'}' />
+								<input type='image' name='$Lang::tr{'edit'}' src='/images/edit.gif' alt='$Lang::tr{'edit'}' title='$Lang::tr{'edit'}' />
+								<input type='hidden' name='ID' value='$key' />
+							</form>
+						</td>
+						<td align='center' $col>
+							<form method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
+								<input type='image' name='$Lang::tr{'remove'}' src='/images/delete.gif' title='$Lang::tr{'remove'}' alt='$Lang::tr{'remove'}'>
+								<input type='hidden' name='ID' value='$key'>
+								<input type='hidden' name='ACTION' value='$Lang::tr{'remove'}'>
+							</form>
+						</td>
+					</tr>
+END
+				}
+			} else {
+				# Print notice that currently no ports are redirected.
+				print "<tr>\n";
+				print "<td class='base' colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
+				print "</tr>\n";
+			}
+
+		print "</table>\n";
+
+	# Section to add new elements or edit existing ones.
+	print <<END;
+	<br>
+	<hr>
+	<br>
+	<div align='center'>
+		<table width='100%' cellspacing='0' border='0'>
+END
+
+	# Assign correct headline and button text.
+	my $buttontext;
+	my $entry_interface;
+	my $entry_protocol;
+	my $entry_port;
+	my $entry_address;
+	my $entry_remark;
+
+	# Check if an ID (key) has been given, in this case an existing entry should be edited.
+	if ($settings{'ID'} ne '') {
+		$buttontext = $Lang::tr{'update'};
+		print "<tr><td class='boldbase' colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
+
+		# Grab address and remark for the given key.
+		$entry_interface = $redirects{$settings{'ID'}}[0];
+		$entry_protocol = $redirects{$settings{'ID'}}[1];
+		$entry_port = $redirects{$settings{'ID'}}[2];
+		$entry_address = $redirects{$settings{'ID'}}[3];
+		$entry_remark = $redirects{$settings{'ID'}}[5];
+
+	} else {
+		$buttontext = $Lang::tr{'add'};
+		print "<tr><td class='boldbase' colspan='11'><b>$Lang::tr{'dnsforward add a new entry'}</b></td></tr>\n";
+		print "<tr><td>&nbsp</td></tr>\n";
+	}
+
+	print <<END;
+			<tr>
+				<td class='base' width='1%'  bgcolor='$color{'color22'}'></td>
+				<td class='base' width='15%' bgcolor='$color{'color22'}' align='left'><b>$Lang::tr{'interface'}</b></td>
+				<td class='base' width='10%' bgcolor='$color{'color22'}' align='left'><b>$Lang::tr{'protocol'}</b></td>
+				<td class='base' width='10%' bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
+				<td class='base' width='13%' bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'ip address'}</b></td>
+				<td class='base' width='30%' bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
+				<td class='base' width='15%' bgcolor='$color{'color22'}'></td>
+				<td class='base' width='1%'  bgcolor='$color{'color22'}'></td>
+			</tr>
+
+			<form method='post' action='$ENV{'SCRIPT_NAME'}'>
+			<input type='hidden' name='ID' value='$settings{'ID'}'>
+			<tr>
+				<td class='base'></td>
+				<td><select style='width:90px;' id='interface' name='REDIR_ENTRY_INTERFACE'>
+END
+				if ($netsettings{'GREEN_DEV'}) {
+					if ($entry_interface eq "green") {
+						print "<option value='green' selected='selected' {'green'}>$Lang::tr{'green'}</option>";
+					} else { print "<option value='green' {'green'}>$Lang::tr{'green'}</option>";}
+				}
+				if ($netsettings{'BLUE_DEV'}) {
+					if ($entry_interface eq "blue") { 
+						print "<option value='blue' selected='selected' {'blue'}>$Lang::tr{'blue'}</option>";
+					} else { print "<option value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
+				}
+				if ($netsettings{'ORANGE_DEV'}) {
+					if ($entry_interface eq "orange") { 
+						print "<option value='orange' selected='selected' {'orange'}>$Lang::tr{'orange'}</option>";
+					} else { print "<option value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
+				}
+
+			print "</select><td><select style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
+			if ((!$entry_protocol) || ($entry_protocol eq "tcp")) {
+				print "<option selected='selected' id='protocol' value='tcp' {'tcp'}>tcp</option>";
+				print "<option value='udp' {'udp'}>udp</option>";
+			} elsif ($entry_protocol eq "udp") {
+				print "<option value='tcp' {'tcp'}>tcp</option>";
+				print "<option selected='selected' value='udp' {'udp'}>udp</option>";
+			}
+	print <<END;
+				</select></td>
+				<td><input type='text' name='REDIR_ENTRY_PORT'    value='$entry_port'    size='4'></td>
+				<td><input type='text' name='REDIR_ENTRY_ADDRESS' value='$entry_address' size='14'></td>
+				<td><input type='text' name='REDIR_ENTRY_REMARK'  value='$entry_remark'  size='35'></td>
+				<td width='10%' align='center'><input type='submit' name='ACTION' value='  $buttontext  '></td>
+				<td class='base'></td>
+			</tr>
+			</form>
+		</table>
+	</div>
+END
+	&Header::closebox();
+}
diff --git a/config/rootfiles/common/misc-progs b/config/rootfiles/common/misc-progs
index d6594b3f8..fbad2af8b 100644
--- a/config/rootfiles/common/misc-progs
+++ b/config/rootfiles/common/misc-progs
@@ -17,6 +17,7 @@  usr/local/bin/logwatch
 #usr/local/bin/mpfirectrl
 usr/local/bin/openvpnctrl
 usr/local/bin/pakfire
+#usr/local/bin/portredirctrl
 usr/local/bin/qosctrl
 usr/local/bin/rebuildhosts
 usr/local/bin/rebuildroutes
diff --git a/config/rootfiles/packages/portredir b/config/rootfiles/packages/portredir
new file mode 100644
index 000000000..4b4ba8366
--- /dev/null
+++ b/config/rootfiles/packages/portredir
@@ -0,0 +1,11 @@ 
+etc/rc.d/init.d/portredir
+etc/rc.d/rc0.d/K77portredir
+etc/rc.d/rc3.d/S23portredir
+etc/rc.d/rc6.d/K77portredir
+srv/web/ipfire/cgi-bin/portredir.cgi
+usr/local/bin/portredirctrl
+var/ipfire/addon-lang/portredir.de.pl
+var/ipfire/addon-lang/portredir.en.pl
+var/ipfire/backup/addons/includes/portredir
+var/ipfire/menu.d/EX-portredir.menu
+var/ipfire/portredir
diff --git a/lfs/portredir b/lfs/portredir
new file mode 100644
index 000000000..a4911f71f
--- /dev/null
+++ b/lfs/portredir
@@ -0,0 +1,85 @@ 
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2007-2021  IPFire Team  <info@ipfire.org>                     #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+###############################################################################
+# Definitions
+###############################################################################
+
+include Config
+
+VER        = 1.0
+
+THISAPP    = portredir-$(VER)
+DIR_APP    = $(DIR_SRC)/$(THISAPP)
+TARGET     = $(DIR_INFO)/$(THISAPP)
+PROG       = portredir
+PAK_VER    = 1
+
+###############################################################################
+# Top-level Rules
+###############################################################################
+
+install : $(TARGET)
+
+check :
+
+download :
+
+md5 :
+
+dist: 
+	@$(PAK)
+
+###############################################################################
+# Installation Details
+###############################################################################
+
+$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
+	@$(PREBUILD)
+	@rm -rf $(DIR_APP) && cd $(DIR_SRC)
+
+	#install cgi 
+	install -v -m 755 $(DIR_CONF)/portredir/portredir.cgi /srv/web/ipfire/cgi-bin/
+
+	#create configuration dir 
+	-mkdir -pv /var/ipfire/portredir/
+	chown -R nobody:nobody /var/ipfire/portredir/
+
+	# Install include file for backup
+	install -v -m 644 $(DIR_CONF)/portredir/portredir-backup /var/ipfire/backup/addons/includes/portredir
+
+	# Install menu file
+	install -v -m 644 $(DIR_CONF)/portredir/EX-portredir.menu /var/ipfire/menu.d/
+	chown nobody:nobody /var/ipfire/menu.d/EX-portredir.menu
+
+	# Install addon-specific language-files
+	install -v -m 644 $(DIR_CONF)/portredir/lang/portredir.*.pl /var/ipfire/addon-lang/
+
+	#install initscripts
+	$(call INSTALL_INITSCRIPT,portredir)
+
+	# Create symlinks for runlevel interaction.
+	ln -svf /etc/rc.d/init.d/portredir /etc/rc.d/rc3.d/S23portredir
+	ln -svf /etc/rc.d/init.d/portredir /etc/rc.d/rc0.d/K77portredir
+	ln -svf /etc/rc.d/init.d/portredir /etc/rc.d/rc6.d/K77portredir
+
+	@rm -rf $(DIR_APP)
+	@$(POSTBUILD)
+
diff --git a/make.sh b/make.sh
index fc03ebcd5..ab9fe881a 100755
--- a/make.sh
+++ b/make.sh
@@ -1623,6 +1623,7 @@  buildipfire() {
   lfsmake2 socat
   lfsmake2 libcdada
   lfsmake2 pmacct
+  lfsmake2 portredir
 }
 
 buildinstaller() {
diff --git a/src/initscripts/packages/portredir b/src/initscripts/packages/portredir
new file mode 100644
index 000000000..cc57fb9cc
--- /dev/null
+++ b/src/initscripts/packages/portredir
@@ -0,0 +1,191 @@ 
+#!/bin/sh
+########################################################################
+# Begin $rc_base/init.d/portredir
+#
+# Description : portredir init script for DNS/NTP and custom 
+#		port redirection rules
+#
+########################################################################
+
+. /etc/sysconfig/rc
+. ${rc_functions}
+
+IPT="/sbin/iptables";
+parent_chain="PREROUTING";
+chain="PORT_REDIRECT";
+
+confdir="/var/ipfire/portredir";
+settingsfile="${confdir}/settings";
+redirectsfile="${confdir}/redirects";
+SYSLOG="NO";
+VERBOSE="NO";
+
+eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings);
+eval $(/usr/local/bin/readhash ${settingsfile});
+
+logtext() {
+	if [ "${SYSLOG}" = "YES" ]; then logger -t "portredir" ${1}; fi;
+	if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
+
+create_chain() {
+
+	local line=$(${IPT} -t nat -L ${parent_chain} --line-numbers |grep "SQUID" |awk '{printf($1)}');
+
+	if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z "${REDIR_ENABLE_ADDON}" ]]; then
+		logtext "addon not enabled in web interface...";
+		echo "Portredir addon not enabled in web interface...";
+		exit 0;
+	fi;
+
+	if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep ${chain})" ]; then
+		${IPT} -t nat -N ${chain};
+
+		if [ ! -z "${line}" ]; then
+			logtext "create chain ${chain} and link in ${parent_chain} at position ${line}...";
+			${IPT} -t nat -I ${parent_chain} ${line} -j ${chain};
+		else
+			logtext "create chain ${chain} and link in ${parent_chain} at last position...";
+			${IPT} -t nat -A ${parent_chain} -j ${chain};
+		fi
+	else
+		return 1;
+	fi;
+	return 0;
+}
+
+remove_chain() {
+	if [ ! -z "$(${IPT} -t nat -L ${parent_chain} |grep ${chain})" ]; then
+		logtext "remove chain ${chain} and link in ${parent_chain} from system...";
+		${IPT} -t nat -D "${parent_chain}" -j ${chain};
+		${IPT} -t nat -F ${chain};
+		${IPT} -t nat -X ${chain};
+	else
+		return 1;
+	fi;
+	return 0;
+}
+
+activate_custom_redirections() {
+	
+	local array=();
+	local redirects=();
+	local i;
+	index=();
+	iface=();
+	protocol=();
+	port=();
+	targetip=();
+	enabled=();
+
+	IFS=$'\n' read -d '' -ra redirects < ${redirectsfile};
+
+	for i in "${!redirects[@]}"
+	do
+		IFS=$',' read -ra array <<< ${redirects[i]};
+		index[i]=${array[0]};
+		iface[i]=${array[1]};
+		protocol[i]=${array[2]};
+		port[i]=${array[3]};
+		targetip[i]=${array[4]};
+		enabled[i]=${array[5]};
+	done
+
+	for i in "${!index[@]}"
+	do
+		if [[ ! -z "${GREEN_DEV}" &&  "${iface[i]}" = "green" && "${enabled[i]}" = "enabled" ]]; then
+
+			logtext "add redirect in ${chain} on ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]} port ${port[i]} ";
+			${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j RETURN;
+			${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j REDIRECT;
+		fi
+		if [[ ! -z "${BLUE_DEV}" &&  "${iface[i]}" = "blue" && "${enabled[i]}" = "enabled" ]]; then
+			logtext "add redirect in ${chain} on ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port ${port[i]} ";
+			${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j RETURN;
+			${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j REDIRECT;
+		fi
+		if [[ ! -z "${ORANGE_DEV}" &&  "${iface[i]}" = "orange" && "${enabled[i]}" = "enabled" ]]; then
+			logtext "add redirect in ${chain} on ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port ${port[i]} ";
+			${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j RETURN;
+			${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport ${port[i]} -j REDIRECT;
+		fi
+	done
+	unset array redirects i index iface protocol port targetip enabled;
+	return 0;
+}
+
+activate_redirections() {
+
+	if ! create_chain; then return 1; fi;
+	
+	# Force DNS REDIRECTs on GREEN (udp, tcp, 53)
+	if [[ "${REDIR_DNS_GREEN}" == "on" &&  "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
+		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
+		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p udp -m udp --dport domain -j REDIRECT;
+		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
+		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p tcp -m tcp --dport domain -j REDIRECT;
+	fi
+
+	# Force DNS REDIRECTs on BLUE (udp, tcp, 53)
+	if [[ "${REDIR_DNS_BLUE}" == "on" &&  "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
+		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
+		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp -m udp --dport domain -j REDIRECT
+		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
+		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p tcp -m tcp --dport domain -j REDIRECT
+	fi
+
+	# Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
+	if [[ "${REDIR_DNS_ORANGE}" == "on" &&  "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
+		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
+		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p udp -m udp --dport domain -j REDIRECT
+		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
+		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p tcp -m tcp --dport domain -j REDIRECT
+	fi
+
+	# Force NTP REDIRECTs on GREEN (udp, 123)
+	if [[ "${REDIR_NTP_GREEN}" == "on" &&  "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
+		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
+		${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p udp -m udp --dport ntp -j REDIRECT
+	fi
+
+	# Force NTP REDIRECTs on BLUE (udp, 123)
+	if [[ "${REDIR_NTP_BLUE}" == "on" &&  "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
+		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
+		${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp -m udp --dport ntp -j REDIRECT
+	fi
+
+	# Force NTP REDIRECTs on ORANGE (udp, 123)
+	if [[ "${REDIR_NTP_ORANGE}" == "on" &&  "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
+		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
+		${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p udp -m udp --dport ntp -j REDIRECT
+	fi
+
+	if ! activate_custom_redirections; then return 1; fi;
+
+	return 0;
+}
+
+case "${1}" in
+	start)
+		boot_mesg "Loading port redirections..."
+		activate_redirections;
+		evaluate_retval;
+		;;
+
+	stop)	
+		boot_mesg "Removing port redirections..."
+		remove_chain;
+		evaluate_retval;
+		;;
+
+	restart)
+		${0} stop
+		${0} start
+		;;
+
+	*)
+		echo "Usage: ${0} {start|stop|restart}"
+		exit 1
+		;;
+esac
+
+# End $rc_base/init.d/portredir
diff --git a/src/misc-progs/Makefile b/src/misc-progs/Makefile
index 7c3ef7529..850f8fdcc 100644
--- a/src/misc-progs/Makefile
+++ b/src/misc-progs/Makefile
@@ -30,7 +30,7 @@  SUID_PROGS = squidctrl sshctrl ipfirereboot \
 	wirelessctrl getipstat qosctrl \
 	redctrl syslogdctrl extrahdctrl sambactrl \
 	smartctrl clamavctrl addonctrl pakfire mpfirectrl wlanapctrl \
-	setaliases urlfilterctrl updxlratorctrl fireinfoctrl rebuildroutes \
+	setaliases urlfilterctrl updxlratorctrl fireinfoctrl rebuildroutes portredirctrl \
 	getconntracktable wirelessclient torctrl ddnsctrl unboundctrl \
 	captivectrl
 
diff --git a/src/misc-progs/portredirctrl.c b/src/misc-progs/portredirctrl.c
new file mode 100644
index 000000000..7897d711c
--- /dev/null
+++ b/src/misc-progs/portredirctrl.c
@@ -0,0 +1,47 @@ 
+/* This file is part of the IPFire Firewall.
+ *
+ * This program is distributed under the terms of the GNU General Public
+ * Licence.  See the file COPYING for details.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include "setuid.h"
+
+int main(int argc, char *argv[]) {
+	if (!(initsetuid()))
+		exit(1);
+
+	// Check what command is asked
+        if (argc < 2) {
+                fprintf(stderr, "\nNo argument given.\n\nportredirctrl (start|stop|restart|enable|disable)\n\n");
+                exit(1);
+        }
+
+        if (strcmp(argv[1], "start") == 0) {
+                safe_system("/etc/rc.d/init.d/portredir start");
+        } else if (strcmp(argv[1], "stop") == 0) {
+                safe_system("/etc/rc.d/init.d/portredir stop");
+        } else if (strcmp(argv[1], "restart") == 0) {
+                safe_system("/etc/rc.d/init.d/portredir restart");
+	} else if (strcmp(argv[1], "enable") == 0) {
+		safe_system("touch /var/ipfire/portredir/enable");
+		safe_system("ln -snf /etc/rc.d/init.d/portredir /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
+		safe_system("ln -snf /etc/rc.d/init.d/portredir /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
+		safe_system("ln -snf /etc/rc.d/init.d/portredir /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
+	} else if (strcmp(argv[1], "disable") == 0) {
+		safe_system("/etc/rc.d/init.d/portredir stop");
+		safe_system("unlink /var/ipfire/portredir/enable");
+		safe_system("rm -rf /etc/rc.d/rc*.d/*portredir >/dev/null 2>&1");
+        } else {
+                fprintf(stderr, "\nBad argument given.\n\nportredirctrl (start|stop|restart|enable|disable)\n\n");
+                exit(1);
+        }
+
+	return 0;
+}
diff --git a/src/paks/portredir/install.sh b/src/paks/portredir/install.sh
new file mode 100644
index 000000000..9f69aeae2
--- /dev/null
+++ b/src/paks/portredir/install.sh
@@ -0,0 +1,32 @@ 
+#!/bin/bash
+############################################################################
+#                                                                          #
+# This file is part of the IPFire Firewall.                                #
+#                                                                          #
+# IPFire is free software; you can redistribute it and/or modify           #
+# it under the terms of the GNU General Public License as published by     #
+# the Free Software Foundation; either version 2 of the License, or        #
+# (at your option) any later version.                                      #
+#                                                                          #
+# IPFire is distributed in the hope that it will be useful,                #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of           #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
+# GNU General Public License for more details.                             #
+#                                                                          #
+# You should have received a copy of the GNU General Public License        #
+# along with IPFire; if not, write to the Free Software                    #
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA #
+#                                                                          #
+# Copyright (C) 2021 IPFire-Team <info@ipfire.org>.                        #
+#                                                                          #
+############################################################################
+#
+. /opt/pakfire/lib/functions.sh
+extract_files
+restore_backup ${NAME}
+
+/usr/local/bin/update-lang-cache
+
+chown root:nobody /usr/local/bin/portredirctrl
+chmod 4750 /usr/local/bin/portredirctrl
+chmod u+s /usr/local/bin/portredirctrl
diff --git a/src/paks/portredir/uninstall.sh b/src/paks/portredir/uninstall.sh
new file mode 100644
index 000000000..df9270125
--- /dev/null
+++ b/src/paks/portredir/uninstall.sh
@@ -0,0 +1,28 @@ 
+#!/bin/bash
+############################################################################
+#                                                                          #
+# This file is part of the IPFire Firewall.                                #
+#                                                                          #
+# IPFire is free software; you can redistribute it and/or modify           #
+# it under the terms of the GNU General Public License as published by     #
+# the Free Software Foundation; either version 2 of the License, or        #
+# (at your option) any later version.                                      #
+#                                                                          #
+# IPFire is distributed in the hope that it will be useful,                #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of           #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
+# GNU General Public License for more details.                             #
+#                                                                          #
+# You should have received a copy of the GNU General Public License        #
+# along with IPFire; if not, write to the Free Software                    #
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA #
+#                                                                          #
+# Copyright (C) 2007 IPFire-Team <info@ipfire.org>.                        #
+#                                                                          #
+############################################################################
+#
+. /opt/pakfire/lib/functions.sh
+make_backup ${NAME}
+remove_files
+
+/usr/local/bin/update-lang-cache
diff --git a/src/paks/portredir/update.sh b/src/paks/portredir/update.sh
new file mode 100644
index 000000000..89c40d0d7
--- /dev/null
+++ b/src/paks/portredir/update.sh
@@ -0,0 +1,26 @@ 
+#!/bin/bash
+############################################################################
+#                                                                          #
+# This file is part of the IPFire Firewall.                                #
+#                                                                          #
+# IPFire is free software; you can redistribute it and/or modify           #
+# it under the terms of the GNU General Public License as published by     #
+# the Free Software Foundation; either version 2 of the License, or        #
+# (at your option) any later version.                                      #
+#                                                                          #
+# IPFire is distributed in the hope that it will be useful,                #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of           #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
+# GNU General Public License for more details.                             #
+#                                                                          #
+# You should have received a copy of the GNU General Public License        #
+# along with IPFire; if not, write to the Free Software                    #
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA #
+#                                                                          #
+# Copyright (C) 2007 IPFire-Team <info@ipfire.org>.                        #
+#                                                                          #
+############################################################################
+#
+. /opt/pakfire/lib/functions.sh
+./uninstall.sh
+./install.sh