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. - submitted by SS and reviewed by MT.

[PATCH 1/3] firewall.cgi: Remove faulty assignments for selected hash. - submitted by SS
[PATCH 2/3] firewall.cgi: Fix multiple usage of configured net or interface. - submitted by SS
[PATCH 3/3] firewall.cgi: Make checked and selected hashes private for each single function. - submitted by SS

[PATCH] firewall.cgi: Allow to creating input rules from Orange to another zone. - submitted by SS and reviewed by MT.

[PATCH] firewall.cgi: Map rule if manual target address belongs to IPFire - submitted by SS and reviewed by MT.

[PATCH] firewall.cgi: Bring back check for single IP when using DNAT.


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