From 7fe568ca1672f1dfbd2b56ee3ef7a6ab48b03070 Mon Sep 17 00:00:00 2001
From: Christian Breunig <christian@breunig.cc>
Date: Wed, 22 May 2024 21:31:32 +0200
Subject: nat: T6345: source NAT port mapping "fully-random" is superfluous in
 Kernel >=5.0

random - In kernel 5.0 and newer this is the same as fully-random. In earlier
kernels the port mapping will be randomized using a seeded MD5 hash mix using
source and destination address and destination port.

https://git.netfilter.org/nftables/commit/?id=fbe27464dee4588d906492749251454
---
 .../include/nat-translation-options.xml.i          |   8 +-
 .../include/version/nat-version.xml.i              |   2 +-
 smoketest/config-tests/nat-basic                   |  85 +++++++
 smoketest/configs/nat-basic                        | 256 +++++++++++++++++++++
 src/migration-scripts/nat/7-to-8                   |  62 +++++
 5 files changed, 406 insertions(+), 7 deletions(-)
 create mode 100644 smoketest/config-tests/nat-basic
 create mode 100644 smoketest/configs/nat-basic
 create mode 100755 src/migration-scripts/nat/7-to-8

diff --git a/interface-definitions/include/nat-translation-options.xml.i b/interface-definitions/include/nat-translation-options.xml.i
index 6b95de045..c8900590f 100644
--- a/interface-definitions/include/nat-translation-options.xml.i
+++ b/interface-definitions/include/nat-translation-options.xml.i
@@ -28,22 +28,18 @@
       <properties>
         <help>Port mapping options</help>
         <completionHelp>
-          <list>random fully-random none</list>
+          <list>random none</list>
         </completionHelp>
         <valueHelp>
           <format>random</format>
           <description>Randomize source port mapping</description>
         </valueHelp>
-        <valueHelp>
-          <format>fully-random</format>
-          <description>Full port randomization</description>
-        </valueHelp>
         <valueHelp>
           <format>none</format>
           <description>Do not apply port randomization</description>
         </valueHelp>
         <constraint>
-          <regex>(random|fully-random|none)</regex>
+          <regex>(random|none)</regex>
         </constraint>
       </properties>
       <defaultValue>none</defaultValue>
diff --git a/interface-definitions/include/version/nat-version.xml.i b/interface-definitions/include/version/nat-version.xml.i
index 656da6e14..173e91ed3 100644
--- a/interface-definitions/include/version/nat-version.xml.i
+++ b/interface-definitions/include/version/nat-version.xml.i
@@ -1,3 +1,3 @@
 <!-- include start from include/version/nat-version.xml.i -->
-<syntaxVersion component='nat' version='7'></syntaxVersion>
+<syntaxVersion component='nat' version='8'></syntaxVersion>
 <!-- include end -->
diff --git a/smoketest/config-tests/nat-basic b/smoketest/config-tests/nat-basic
new file mode 100644
index 000000000..9fea08b02
--- /dev/null
+++ b/smoketest/config-tests/nat-basic
@@ -0,0 +1,85 @@
+set interfaces ethernet eth0 offload rps
+set interfaces ethernet eth0 disable
+set interfaces ethernet eth1 offload gro
+set interfaces ethernet eth1 offload gso
+set interfaces ethernet eth1 offload rps
+set interfaces ethernet eth1 offload sg
+set interfaces ethernet eth1 offload tso
+set interfaces ethernet eth2 offload gro
+set interfaces ethernet eth2 offload gso
+set interfaces ethernet eth2 offload rps
+set interfaces ethernet eth2 offload sg
+set interfaces ethernet eth2 offload tso
+set interfaces ethernet eth3 offload gro
+set interfaces ethernet eth3 offload gso
+set interfaces ethernet eth3 offload rps
+set interfaces ethernet eth3 offload sg
+set interfaces ethernet eth3 offload tso
+set interfaces bonding bond10 hash-policy 'layer3+4'
+set interfaces bonding bond10 member interface 'eth2'
+set interfaces bonding bond10 member interface 'eth3'
+set interfaces bonding bond10 mode '802.3ad'
+set interfaces bonding bond10 vif 50 address '192.168.189.1/24'
+set interfaces loopback lo
+set interfaces pppoe pppoe7 authentication password 'vyos'
+set interfaces pppoe pppoe7 authentication username 'vyos'
+set interfaces pppoe pppoe7 dhcpv6-options pd 0 interface bond10.50 address '1'
+set interfaces pppoe pppoe7 dhcpv6-options pd 0 length '56'
+set interfaces pppoe pppoe7 ip adjust-mss '1452'
+set interfaces pppoe pppoe7 ipv6 address autoconf
+set interfaces pppoe pppoe7 ipv6 adjust-mss '1432'
+set interfaces pppoe pppoe7 mtu '1492'
+set interfaces pppoe pppoe7 no-peer-dns
+set interfaces pppoe pppoe7 source-interface 'eth1'
+set service lldp interface eth1 disable
+set service ntp allow-client address '192.168.189.0/24'
+set service ntp server time1.vyos.net
+set service ntp server time2.vyos.net
+set service ntp listen-address '192.168.189.1'
+set service ssh dynamic-protection
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 lease '604800'
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option default-router '192.168.189.1'
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option domain-name 'vyos.net'
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option name-server '1.1.1.1'
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option name-server '9.9.9.9'
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 range 0 start '192.168.189.20'
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 range 0 stop '192.168.189.254'
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 subnet-id '1'
+set service router-advert interface bond10.50 prefix ::/64 preferred-lifetime '2700'
+set service router-advert interface bond10.50 prefix ::/64 valid-lifetime '5400'
+set system config-management commit-revisions '100'
+set system domain-name 'vyos.net'
+set system host-name 'R1'
+set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
+set system login user vyos authentication plaintext-password ''
+set system name-server '1.1.1.1'
+set system name-server '9.9.9.9'
+set system console device ttyS0 speed '115200'
+set nat destination rule 1000 destination port '3389'
+set nat destination rule 1000 inbound-interface name 'pppoe7'
+set nat destination rule 1000 protocol 'tcp'
+set nat destination rule 1000 translation address '192.168.189.5'
+set nat destination rule 1000 translation port '3389'
+set nat destination rule 10022 destination port '10022'
+set nat destination rule 10022 inbound-interface name 'pppoe7'
+set nat destination rule 10022 protocol 'tcp'
+set nat destination rule 10022 translation address '192.168.189.2'
+set nat destination rule 10022 translation port '22'
+set nat destination rule 10300 destination port '10300'
+set nat destination rule 10300 inbound-interface name 'pppoe7'
+set nat destination rule 10300 protocol 'udp'
+set nat destination rule 10300 translation address '192.168.189.2'
+set nat destination rule 10300 translation port '10300'
+set nat source rule 10 outbound-interface name 'eth1'
+set nat source rule 10 source address '192.168.189.0/24'
+set nat source rule 10 translation address 'masquerade'
+set nat source rule 10 translation options port-mapping 'random'
+set nat source rule 50 outbound-interface name 'pppoe7'
+set nat source rule 50 protocol 'udp'
+set nat source rule 50 source address '192.168.189.2'
+set nat source rule 50 source port '10300'
+set nat source rule 50 translation address 'masquerade'
+set nat source rule 50 translation port '10300'
+set nat source rule 100 outbound-interface name 'pppoe7'
+set nat source rule 100 source address '192.168.189.0/24'
+set nat source rule 100 translation address 'masquerade'
diff --git a/smoketest/configs/nat-basic b/smoketest/configs/nat-basic
new file mode 100644
index 000000000..52f369f34
--- /dev/null
+++ b/smoketest/configs/nat-basic
@@ -0,0 +1,256 @@
+interfaces {
+    bonding bond10 {
+        hash-policy "layer3+4"
+        member {
+            interface "eth2"
+            interface "eth3"
+        }
+        mode "802.3ad"
+        vif 50 {
+            address "192.168.189.1/24"
+        }
+    }
+    ethernet eth0 {
+        disable
+        offload {
+            gro
+            gso
+            rps
+            sg
+            tso
+        }
+    }
+    ethernet eth1 {
+        offload {
+            gro
+            gso
+            rps
+            sg
+            tso
+        }
+    }
+    ethernet eth2 {
+        offload {
+            gro
+            gso
+            rps
+            sg
+            tso
+        }
+    }
+    ethernet eth3 {
+        offload {
+            gro
+            gso
+            rps
+            sg
+            tso
+        }
+    }
+    loopback lo {
+    }
+    pppoe pppoe7 {
+        authentication {
+            password "vyos"
+            username "vyos"
+        }
+        dhcpv6-options {
+            pd 0 {
+                interface bond10.50 {
+                    address "1"
+                }
+                length "56"
+            }
+        }
+        ip {
+            adjust-mss "1452"
+        }
+        ipv6 {
+            address {
+                autoconf
+            }
+            adjust-mss "1432"
+        }
+        mtu "1492"
+        no-peer-dns
+        source-interface "eth1"
+    }
+}
+nat {
+    destination {
+        rule 1000 {
+            destination {
+                port "3389"
+            }
+            inbound-interface {
+                name "pppoe7"
+            }
+            protocol "tcp"
+            translation {
+                address "192.168.189.5"
+                port "3389"
+            }
+        }
+        rule 10022 {
+            destination {
+                port "10022"
+            }
+            inbound-interface {
+                name "pppoe7"
+            }
+            protocol "tcp"
+            translation {
+                address "192.168.189.2"
+                port "22"
+            }
+        }
+        rule 10300 {
+            destination {
+                port "10300"
+            }
+            inbound-interface {
+                name "pppoe7"
+            }
+            protocol "udp"
+            translation {
+                address "192.168.189.2"
+                port "10300"
+            }
+        }
+    }
+    source {
+        rule 10 {
+            outbound-interface {
+                name "eth1"
+            }
+            source {
+                address "192.168.189.0/24"
+            }
+            translation {
+                address "masquerade"
+                options {
+                    port-mapping fully-random
+                }
+            }
+        }
+        rule 50 {
+            outbound-interface {
+                name "pppoe7"
+            }
+            protocol "udp"
+            source {
+                address "192.168.189.2"
+                port "10300"
+            }
+            translation {
+                address "masquerade"
+                port "10300"
+            }
+        }
+        rule 100 {
+            outbound-interface {
+                name "pppoe7"
+            }
+            source {
+                address "192.168.189.0/24"
+            }
+            translation {
+                address "masquerade"
+            }
+        }
+    }
+}
+service {
+    dhcp-server {
+        shared-network-name LAN {
+            subnet 192.168.189.0/24 {
+                default-router "192.168.189.1"
+                domain-name "vyos.net"
+                lease "604800"
+                name-server "1.1.1.1"
+                name-server "9.9.9.9"
+                range 0 {
+                    start "192.168.189.20"
+                    stop "192.168.189.254"
+                }
+            }
+        }
+    }
+    lldp {
+        interface all {
+        }
+        interface eth1 {
+            disable
+        }
+    }
+    ntp {
+        allow-client {
+            address "192.168.189.0/24"
+        }
+        listen-address "192.168.189.1"
+        server time1.vyos.net {
+        }
+        server time2.vyos.net {
+        }
+    }
+    router-advert {
+        interface bond10.50 {
+            prefix ::/64 {
+                preferred-lifetime "2700"
+                valid-lifetime "5400"
+            }
+        }
+    }
+    ssh {
+        disable-host-validation
+        dynamic-protection {
+        }
+    }
+}
+system {
+    config-management {
+        commit-revisions "100"
+    }
+    conntrack {
+        modules {
+            ftp
+            h323
+            nfs
+            pptp
+            sip
+            sqlnet
+            tftp
+        }
+    }
+    console {
+        device ttyS0 {
+            speed "115200"
+        }
+    }
+    domain-name "vyos.net"
+    host-name "R1"
+    login {
+        user vyos {
+            authentication {
+                encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/
+                plaintext-password ""
+            }
+        }
+    }
+    name-server "1.1.1.1"
+    name-server "9.9.9.9"
+    syslog {
+        global {
+            facility all {
+                level "info"
+            }
+            facility local7 {
+                level "debug"
+            }
+        }
+    }
+}
+
+// Warning: Do not remove the following line.
+// vyos-config-version: "bgp@5:broadcast-relay@1:cluster@2:config-management@1:conntrack@5:conntrack-sync@2:container@2:dhcp-relay@2:dhcp-server@8:dhcpv6-server@1:dns-dynamic@4:dns-forwarding@4:firewall@15:flow-accounting@1:https@6:ids@1:interfaces@32:ipoe-server@3:ipsec@13:isis@3:l2tp@9:lldp@2:mdns@1:monitoring@1:nat@7:nat66@3:ntp@3:openconnect@3:ospf@2:pim@1:policy@8:pppoe-server@10:pptp@5:qos@2:quagga@11:rip@1:rpki@2:salt@1:snmp@3:ssh@2:sstp@6:system@27:vrf@3:vrrp@4:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2"
+// Release version: 1.4.0-epa3
diff --git a/src/migration-scripts/nat/7-to-8 b/src/migration-scripts/nat/7-to-8
new file mode 100755
index 000000000..ab2ffa6d3
--- /dev/null
+++ b/src/migration-scripts/nat/7-to-8
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# 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/>.
+
+# T6345: random - In kernel 5.0 and newer this is the same as fully-random.
+#        In earlier kernels the port mapping will be randomized using a seeded
+#        MD5 hash mix using source and destination address and destination port.
+#        drop fully-random from CLI
+
+from sys import argv,exit
+from vyos.configtree import ConfigTree
+
+if len(argv) < 2:
+    print("Must specify file name!")
+    exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+    config_file = f.read()
+
+config = ConfigTree(config_file)
+
+if not config.exists(['nat']):
+    # Nothing to do
+    exit(0)
+
+for direction in ['source', 'destination']:
+    # If a node doesn't exist, we obviously have nothing to do.
+    if not config.exists(['nat', direction]):
+        continue
+
+    # However, we also need to handle the case when a 'source' or 'destination' sub-node does exist,
+    # but there are no rules under it.
+    if not config.list_nodes(['nat', direction]):
+        continue
+
+    for rule in config.list_nodes(['nat', direction, 'rule']):
+        port_mapping = ['nat', direction, 'rule', rule, 'translation', 'options', 'port-mapping']
+        if config.exists(port_mapping):
+            tmp = config.return_value(port_mapping)
+            if tmp == 'fully-random':
+                config.set(port_mapping, value='random')
+
+try:
+    with open(file_name, 'w') as f:
+        f.write(config.to_string())
+except OSError as e:
+    print(f'Failed to save the modified config: {e}')
+    exit(1)
-- 
cgit v1.2.3