diff --git a/data/templates/pppoe/peer.j2 b/data/templates/pppoe/peer.j2
index f30cefe63..2a99fcb2a 100644
--- a/data/templates/pppoe/peer.j2
+++ b/data/templates/pppoe/peer.j2
@@ -1,89 +1,89 @@
 ### Autogenerated by interfaces-pppoe.py ###
 {{ '# ' ~ description if description is vyos_defined else '' }}
 
 # Require peer to provide the local IP address if it is not
 # specified explicitly in the config file.
 noipdefault
 
 # Don't show the password in logfiles:
 hide-password
 
 # Standard Link Control Protocol (LCP) parameters:
 lcp-echo-interval 20
 lcp-echo-failure 3
 
 # RFC 2516, paragraph 7 mandates that the following options MUST NOT be
 # requested and MUST be rejected if requested by the peer:
 # Address-and-Control-Field-Compression (ACFC)
 noaccomp
 
 # Asynchronous-Control-Character-Map (ACCM)
 default-asyncmap
 
 # Override any connect script that may have been set in /etc/ppp/options.
 connect /bin/true
 
 # Don't try to authenticate the remote node
 noauth
 
 # Don't try to proxy ARP for the remote endpoint. User can set proxy
 # arp entries up manually if they wish. More importantly, having
 # the "proxyarp" parameter set disables the "defaultroute" option.
 noproxyarp
 
 # Unlimited connection attempts
 maxfail 0
 
 plugin rp-pppoe.so {{ source_interface }}
 {% if access_concentrator is vyos_defined %}
 pppoe-ac "{{ access_concentrator }}"
 {% endif %}
 {% if service_name is vyos_defined %}
 pppoe-service "{{ service_name }}"
 {% endif %}
 {% if host_uniq is vyos_defined %}
 pppoe-host-uniq "{{ host_uniq }}"
 {% endif %}
 
 persist
 ifname {{ ifname }}
 ipparam {{ ifname }}
 debug
 mtu {{ mtu }}
-mru {{ mtu }}
+mru {{ mru }}
 
 {% if authentication is vyos_defined %}
 {{ 'user "' + authentication.username + '"' if authentication.username is vyos_defined }}
 {{ 'password "' + authentication.password + '"' if authentication.password is vyos_defined }}
 {% endif %}
 
 {{ "usepeerdns" if no_peer_dns is not vyos_defined }}
 
 {% if ipv6 is vyos_defined %}
 +ipv6 {{ 'ipv6cp-use-ipaddr' if ipv6.address.autoconf is vyos_defined }}
 {% else %}
 noipv6
 {% endif %}
 
 {% if holdoff is vyos_defined %}
 holdoff {{ holdoff }}
 {% endif %}
 
 {% if connect_on_demand is vyos_defined %}
 demand
 # See T2249. PPP default route options should only be set when in on-demand
 # mode. As soon as we are not in on-demand mode the default-route handling is
 # passed to the ip-up.d/ip-down.s scripts which is required for VRF support.
 {%     if 'auto' in default_route %}
 defaultroute
 {{ 'defaultroute6' if ipv6 is vyos_defined }}
 {%     elif 'force' in default_route %}
 defaultroute
 replacedefaultroute
 {{ 'defaultroute6' if ipv6 is vyos_defined }}
 {%     endif %}
 {% else %}
 nodefaultroute
 noreplacedefaultroute
 {{ 'nodefaultroute6' if ipv6 is vyos_defined }}
 {% endif %}
diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in
index b78f92c85..30fcb8573 100644
--- a/interface-definitions/interfaces-pppoe.xml.in
+++ b/interface-definitions/interfaces-pppoe.xml.in
@@ -1,140 +1,154 @@
 <?xml version="1.0"?>
 <interfaceDefinition>
   <node name="interfaces">
     <children>
       <tagNode name="pppoe" owner="${vyos_conf_scripts_dir}/interfaces-pppoe.py">
         <properties>
           <help>Point-to-Point Protocol over Ethernet (PPPoE) Interface</help>
           <priority>322</priority>
           <constraint>
             <regex>pppoe[0-9]+</regex>
           </constraint>
           <constraintErrorMessage>PPPoE interface must be named pppoeN</constraintErrorMessage>
           <valueHelp>
             <format>pppoeN</format>
             <description>PPPoE dialer interface name</description>
           </valueHelp>
         </properties>
         <children>
           #include <include/pppoe-access-concentrator.xml.i>
           #include <include/interface/authentication.xml.i>
           #include <include/interface/dial-on-demand.xml.i>
           #include <include/interface/no-default-route.xml.i>
           #include <include/interface/default-route-distance.xml.i>
           #include <include/interface/dhcpv6-options.xml.i>
           #include <include/generic-description.xml.i>
           #include <include/interface/disable.xml.i>
           <leafNode name="idle-timeout">
             <properties>
               <help>Delay before disconnecting idle session (in seconds)</help>
               <valueHelp>
                 <format>u32:0-86400</format>
                 <description>Idle timeout in seconds</description>
               </valueHelp>
               <constraint>
                 <validator name="numeric" argument="--range 0-86400"/>
               </constraint>
               <constraintErrorMessage>Timeout must be in range 0 to 86400</constraintErrorMessage>
             </properties>
           </leafNode>
           <leafNode name="host-uniq">
             <properties>
               <help>PPPoE RFC2516 host-uniq tag</help>
               <valueHelp>
                 <format>txt</format>
                 <description>Host-uniq tag as byte string in HEX</description>
               </valueHelp>
               <constraint>
                 <regex>([a-fA-F0-9][a-fA-F0-9]){1,18}</regex>
               </constraint>
               <constraintErrorMessage>Host-uniq must be specified as hex-adecimal byte-string (even number of HEX characters)</constraintErrorMessage>
             </properties>
           </leafNode>
           <leafNode name="holdoff">
             <properties>
               <help>Delay before re-dial to the access concentrator when PPP session terminated by peer (in seconds)</help>
               <valueHelp>
                 <format>u32:0-86400</format>
                 <description>Holdoff time in seconds</description>
               </valueHelp>
               <constraint>
                 <validator name="numeric" argument="--range 0-86400"/>
               </constraint>
               <constraintErrorMessage>Holdoff must be in range 0 to 86400</constraintErrorMessage>
             </properties>
             <defaultValue>30</defaultValue>
           </leafNode>
           <node name="ip">
             <properties>
               <help>IPv4 routing parameters</help>
             </properties>
             <children>
               #include <include/interface/adjust-mss.xml.i>
               #include <include/interface/disable-forwarding.xml.i>
               #include <include/interface/source-validation.xml.i>
             </children>
           </node>
           <node name="ipv6">
             <properties>
               <help>IPv6 routing parameters</help>
             </properties>
             <children>
               <node name="address">
                 <properties>
                   <help>IPv6 address configuration modes</help>
                 </properties>
                 <children>
                   #include <include/interface/ipv6-address-autoconf.xml.i>
                 </children>
               </node>
               #include <include/interface/adjust-mss.xml.i>
               #include <include/interface/disable-forwarding.xml.i>
             </children>
           </node>
           #include <include/source-interface.xml.i>
           <leafNode name="local-address">
             <properties>
               <help>IPv4 address of local end of the PPPoE link</help>
               <valueHelp>
                 <format>ipv4</format>
                 <description>Address of local end of the PPPoE link</description>
               </valueHelp>
               <constraint>
                 <validator name="ipv4-address"/>
               </constraint>
             </properties>
           </leafNode>
           #include <include/interface/mirror.xml.i>
           #include <include/interface/mtu-68-1500.xml.i>
           <leafNode name="mtu">
             <defaultValue>1492</defaultValue>
           </leafNode>
+          <leafNode name="mru">
+            <properties>
+              <help>Maximum Receive Unit (MRU)</help>
+              <valueHelp>
+                <format>u32:128-16384</format>
+                <description>Maximum Receive Unit in byte</description>
+              </valueHelp>
+              <constraint>
+                <validator name="numeric" argument="--range 128-16384"/>
+              </constraint>
+              <constraintErrorMessage>MRU must be between 128 and 16384</constraintErrorMessage>
+            </properties>
+            <defaultValue>1492</defaultValue>
+          </leafNode>
           #include <include/interface/no-peer-dns.xml.i>
           <leafNode name="remote-address">
             <properties>
               <help>IPv4 address of remote end of the PPPoE link</help>
               <valueHelp>
                 <format>ipv4</format>
                 <description>Address of remote end of the PPPoE link</description>
               </valueHelp>
               <constraint>
                 <validator name="ipv4-address"/>
               </constraint>
             </properties>
           </leafNode>
           <leafNode name="service-name">
             <properties>
               <help>Service name, only connect to access concentrators advertising this</help>
               <constraint>
                 <regex>[a-zA-Z0-9]+</regex>
               </constraint>
               <constraintErrorMessage>Service name must be alphanumeric only</constraintErrorMessage>
             </properties>
           </leafNode>
           #include <include/interface/redirect.xml.i>
           #include <include/interface/vrf.xml.i>
         </children>
       </tagNode>
     </children>
   </node>
 </interfaceDefinition>
diff --git a/smoketest/scripts/cli/test_interfaces_pppoe.py b/smoketest/scripts/cli/test_interfaces_pppoe.py
index 0ce5e2fe0..7b702759f 100755
--- a/smoketest/scripts/cli/test_interfaces_pppoe.py
+++ b/smoketest/scripts/cli/test_interfaces_pppoe.py
@@ -1,210 +1,219 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2019-2023 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/>.
 
 import unittest
 
 from psutil import process_iter
 from base_vyostest_shim import VyOSUnitTestSHIM
 
 from vyos.configsession import ConfigSessionError
 
 config_file = '/etc/ppp/peers/{}'
 base_path = ['interfaces', 'pppoe']
 
 def get_config_value(interface, key):
     with open(config_file.format(interface), 'r') as f:
         for line in f:
             if line.startswith(key):
                 return list(line.split())
     return []
 
 # add a classmethod to setup a temporaray PPPoE server for "proper" validation
 class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
     @classmethod
     def setUpClass(cls):
         super(PPPoEInterfaceTest, cls).setUpClass()
 
         cls._interfaces = ['pppoe10', 'pppoe20', 'pppoe30']
         cls._source_interface = 'eth0'
 
     def tearDown(self):
         # Validate PPPoE client process
         for interface in self._interfaces:
             running = False
             for proc in process_iter():
                 if interface in proc.cmdline():
                     running = True
                     break
             self.assertTrue(running)
 
         self.cli_delete(base_path)
         self.cli_commit()
 
     def test_01_pppoe_client(self):
         # Check if PPPoE dialer can be configured and runs
         for interface in self._interfaces:
             user = f'VyOS-user-{interface}'
             passwd = f'VyOS-passwd-{interface}'
             mtu = '1400'
+            mru = '1300'
 
             self.cli_set(base_path + [interface, 'authentication', 'username', user])
             self.cli_set(base_path + [interface, 'authentication', 'password', passwd])
             self.cli_set(base_path + [interface, 'mtu', mtu])
+            self.cli_set(base_path + [interface, 'mru', '9000'])
             self.cli_set(base_path + [interface, 'no-peer-dns'])
 
             # check validate() - a source-interface is required
             with self.assertRaises(ConfigSessionError):
                 self.cli_commit()
             self.cli_set(base_path + [interface, 'source-interface', self._source_interface])
 
+            # check validate() - MRU needs to be less or equal then MTU
+            with self.assertRaises(ConfigSessionError):
+                self.cli_commit()
+            self.cli_set(base_path + [interface, 'mru', mru])
+
         # commit changes
         self.cli_commit()
 
         # verify configuration file(s)
         for interface in self._interfaces:
             user = f'VyOS-user-{interface}'
             passwd = f'VyOS-passwd-{interface}'
 
             tmp = get_config_value(interface, 'mtu')[1]
             self.assertEqual(tmp, mtu)
+            tmp = get_config_value(interface, 'mru')[1]
+            self.assertEqual(tmp, mru)
             tmp = get_config_value(interface, 'user')[1].replace('"', '')
             self.assertEqual(tmp, user)
             tmp = get_config_value(interface, 'password')[1].replace('"', '')
             self.assertEqual(tmp, passwd)
             tmp = get_config_value(interface, 'ifname')[1]
             self.assertEqual(tmp, interface)
 
     def test_02_pppoe_client_disabled_interface(self):
         # Check if PPPoE Client can be disabled
         for interface in self._interfaces:
             user = f'VyOS-user-{interface}'
             passwd = f'VyOS-passwd-{interface}'
 
             self.cli_set(base_path + [interface, 'authentication', 'username', user])
             self.cli_set(base_path + [interface, 'authentication', 'password', passwd])
             self.cli_set(base_path + [interface, 'source-interface', self._source_interface])
             self.cli_set(base_path + [interface, 'disable'])
 
         self.cli_commit()
 
         # Validate PPPoE client process - must not run as interfaces are disabled
         for interface in self._interfaces:
             running = False
             for proc in process_iter():
                 if interface in proc.cmdline():
                     running = True
                     break
             self.assertFalse(running)
 
         # enable PPPoE interfaces
         for interface in self._interfaces:
             self.cli_delete(base_path + [interface, 'disable'])
 
         self.cli_commit()
 
 
     def test_03_pppoe_authentication(self):
         # When username or password is set - so must be the other
         for interface in self._interfaces:
             user = f'VyOS-user-{interface}'
             passwd = f'VyOS-passwd-{interface}'
 
             self.cli_set(base_path + [interface, 'authentication', 'username', user])
             self.cli_set(base_path + [interface, 'source-interface', self._source_interface])
             self.cli_set(base_path + [interface, 'ipv6', 'address', 'autoconf'])
 
             # check validate() - if user is set, so must be the password
             with self.assertRaises(ConfigSessionError):
                 self.cli_commit()
 
             self.cli_set(base_path + [interface, 'authentication', 'password', passwd])
 
         self.cli_commit()
 
     def test_04_pppoe_dhcpv6pd(self):
         # Check if PPPoE dialer can be configured with DHCPv6-PD
         address = '1'
         sla_id = '0'
         sla_len = '8'
 
         for interface in self._interfaces:
             user = f'VyOS-user-{interface}'
             passwd = f'VyOS-passwd-{interface}'
 
             self.cli_set(base_path + [interface, 'authentication', 'username', user])
             self.cli_set(base_path + [interface, 'authentication', 'password', passwd])
             self.cli_set(base_path + [interface, 'no-default-route'])
             self.cli_set(base_path + [interface, 'no-peer-dns'])
             self.cli_set(base_path + [interface, 'source-interface', self._source_interface])
             self.cli_set(base_path + [interface, 'ipv6', 'address', 'autoconf'])
 
             # prefix delegation stuff
             dhcpv6_pd_base = base_path + [interface, 'dhcpv6-options', 'pd', '0']
             self.cli_set(dhcpv6_pd_base + ['length', '56'])
             self.cli_set(dhcpv6_pd_base + ['interface', self._source_interface, 'address', address])
             self.cli_set(dhcpv6_pd_base + ['interface', self._source_interface, 'sla-id',  sla_id])
 
         # commit changes
         self.cli_commit()
 
         for interface in self._interfaces:
             user = f'VyOS-user-{interface}'
             passwd = f'VyOS-passwd-{interface}'
 
             # verify "normal" PPPoE value - 1492 is default MTU
             tmp = get_config_value(interface, 'mtu')[1]
             self.assertEqual(tmp, '1492')
             tmp = get_config_value(interface, 'user')[1].replace('"', '')
             self.assertEqual(tmp, user)
             tmp = get_config_value(interface, 'password')[1].replace('"', '')
             self.assertEqual(tmp, passwd)
             tmp = get_config_value(interface, '+ipv6 ipv6cp-use-ipaddr')
             self.assertListEqual(tmp, ['+ipv6', 'ipv6cp-use-ipaddr'])
 
     def test_05_pppoe_options(self):
         # Check if PPPoE dialer can be configured with DHCPv6-PD
         for interface in self._interfaces:
             user = f'VyOS-user-{interface}'
             passwd = f'VyOS-passwd-{interface}'
             ac_name = f'AC{interface}'
             service_name = f'SRV{interface}'
             host_uniq = 'cafebeefBABE123456'
 
             self.cli_set(base_path + [interface, 'authentication', 'username', user])
             self.cli_set(base_path + [interface, 'authentication', 'password', passwd])
             self.cli_set(base_path + [interface, 'source-interface', self._source_interface])
 
             self.cli_set(base_path + [interface, 'access-concentrator', ac_name])
             self.cli_set(base_path + [interface, 'service-name', service_name])
             self.cli_set(base_path + [interface, 'host-uniq', host_uniq])
 
         # commit changes
         self.cli_commit()
 
         for interface in self._interfaces:
             ac_name = f'AC{interface}'
             service_name = f'SRV{interface}'
             host_uniq = 'cafebeefBABE123456'
 
             tmp = get_config_value(interface, 'pppoe-ac')[1]
             self.assertEqual(tmp, f'"{ac_name}"')
             tmp = get_config_value(interface, 'pppoe-service')[1]
             self.assertEqual(tmp, f'"{service_name}"')
             tmp = get_config_value(interface, 'pppoe-host-uniq')[1]
             self.assertEqual(tmp, f'"{host_uniq}"')
 
 if __name__ == '__main__':
     unittest.main(verbosity=2)
diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py
index fca91253c..0a03a172c 100755
--- a/src/conf_mode/interfaces-pppoe.py
+++ b/src/conf_mode/interfaces-pppoe.py
@@ -1,137 +1,142 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2019-2021 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/>.
 
 import os
 
 from sys import exit
 from copy import deepcopy
 from netifaces import interfaces
 
 from vyos.config import Config
 from vyos.configdict import get_interface_dict
 from vyos.configdict import is_node_changed
 from vyos.configdict import get_pppoe_interfaces
 from vyos.configverify import verify_authentication
 from vyos.configverify import verify_source_interface
 from vyos.configverify import verify_interface_exists
 from vyos.configverify import verify_vrf
 from vyos.configverify import verify_mtu_ipv6
 from vyos.configverify import verify_mirror_redirect
 from vyos.ifconfig import PPPoEIf
 from vyos.template import render
 from vyos.utils.process import call
 from vyos.utils.process import is_systemd_service_running
 from vyos import ConfigError
 from vyos import airbag
 airbag.enable()
 
 def get_config(config=None):
     """
     Retrive CLI config as dictionary. Dictionary can never be empty, as at least the
     interface name will be added or a deleted flag
     """
     if config:
         conf = config
     else:
         conf = Config()
     base = ['interfaces', 'pppoe']
     ifname, pppoe = get_interface_dict(conf, base)
 
     # We should only terminate the PPPoE session if critical parameters change.
     # All parameters that can be changed on-the-fly (like interface description)
     # should not lead to a reconnect!
     for options in ['access-concentrator', 'connect-on-demand', 'service-name',
                     'source-interface', 'vrf', 'no-default-route',
                     'authentication', 'host_uniq']:
         if is_node_changed(conf, base + [ifname, options]):
             pppoe.update({'shutdown_required': {}})
             # bail out early - no need to further process other nodes
             break
 
     return pppoe
 
 def verify(pppoe):
     if 'deleted' in pppoe:
         # bail out early
         return None
 
     verify_source_interface(pppoe)
     verify_authentication(pppoe)
     verify_vrf(pppoe)
     verify_mtu_ipv6(pppoe)
     verify_mirror_redirect(pppoe)
 
     if {'connect_on_demand', 'vrf'} <= set(pppoe):
         raise ConfigError('On-demand dialing and VRF can not be used at the same time')
 
+    # both MTU and MRU have default values, thus we do not need to check
+    # if the key exists
+    if int(pppoe['mru']) > int(pppoe['mtu']):
+        raise ConfigError('PPPoE MRU needs to be lower then MTU!')
+
     return None
 
 def generate(pppoe):
     # set up configuration file path variables where our templates will be
     # rendered into
     ifname = pppoe['ifname']
     config_pppoe = f'/etc/ppp/peers/{ifname}'
 
     if 'deleted' in pppoe or 'disable' in pppoe:
         if os.path.exists(config_pppoe):
             os.unlink(config_pppoe)
 
         return None
 
     # Create PPP configuration files
     render(config_pppoe, 'pppoe/peer.j2', pppoe, permission=0o640)
 
     return None
 
 def apply(pppoe):
     ifname = pppoe['ifname']
     if 'deleted' in pppoe or 'disable' in pppoe:
         if os.path.isdir(f'/sys/class/net/{ifname}'):
             p = PPPoEIf(ifname)
             p.remove()
         call(f'systemctl stop ppp@{ifname}.service')
         return None
 
     # reconnect should only be necessary when certain config options change,
     # like ACS name, authentication ... (see get_config() for details)
     if ((not is_systemd_service_running(f'ppp@{ifname}.service')) or
         'shutdown_required' in pppoe):
 
         # cleanup system (e.g. FRR routes first)
         if os.path.isdir(f'/sys/class/net/{ifname}'):
             p = PPPoEIf(ifname)
             p.remove()
 
         call(f'systemctl restart ppp@{ifname}.service')
         # When interface comes "live" a hook is called:
         # /etc/ppp/ip-up.d/99-vyos-pppoe-callback
         # which triggers PPPoEIf.update()
     else:
         if os.path.isdir(f'/sys/class/net/{ifname}'):
             p = PPPoEIf(ifname)
             p.update(pppoe)
 
     return None
 
 if __name__ == '__main__':
     try:
         c = get_config()
         verify(c)
         generate(c)
         apply(c)
     except ConfigError as e:
         print(e)
         exit(1)