From e0bd74e399cc2693b6d442af52c9345e279db059 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 27 Jun 2020 12:54:13 +0200 Subject: ifconfig: T2653: move macsec interface to get_config_dict() --- src/conf_mode/interfaces-macsec.py | 209 +++++++++++-------------------------- 1 file changed, 62 insertions(+), 147 deletions(-) (limited to 'src/conf_mode') diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index a8966148f..ca5c32eeb 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -18,177 +18,111 @@ import os from copy import deepcopy from sys import exit -from netifaces import interfaces from vyos.config import Config -from vyos.configdict import list_diff +from vyos.configdict import dict_merge from vyos.ifconfig import MACsecIf from vyos.template import render from vyos.util import call from vyos.validate import is_member +from vyos.configverify import verify_bridge_vrf +from vyos.configverify import verify_bridge_address +from vyos.configverify import verify_bridge_delete +from vyos.configverify import verify_source_interface +from vyos.xml import defaults from vyos import ConfigError - from vyos import airbag airbag.enable() -default_config_data = { - 'address': [], - 'address_remove': [], - 'deleted': False, - 'description': '', - 'disable': False, - 'security_cipher': '', - 'security_encrypt': False, - 'security_mka_cak': '', - 'security_mka_ckn': '', - 'security_mka_priority': '255', - 'security_replay_window': '', - 'intf': '', - 'source_interface': '', - 'is_bridge_member': False, - 'vrf': '' -} +# XXX: workaround for https://phabricator.vyos.net/T2656 +default = {'security' : {'mka' : {'priority' : '255'}}} # XXX: wpa_supplicant works on the source interface wpa_suppl_conf = '/run/wpa_supplicant/{source_interface}.conf' - def get_config(): - macsec = deepcopy(default_config_data) + """ Retrive CLI config as dictionary. Dictionary can never be empty, + as at least the interface name will be added or a deleted flag """ conf = Config() # determine tagNode instance if 'VYOS_TAGNODE_VALUE' not in os.environ: raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified') - macsec['intf'] = os.environ['VYOS_TAGNODE_VALUE'] - base_path = ['interfaces', 'macsec', macsec['intf']] + # retrieve interface default values + base = ['interfaces', 'macsec'] + #default_values = defaults(base) - # check if we are a member of any bridge - macsec['is_bridge_member'] = is_member(conf, macsec['intf'], 'bridge') + ifname = os.environ['VYOS_TAGNODE_VALUE'] + base = base + [ifname] + macsec = conf.get_config_dict(base, key_mangling=('-', '_')) # Check if interface has been removed - if not conf.exists(base_path): - macsec['deleted'] = True - # When stopping wpa_supplicant we need to stop it via the physical - # interface - thus we need to retrieve ir from the effective config - if conf.exists_effective(base_path + ['source-interface']): - macsec['source_interface'] = conf.return_effective_value( - base_path + ['source-interface']) - - return macsec - - # set new configuration level - conf.set_level(base_path) - - # retrieve configured interface addresses - if conf.exists(['address']): - macsec['address'] = conf.return_values(['address']) - - # retrieve interface description - if conf.exists(['description']): - macsec['description'] = conf.return_value(['description']) - - # Disable this interface - if conf.exists(['disable']): - macsec['disable'] = True - - # retrieve interface cipher - if conf.exists(['security', 'cipher']): - macsec['security_cipher'] = conf.return_value(['security', 'cipher']) - - # Enable optional MACsec encryption - if conf.exists(['security', 'encrypt']): - macsec['security_encrypt'] = True - - # Secure Connectivity Association Key - if conf.exists(['security', 'mka', 'cak']): - macsec['security_mka_cak'] = conf.return_value( - ['security', 'mka', 'cak']) - - # Secure Connectivity Association Name - if conf.exists(['security', 'mka', 'ckn']): - macsec['security_mka_ckn'] = conf.return_value( - ['security', 'mka', 'ckn']) - - # MACsec Key Agreement protocol (MKA) actor priority - if conf.exists(['security', 'mka', 'priority']): - macsec['security_mka_priority'] = conf.return_value( - ['security', 'mka', 'priority']) - - # IEEE 802.1X/MACsec replay protection - if conf.exists(['security', 'replay-window']): - macsec['security_replay_window'] = conf.return_value( - ['security', 'replay-window']) - - # Physical interface - if conf.exists(['source-interface']): - macsec['source_interface'] = conf.return_value(['source-interface']) - - # Determine interface addresses (currently effective) - to determine which - # address is no longer valid and needs to be removed from the interface - eff_addr = conf.return_effective_values(['address']) - act_addr = conf.return_values(['address']) - macsec['address_remove'] = list_diff(eff_addr, act_addr) - - # retrieve VRF instance - if conf.exists(['vrf']): - macsec['vrf'] = conf.return_value(['vrf']) + if macsec == {}: + tmp = { + 'deleted' : '', + 'source_interface' : conf.return_effective_value( + base + ['source-interface']) + } + macsec.update(tmp) + + # We have gathered the dict representation of the CLI, but there are + # default options which we need to update into the dictionary + # retrived. + macsec = dict_merge(default, macsec) + + # Add interface instance name into dictionary + macsec.update({'ifname': ifname}) + + # Check if we are a member of any bridge + bridge = is_member(conf, ifname, 'bridge') + if bridge: + tmp = {'is_bridge_member' : bridge} + macsec.update(tmp) return macsec def verify(macsec): - if macsec['deleted']: - if macsec['is_bridge_member']: - raise ConfigError( - 'Interface "{intf}" cannot be deleted as it is a ' - 'member of bridge "{is_bridge_member}"!'.format(**macsec)) - + if 'deleted' in macsec.keys(): + verify_bridge_delete(macsec) return None - if not macsec['source_interface']: - raise ConfigError('Physical source interface must be set for ' - 'MACsec "{intf}"'.format(**macsec)) + verify_source_interface(macsec) + verify_bridge_vrf(macsec) + verify_bridge_address(macsec) - if not macsec['security_cipher']: + if not (('security' in macsec.keys()) and + ('cipher' in macsec['security'].keys())): raise ConfigError( - 'Cipher suite must be set for MACsec "{intf}"'.format(**macsec)) + 'Cipher suite must be set for MACsec "{ifname}"'.format(**macsec)) - if macsec['security_encrypt']: - if not (macsec['security_mka_cak'] and macsec['security_mka_ckn']): - raise ConfigError( - 'MACsec security keys mandartory when encryption is enabled') + if (('security' in macsec.keys()) and + ('encrypt' in macsec['security'].keys())): + tmp = macsec.get('security') - if macsec['vrf']: - if macsec['vrf'] not in interfaces(): - raise ConfigError('VRF "{vrf}" does not exist'.format(**macsec)) - - if macsec['is_bridge_member']: - raise ConfigError('Interface "{intf}" cannot be member of VRF ' - '"{vrf}" and bridge "{is_bridge_member}" at ' - 'the same time!'.format(**macsec)) - - if macsec['is_bridge_member'] and macsec['address']: - raise ConfigError( - 'Cannot assign address to interface "{intf}" as it is' - 'a member of bridge "{is_bridge_member}"!'.format(**macsec)) + if not (('mka' in tmp.keys()) and + ('cak' in tmp['mka'].keys()) and + ('ckn' in tmp['mka'].keys())): + raise ConfigError('Missing mandatory MACsec security ' + 'keys as encryption is enabled!') return None def generate(macsec): render(wpa_suppl_conf.format(**macsec), - 'macsec/wpa_supplicant.conf.tmpl', macsec, permission=0o640) + 'macsec/wpa_supplicant.conf.tmpl', macsec) return None def apply(macsec): # Remove macsec interface - if macsec['deleted']: + if 'deleted' in macsec.keys(): call('systemctl stop wpa_supplicant-macsec@{source_interface}' .format(**macsec)) - MACsecIf(macsec['intf']).remove() + + MACsecIf(macsec['ifname']).remove() # delete configuration on interface removal if os.path.isfile(wpa_suppl_conf.format(**macsec)): @@ -198,35 +132,16 @@ def apply(macsec): # MACsec interfaces require a configuration when they are added using # iproute2. This static method will provide the configuration # dictionary used by this class. - conf = deepcopy(MACsecIf.get_config()) - # Assign MACsec instance configuration parameters to config dict + # XXX: subject of removal after completing T2653 + conf = deepcopy(MACsecIf.get_config()) conf['source_interface'] = macsec['source_interface'] - conf['security_cipher'] = macsec['security_cipher'] + conf['security_cipher'] = macsec['security']['cipher'] # It is safe to "re-create" the interface always, there is a sanity # check that the interface will only be create if its non existent - i = MACsecIf(macsec['intf'], **conf) - - # update interface description used e.g. within SNMP - i.set_alias(macsec['description']) - - # Configure interface address(es) - # - not longer required addresses get removed first - # - newly addresses will be added second - for addr in macsec['address_remove']: - i.del_addr(addr) - for addr in macsec['address']: - i.add_addr(addr) - - # assign/remove VRF (ONLY when not a member of a bridge, - # otherwise 'nomaster' removes it from it) - if not macsec['is_bridge_member']: - i.set_vrf(macsec['vrf']) - - # Interface is administratively down by default, enable if desired - if not macsec['disable']: - i.set_admin_state('up') + i = MACsecIf(macsec['ifname'], **conf) + i.update(macsec) call('systemctl restart wpa_supplicant-macsec@{source_interface}' .format(**macsec)) -- cgit v1.2.3 From adfb4c81a41b9eec4b33a27a8d8db7184dbda6da Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 27 Jun 2020 12:56:18 +0200 Subject: ifconfig: T2653: dummy: loopback: use same get_config() structure as MACsec --- src/conf_mode/interfaces-dummy.py | 12 ++++++------ src/conf_mode/interfaces-loopback.py | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'src/conf_mode') diff --git a/src/conf_mode/interfaces-dummy.py b/src/conf_mode/interfaces-dummy.py index 676211428..a8093ffa5 100755 --- a/src/conf_mode/interfaces-dummy.py +++ b/src/conf_mode/interfaces-dummy.py @@ -41,6 +41,10 @@ def get_config(): base = ['interfaces', 'dummy', ifname] dummy = conf.get_config_dict(base, key_mangling=('-', '_')) + # Check if interface has been removed + if dummy == {}: + dummy.update({'deleted' : ''}) + # store interface instance name in dictionary dummy.update({'ifname': ifname}) @@ -50,14 +54,10 @@ def get_config(): tmp = {'is_bridge_member' : bridge} dummy.update(tmp) - # Check if interface has been removed - tmp = {'deleted' : not conf.exists(base)} - dummy.update(tmp) - return dummy def verify(dummy): - if dummy['deleted']: + if 'deleted' in dummy.keys(): verify_bridge_delete(dummy) return None @@ -73,7 +73,7 @@ def apply(dummy): d = DummyIf(dummy['ifname']) # Remove dummy interface - if dummy['deleted']: + if 'deleted' in dummy.keys(): d.remove() else: d.update(dummy) diff --git a/src/conf_mode/interfaces-loopback.py b/src/conf_mode/interfaces-loopback.py index 32e683c07..7c3d8663d 100755 --- a/src/conf_mode/interfaces-loopback.py +++ b/src/conf_mode/interfaces-loopback.py @@ -36,13 +36,13 @@ def get_config(): base = ['interfaces', 'loopback', ifname] loopback = conf.get_config_dict(base, key_mangling=('-', '_')) + # Check if interface has been removed + if loopback == {}: + loopback.update({'deleted' : ''}) + # store interface instance name in dictionary loopback.update({'ifname': ifname}) - # Check if interface has been removed - tmp = {'deleted' : not conf.exists(base)} - loopback.update(tmp) - return loopback def verify(loopback): @@ -53,7 +53,7 @@ def generate(loopback): def apply(loopback): l = LoopbackIf(loopback['ifname']) - if loopback['deleted']: + if 'deleted' in loopback.keys(): l.remove() else: l.update(loopback) -- cgit v1.2.3