From 103e8404cdea70dad486940f209b9683f1c7b936 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 20 Sep 2020 15:18:50 +0200 Subject: ifconfig: T2653: remove duplicates of get_config() A lot of derived classes from Interface implemented their own get_config() method which more or less was the same everywhere. We also hat different qualifiers like @staticmethod or @classmethod. This is now changed to only have the @classmethod in Interface base class which will return the necessary dictionary keys for the required interfaces. This change is a mid reduction in lines of code which is always a very nice thing! --- src/conf_mode/interfaces-macsec.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'src/conf_mode/interfaces-macsec.py') diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index 2866ccc0a..73c80866a 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -16,7 +16,6 @@ import os -from copy import deepcopy from sys import exit from vyos.config import Config @@ -102,12 +101,11 @@ def apply(macsec): os.unlink(wpa_suppl_conf.format(**macsec)) else: - # MACsec interfaces require a configuration when they are added using - # iproute2. This static method will provide the configuration - # dictionary used by this class. - - # XXX: subject of removal after completing T2653 - conf = deepcopy(MACsecIf.get_config()) + # This is a special type of interface which needs additional parameters + # when created using iproute2. Instead of passing a ton of arguments, + # use a dictionary provided by the interface class which holds all the + # options necessary. + conf = MACsecIf.get_config() conf['source_interface'] = macsec['source_interface'] conf['security_cipher'] = macsec['security']['cipher'] -- cgit v1.2.3 From a420f7637f80a9ad16160efcac8c2095fa52382b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 20 Sep 2020 21:05:03 +0200 Subject: macsec: T2023: add missing mtu CLI option Base MTU for MACsec is 1468 bytes (encryption headers), but we leave room for 802.1ad and 802.1q VLAN tags, thus the limit is lowered to 1460 bytes to not make the user juggle with the MTU bytes if he enables VLAN support later on, which is yet to come. --- interface-definitions/interfaces-macsec.xml.in | 3 ++- src/conf_mode/interfaces-macsec.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'src/conf_mode/interfaces-macsec.py') diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in index dfef387d2..9384726cc 100644 --- a/interface-definitions/interfaces-macsec.xml.in +++ b/interface-definitions/interfaces-macsec.xml.in @@ -107,8 +107,9 @@ #include #include - #include + #include #include + #include diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index 73c80866a..abf8b05c3 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -21,6 +21,7 @@ from sys import exit from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.ifconfig import MACsecIf +from vyos.ifconfig import Interface from vyos.template import render from vyos.util import call from vyos.configverify import verify_vrf @@ -46,6 +47,14 @@ def get_config(config=None): base = ['interfaces', 'macsec'] macsec = get_interface_dict(conf, base) + # MACsec is "special" the default MTU is 1460 - update accordingly + # as the config_level is already st in get_interface_dict() - we can use [] + tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) + if 'mtu' not in tmp: + # base MTU for MACsec is 1468 bytes, but we leave room for 802.1ad and + # 802.1q VLAN tags, thus the limit is 1460 bytes. + macsec['mtu'] = '1460' + # Check if interface has been removed if 'deleted' in macsec: source_interface = conf.return_effective_value( @@ -79,6 +88,15 @@ def verify(macsec): raise ConfigError('Missing mandatory MACsec security ' 'keys as encryption is enabled!') + if 'source_interface' in macsec: + # MACsec adds a 40 byte overhead (32 byte MACsec + 8 bytes VLAN 802.1ad + # and 802.1q) - we need to check the underlaying MTU if our configured + # MTU is at least 40 bytes less then the MTU of our physical interface. + underlay_mtu = int(Interface(macsec['source_interface']).get_mtu()) + if underlay_mtu < (int(macsec['mtu']) + 40): + raise ConfigError('MACsec overhead does not fit into underlaying device MTU,\n' \ + f'{underlay_mtu} bytes is too small!') + return None -- cgit v1.2.3 From e31dfd9f5542b0572e3ece89bdc347679b08aa72 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 21 Sep 2020 22:24:25 +0200 Subject: macsec: T2788: source-interface must not be member of a bridge Add verify() step to ensure the macsec source-interface is not already part of a bridge interface. This should probably also be checked for bond interfaces. --- src/conf_mode/interfaces-macsec.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/conf_mode/interfaces-macsec.py') diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index abf8b05c3..73b62dcf1 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -28,6 +28,7 @@ from vyos.configverify import verify_vrf from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_source_interface +from vyos.validate import is_member from vyos import ConfigError from vyos import airbag airbag.enable() @@ -61,6 +62,11 @@ def get_config(config=None): base + ['source-interface']) macsec.update({'source_interface': source_interface}) + if 'source_interface' in macsec: + # Check if source interface is used by another bridge + tmp = is_member(conf, macsec['source_interface'], 'bridge') + if tmp: macsec.update({'is_bridge_member_source_interface' : tmp}) + return macsec @@ -88,6 +94,10 @@ def verify(macsec): raise ConfigError('Missing mandatory MACsec security ' 'keys as encryption is enabled!') + if 'is_bridge_member_source_interface' in macsec: + raise ConfigError('source-interface is already member of bridge ' \ + '{is_bridge_member_source_interface}!'.format(**macsec)) + if 'source_interface' in macsec: # MACsec adds a 40 byte overhead (32 byte MACsec + 8 bytes VLAN 802.1ad # and 802.1q) - we need to check the underlaying MTU if our configured -- cgit v1.2.3 From d28a6a516d449ede788816574c35061fbf7d6485 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 22 Sep 2020 18:35:44 +0200 Subject: ifconfig: T2653: move is_member() from vyos.vylidate to vyos.configdict --- python/vyos/configdict.py | 54 +++++++++++++++++++++++++---- python/vyos/validate.py | 41 ---------------------- src/conf_mode/interfaces-bonding.py | 19 +++++----- src/conf_mode/interfaces-bridge.py | 27 +++++++-------- src/conf_mode/interfaces-macsec.py | 10 ------ src/conf_mode/interfaces-openvpn.py | 3 +- src/conf_mode/interfaces-pseudo-ethernet.py | 25 +------------ src/conf_mode/interfaces-tunnel.py | 9 ++--- 8 files changed, 77 insertions(+), 111 deletions(-) (limited to 'src/conf_mode/interfaces-macsec.py') diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index ef1b452a8..4a4a767f3 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -21,8 +21,9 @@ import os from copy import deepcopy from vyos.util import vyos_dict_search -from vyos.validate import is_member from vyos.xml import defaults +from vyos.xml import is_tag +from vyos.xml import is_leaf from vyos import ConfigError def retrieve_config(path_hash, base_path, config): @@ -186,6 +187,47 @@ def T2665_set_dhcpv6pd_defaults(config_dict): return config_dict +def is_member(conf, interface, intftype=None): + """ + Checks if passed interface is member of other interface of specified type. + intftype is optional, if not passed it will search all known types + (currently bridge and bonding) + + Returns: + None -> Interface is not a member + interface name -> Interface is a member of this interface + False -> interface type cannot have members + """ + ret_val = None + intftypes = ['bonding', 'bridge'] + if intftype not in intftypes + [None]: + raise ValueError(( + f'unknown interface type "{intftype}" or it cannot ' + f'have member interfaces')) + + intftype = intftypes if intftype == None else [intftype] + + # set config level to root + old_level = conf.get_level() + conf.set_level([]) + + for it in intftype: + base = ['interfaces', it] + for intf in conf.list_nodes(base): + memberintf = base + [intf, 'member', 'interface'] + if is_tag(memberintf): + if interface in conf.list_nodes(memberintf): + ret_val = intf + break + elif is_leaf(memberintf): + if ( conf.exists(memberintf) and + interface in conf.return_values(memberintf) ): + ret_val = intf + break + + old_level = conf.set_level(old_level) + return ret_val + def get_interface_dict(config, base, ifname=''): """ Common utility function to retrieve and mandgle the interfaces available @@ -236,17 +278,15 @@ def get_interface_dict(config, base, ifname=''): # Check if we are a member of a bridge device bridge = is_member(config, ifname, 'bridge') - if bridge: - dict.update({'is_bridge_member' : bridge}) + if bridge: dict.update({'is_bridge_member' : bridge}) # Check if we are a member of a bond device bond = is_member(config, ifname, 'bonding') - if bond: - dict.update({'is_bond_member' : bond}) + if bond: dict.update({'is_bond_member' : bond}) + mac = leaf_node_changed(config, ['mac']) - if mac: - dict.update({'mac_old' : mac}) + if mac: dict.update({'mac_old' : mac}) eui64 = leaf_node_changed(config, ['ipv6', 'address', 'eui64']) if eui64: diff --git a/python/vyos/validate.py b/python/vyos/validate.py index ceeb6888a..691cf3c8e 100644 --- a/python/vyos/validate.py +++ b/python/vyos/validate.py @@ -19,7 +19,6 @@ import netifaces import ipaddress from vyos.util import cmd -from vyos import xml # Important note when you are adding new validation functions: # @@ -267,46 +266,6 @@ def assert_mac(m): raise ValueError(f'{m} is a VRRP MAC address') -def is_member(conf, interface, intftype=None): - """ - Checks if passed interface is member of other interface of specified type. - intftype is optional, if not passed it will search all known types - (currently bridge and bonding) - - Returns: - None -> Interface is not a member - interface name -> Interface is a member of this interface - False -> interface type cannot have members - """ - ret_val = None - if intftype not in ['bonding', 'bridge', None]: - raise ValueError(( - f'unknown interface type "{intftype}" or it cannot ' - f'have member interfaces')) - - intftype = ['bonding', 'bridge'] if intftype == None else [intftype] - - # set config level to root - old_level = conf.get_level() - conf.set_level([]) - - for it in intftype: - base = ['interfaces', it] - for intf in conf.list_nodes(base): - memberintf = base + [intf, 'member', 'interface'] - if xml.is_tag(memberintf): - if interface in conf.list_nodes(memberintf): - ret_val = intf - break - elif xml.is_leaf(memberintf): - if ( conf.exists(memberintf) and - interface in conf.return_values(memberintf) ): - ret_val = intf - break - - old_level = conf.set_level(old_level) - return ret_val - def has_address_configured(conf, intf): """ Checks if interface has an address configured. diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index a9679b47c..5ac4feb77 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -22,6 +22,7 @@ from netifaces import interfaces from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configdict import leaf_node_changed +from vyos.configdict import is_member from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_dhcpv6 @@ -30,7 +31,7 @@ from vyos.configverify import verify_vlan_config from vyos.configverify import verify_vrf from vyos.ifconfig import BondIf from vyos.ifconfig import Section -from vyos.validate import is_member +from vyos.util import vyos_dict_search from vyos.validate import has_address_configured from vyos import ConfigError from vyos import airbag @@ -98,14 +99,13 @@ def get_config(config=None): # also present the interfaces to be removed from the bond as dictionary bond['member'].update({'interface_remove': tmp}) - if 'member' in bond and 'interface' in bond['member']: + if vyos_dict_search('member.interface', bond): for interface, interface_config in bond['member']['interface'].items(): - # Check if we are a member of another bond device + # Check if member interface is already member of another bridge tmp = is_member(conf, interface, 'bridge') - if tmp: - interface_config.update({'is_bridge_member' : tmp}) + if tmp: interface_config.update({'is_bridge_member' : tmp}) - # Check if we are a member of a bond device + # Check if member interface is already member of a bond tmp = is_member(conf, interface, 'bonding') if tmp and tmp != bond['ifname']: interface_config.update({'is_bond_member' : tmp}) @@ -144,10 +144,9 @@ def verify(bond): verify_vlan_config(bond) bond_name = bond['ifname'] - if 'member' in bond: - member = bond.get('member') - for interface, interface_config in member.get('interface', {}).items(): - error_msg = f'Can not add interface "{interface}" to bond "{bond_name}", ' + if vyos_dict_search('member.interface', bond): + for interface, interface_config in bond['member']['interface'].items(): + error_msg = f'Can not add interface "{interface}" to bond, ' if interface == 'lo': raise ConfigError('Loopback interface "lo" can not be added to a bond') diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py index 47c8c05f9..3bddac023 100755 --- a/src/conf_mode/interfaces-bridge.py +++ b/src/conf_mode/interfaces-bridge.py @@ -22,13 +22,15 @@ from netifaces import interfaces from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configdict import node_changed +from vyos.configdict import is_member from vyos.configverify import verify_dhcpv6 from vyos.configverify import verify_vrf from vyos.ifconfig import BridgeIf -from vyos.validate import is_member, has_address_configured +from vyos.validate import has_address_configured from vyos.xml import defaults from vyos.util import cmd +from vyos.util import vyos_dict_search from vyos import ConfigError from vyos import airbag @@ -54,8 +56,8 @@ def get_config(config=None): else: bridge.update({'member': {'interface_remove': tmp }}) - if 'member' in bridge and 'interface' in bridge['member']: - # XXX TT2665 we need a copy of the dict keys for iteration, else we will get: + if vyos_dict_search('member.interface', bridge): + # XXX: T2665: we need a copy of the dict keys for iteration, else we will get: # RuntimeError: dictionary changed size during iteration for interface in list(bridge['member']['interface']): for key in ['cost', 'priority']: @@ -69,20 +71,19 @@ def get_config(config=None): for interface, interface_config in bridge['member']['interface'].items(): interface_config.update(default_member_values) - # Check if we are a member of another bridge device + # Check if member interface is already member of another bridge tmp = is_member(conf, interface, 'bridge') if tmp and tmp != bridge['ifname']: interface_config.update({'is_bridge_member' : tmp}) - # Check if we are a member of a bond device + # Check if member interface is already member of a bond tmp = is_member(conf, interface, 'bonding') - if tmp: - interface_config.update({'is_bond_member' : tmp}) + if tmp: interface_config.update({'is_bond_member' : tmp}) + # Bridge members must not have an assigned address tmp = has_address_configured(conf, interface) - if tmp: - interface_config.update({'has_address' : ''}) + if tmp: interface_config.update({'has_address' : ''}) return bridge @@ -93,11 +94,9 @@ def verify(bridge): verify_dhcpv6(bridge) verify_vrf(bridge) - if 'member' in bridge: - member = bridge.get('member') - bridge_name = bridge['ifname'] - for interface, interface_config in member.get('interface', {}).items(): - error_msg = f'Can not add interface "{interface}" to bridge "{bridge_name}", ' + if vyos_dict_search('member.interface', bridge): + for interface, interface_config in bridge['member']['interface'].items(): + error_msg = f'Can not add interface "{interface}" to bridge, ' if interface == 'lo': raise ConfigError('Loopback interface "lo" can not be added to a bridge') diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index 73b62dcf1..abf8b05c3 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -28,7 +28,6 @@ from vyos.configverify import verify_vrf from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_source_interface -from vyos.validate import is_member from vyos import ConfigError from vyos import airbag airbag.enable() @@ -62,11 +61,6 @@ def get_config(config=None): base + ['source-interface']) macsec.update({'source_interface': source_interface}) - if 'source_interface' in macsec: - # Check if source interface is used by another bridge - tmp = is_member(conf, macsec['source_interface'], 'bridge') - if tmp: macsec.update({'is_bridge_member_source_interface' : tmp}) - return macsec @@ -94,10 +88,6 @@ def verify(macsec): raise ConfigError('Missing mandatory MACsec security ' 'keys as encryption is enabled!') - if 'is_bridge_member_source_interface' in macsec: - raise ConfigError('source-interface is already member of bridge ' \ - '{is_bridge_member_source_interface}!'.format(**macsec)) - if 'source_interface' in macsec: # MACsec adds a 40 byte overhead (32 byte MACsec + 8 bytes VLAN 802.1ad # and 802.1q) - we need to check the underlaying MTU if our configured diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index 958b305dd..f83590209 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -26,10 +26,11 @@ from shutil import rmtree from vyos.config import Config from vyos.configdict import list_diff +from vyos.configdict import is_member from vyos.ifconfig import VTunIf from vyos.template import render from vyos.util import call, chown, chmod_600, chmod_755 -from vyos.validate import is_addr_assigned, is_member, is_ipv4 +from vyos.validate import is_addr_assigned, is_ipv4 from vyos import ConfigError from vyos import airbag diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py index 98397b28f..ddbef56ac 100755 --- a/src/conf_mode/interfaces-pseudo-ethernet.py +++ b/src/conf_mode/interfaces-pseudo-ethernet.py @@ -25,7 +25,6 @@ from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_source_interface from vyos.configverify import verify_vlan_config from vyos.ifconfig import MACVLANIf -from vyos.validate import is_member from vyos import ConfigError from vyos import airbag @@ -44,19 +43,7 @@ def get_config(config=None): peth = get_interface_dict(conf, base) mode = leaf_node_changed(conf, ['mode']) - if mode: - peth.update({'mode_old' : mode}) - - # Check if source-interface is member of a bridge device - if 'source_interface' in peth: - bridge = is_member(conf, peth['source_interface'], 'bridge') - if bridge: - peth.update({'source_interface_is_bridge_member' : bridge}) - - # Check if we are a member of a bond device - bond = is_member(conf, peth['source_interface'], 'bonding') - if bond: - peth.update({'source_interface_is_bond_member' : bond}) + if mode: peth.update({'mode_old' : mode}) return peth @@ -69,16 +56,6 @@ def verify(peth): verify_vrf(peth) verify_address(peth) - if 'source_interface_is_bridge_member' in peth: - raise ConfigError( - 'Source interface "{source_interface}" can not be used as it is already a ' - 'member of bridge "{source_interface_is_bridge_member}"!'.format(**peth)) - - if 'source_interface_is_bond_member' in peth: - raise ConfigError( - 'Source interface "{source_interface}" can not be used as it is already a ' - 'member of bond "{source_interface_is_bond_member}"!'.format(**peth)) - # use common function to verify VLAN configuration verify_vlan_config(peth) return None diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py index 11d8d6edc..f1d885b15 100755 --- a/src/conf_mode/interfaces-tunnel.py +++ b/src/conf_mode/interfaces-tunnel.py @@ -22,10 +22,11 @@ from copy import deepcopy from netifaces import interfaces from vyos.config import Config +from vyos.configdict import is_member from vyos.ifconfig import Interface, GREIf, GRETapIf, IPIPIf, IP6GREIf, IPIP6If, IP6IP6If, SitIf, Sit6RDIf from vyos.ifconfig.afi import IP4, IP6 from vyos.configdict import list_diff -from vyos.validate import is_ipv4, is_ipv6, is_member +from vyos.validate import is_ipv4, is_ipv6 from vyos import ConfigError from vyos.dicts import FixedDict @@ -170,8 +171,8 @@ class ConfigurationState(object): """ >>> conf.get_values('addresses', 'address') will place a list of the new IP present in 'interface dummy dum1 address' - into the dictionnary entry "-add" (here 'addresses-add') using - Config.return_values and will add the the one which were removed in into + into the dictionnary entry "-add" (here 'addresses-add') using + Config.return_values and will add the the one which were removed in into the entry "-del" (here addresses-del') """ add_name = f'{name}-add' @@ -263,7 +264,7 @@ class ConfigurationState(object): d = d[lpath[-1]] # XXX: it should have provided me the content and not the key self._conf.set_level(l) - return d + return d def to_api(self): """ -- cgit v1.2.3 From 818a75c024e4b4c0403ccfe782fb55517f390bef Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 25 Sep 2020 20:48:15 +0200 Subject: ifconfig: T2653: get_mtu() should return int() for easier comparison --- python/vyos/ifconfig/interface.py | 2 +- src/conf_mode/interfaces-macsec.py | 4 ++-- src/conf_mode/interfaces-vxlan.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/conf_mode/interfaces-macsec.py') diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index c8ba05edd..a0f0ffe04 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -321,7 +321,7 @@ class Interface(Control): >>> Interface('eth0').get_mtu() '1500' """ - return self.get_interface('mtu') + return int(self.get_interface('mtu')) def set_mtu(self, mtu): """ diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index abf8b05c3..a224c540e 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -92,8 +92,8 @@ def verify(macsec): # MACsec adds a 40 byte overhead (32 byte MACsec + 8 bytes VLAN 802.1ad # and 802.1q) - we need to check the underlaying MTU if our configured # MTU is at least 40 bytes less then the MTU of our physical interface. - underlay_mtu = int(Interface(macsec['source_interface']).get_mtu()) - if underlay_mtu < (int(macsec['mtu']) + 40): + lower_mtu = Interface(macsec['source_interface']).get_mtu() + if lower_mtu < (int(macsec['mtu']) + 40): raise ConfigError('MACsec overhead does not fit into underlaying device MTU,\n' \ f'{underlay_mtu} bytes is too small!') diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py index a00c58608..850ea28d7 100755 --- a/src/conf_mode/interfaces-vxlan.py +++ b/src/conf_mode/interfaces-vxlan.py @@ -72,8 +72,8 @@ def verify(vxlan): if 'source_interface' in vxlan: # VXLAN adds a 50 byte overhead - we need to check the underlaying MTU # if our configured MTU is at least 50 bytes less - underlay_mtu = int(Interface(vxlan['source_interface']).get_mtu()) - if underlay_mtu < (int(vxlan['mtu']) + 50): + lower_mtu = Interface(vxlan['source_interface']).get_mtu() + if lower_mtu < (int(vxlan['mtu']) + 50): raise ConfigError('VXLAN has a 50 byte overhead, underlaying device ' \ f'MTU is to small ({underlay_mtu} bytes)') -- cgit v1.2.3 From 5db3d63160670c796ed74a170862c367048d89bb Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 26 Sep 2020 12:00:06 +0200 Subject: ifconfig: mtu: disallow MTU < 1280 bytes when IPv6 is enabled on the interface Using an MTU less then the required 1280 bytes (as per RFC) on an interface where IPv6 is not explicitly disabled by: - set interfaces ethernet eth1 ipv6 address no-default-link-local - not having any other IPv6 address configured Will now trigger a commit error via verify() instead of raising FileNotFoundError! --- python/vyos/configverify.py | 30 ++++++++++++++++++++++++++++++ src/conf_mode/interfaces-bonding.py | 2 ++ src/conf_mode/interfaces-bridge.py | 2 ++ src/conf_mode/interfaces-ethernet.py | 3 +++ src/conf_mode/interfaces-geneve.py | 2 ++ src/conf_mode/interfaces-l2tpv3.py | 2 ++ src/conf_mode/interfaces-macsec.py | 2 ++ src/conf_mode/interfaces-pppoe.py | 2 ++ src/conf_mode/interfaces-vxlan.py | 2 ++ src/conf_mode/interfaces-wireguard.py | 2 ++ 10 files changed, 49 insertions(+) (limited to 'src/conf_mode/interfaces-macsec.py') diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 6e5ba1df0..944fc4294 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -44,6 +44,36 @@ def verify_mtu(config): raise ConfigError(f'Interface MTU too high, ' \ f'maximum supported MTU is {max_mtu}!') +def verify_mtu_ipv6(config): + """ + Common helper function used by interface implementations to perform + recurring validation if the specified MTU can be used when IPv6 is + configured on the interface. IPv6 requires a 1280 bytes MTU. + """ + from vyos.validate import is_ipv6 + from vyos.util import vyos_dict_search + # IPv6 minimum required link mtu + min_mtu = 1280 + + if int(config['mtu']) < min_mtu: + interface = config['ifname'] + error_msg = f'IPv6 address will be configured on interface "{interface}" ' \ + f'thus the minimum MTU requirement is {min_mtu}!' + + if not vyos_dict_search('ipv6.address.no_default_link_local', config): + raise ConfigError('link-local ' + error_msg) + + for address in (vyos_dict_search('address', config) or []): + if address in ['dhcpv6'] or is_ipv6(address): + raise ConfigError(error_msg) + + if vyos_dict_search('ipv6.address.autoconf', config): + raise ConfigError(error_msg) + + if vyos_dict_search('ipv6.address.eui64', config): + raise ConfigError(error_msg) + + def verify_vrf(config): """ Common helper function used by interface implementations to perform diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index aece2a04b..9763620ac 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -28,6 +28,7 @@ from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_dhcpv6 from vyos.configverify import verify_source_interface +from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_vlan_config from vyos.configverify import verify_vrf from vyos.ifconfig import BondIf @@ -141,6 +142,7 @@ def verify(bond): raise ConfigError('Option primary - mode dependency failed, not' 'supported in mode {mode}!'.format(**bond)) + verify_mtu_ipv6(bond) verify_address(bond) verify_dhcpv6(bond) verify_vrf(bond) diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py index 485decb17..4ac9c8963 100755 --- a/src/conf_mode/interfaces-bridge.py +++ b/src/conf_mode/interfaces-bridge.py @@ -25,6 +25,7 @@ from vyos.configdict import node_changed from vyos.configdict import is_member from vyos.configdict import is_source_interface from vyos.configverify import verify_dhcpv6 +from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_vrf from vyos.ifconfig import BridgeIf from vyos.validate import has_address_configured @@ -95,6 +96,7 @@ def verify(bridge): if 'deleted' in bridge: return None + verify_mtu_ipv6(bridge) verify_dhcpv6(bridge) verify_vrf(bridge) diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index 5468c7bda..1f622c003 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -24,6 +24,7 @@ from vyos.configverify import verify_address from vyos.configverify import verify_dhcpv6 from vyos.configverify import verify_interface_exists from vyos.configverify import verify_mtu +from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_vlan_config from vyos.configverify import verify_vrf from vyos.ifconfig import EthernetIf @@ -42,6 +43,7 @@ def get_config(config=None): conf = Config() base = ['interfaces', 'ethernet'] ethernet = get_interface_dict(conf, base) + return ethernet def verify(ethernet): @@ -59,6 +61,7 @@ def verify(ethernet): raise ConfigError('If duplex is hardcoded, speed must be hardcoded, too') verify_mtu(ethernet) + verify_mtu_ipv6(ethernet) verify_dhcpv6(ethernet) verify_address(ethernet) verify_vrf(ethernet) diff --git a/src/conf_mode/interfaces-geneve.py b/src/conf_mode/interfaces-geneve.py index af7c121f4..979a5612e 100755 --- a/src/conf_mode/interfaces-geneve.py +++ b/src/conf_mode/interfaces-geneve.py @@ -22,6 +22,7 @@ from netifaces import interfaces from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configverify import verify_address +from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_bridge_delete from vyos.ifconfig import GeneveIf from vyos import ConfigError @@ -47,6 +48,7 @@ def verify(geneve): verify_bridge_delete(geneve) return None + verify_mtu_ipv6(geneve) verify_address(geneve) if 'remote' not in geneve: diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py index 2653ff19c..1118143e4 100755 --- a/src/conf_mode/interfaces-l2tpv3.py +++ b/src/conf_mode/interfaces-l2tpv3.py @@ -24,6 +24,7 @@ from vyos.configdict import get_interface_dict from vyos.configdict import leaf_node_changed from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete +from vyos.configverify import verify_mtu_ipv6 from vyos.ifconfig import L2TPv3If from vyos.util import check_kmod from vyos.validate import is_addr_assigned @@ -80,6 +81,7 @@ def verify(l2tpv3): raise ConfigError('L2TPv3 local-ip address ' '"{local_ip}" is not configured!'.format(**l2tpv3)) + verify_mtu_ipv6(l2tpv3) verify_address(l2tpv3) return None diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index a224c540e..0a20a121b 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -27,6 +27,7 @@ from vyos.util import call from vyos.configverify import verify_vrf from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete +from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_source_interface from vyos import ConfigError from vyos import airbag @@ -71,6 +72,7 @@ def verify(macsec): verify_source_interface(macsec) verify_vrf(macsec) + verify_mtu_ipv6(macsec) verify_address(macsec) if not (('security' in macsec) and diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py index 1b4b9e4ee..ee3b142c8 100755 --- a/src/conf_mode/interfaces-pppoe.py +++ b/src/conf_mode/interfaces-pppoe.py @@ -24,6 +24,7 @@ from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configverify import verify_source_interface from vyos.configverify import verify_vrf +from vyos.configverify import verify_mtu_ipv6 from vyos.template import render from vyos.util import call from vyos import ConfigError @@ -57,6 +58,7 @@ def verify(pppoe): verify_source_interface(pppoe) verify_vrf(pppoe) + verify_mtu_ipv6(pppoe) if {'connect_on_demand', 'vrf'} <= set(pppoe): raise ConfigError('On-demand dialing and VRF can not be used at the same time') diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py index 850ea28d7..002f40aef 100755 --- a/src/conf_mode/interfaces-vxlan.py +++ b/src/conf_mode/interfaces-vxlan.py @@ -23,6 +23,7 @@ from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete +from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_source_interface from vyos.ifconfig import VXLANIf, Interface from vyos import ConfigError @@ -77,6 +78,7 @@ def verify(vxlan): raise ConfigError('VXLAN has a 50 byte overhead, underlaying device ' \ f'MTU is to small ({underlay_mtu} bytes)') + verify_mtu_ipv6(vxlan) verify_address(vxlan) return None diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py index e7c22da1a..d5800264f 100755 --- a/src/conf_mode/interfaces-wireguard.py +++ b/src/conf_mode/interfaces-wireguard.py @@ -27,6 +27,7 @@ from vyos.configdict import leaf_node_changed from vyos.configverify import verify_vrf from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete +from vyos.configverify import verify_mtu_ipv6 from vyos.ifconfig import WireGuardIf from vyos.util import check_kmod from vyos import ConfigError @@ -71,6 +72,7 @@ def verify(wireguard): verify_bridge_delete(wireguard) return None + verify_mtu_ipv6(wireguard) verify_address(wireguard) verify_vrf(wireguard) -- cgit v1.2.3