Page Menu
Home
VyOS Platform
Search
Configure Global Search
Log In
Files
F119063599
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in
index 94d78c6dd..3f2e5bb69 100644
--- a/interface-definitions/interfaces-macsec.xml.in
+++ b/interface-definitions/interfaces-macsec.xml.in
@@ -1,122 +1,126 @@
<?xml version="1.0"?>
<interfaceDefinition>
<node name="interfaces">
<children>
<tagNode name="macsec" owner="${vyos_conf_scripts_dir}/interfaces-macsec.py">
<properties>
<help>MACsec Interface (802.1ae)</help>
<priority>461</priority>
<constraint>
<regex>^macsec[0-9]+$</regex>
</constraint>
<constraintErrorMessage>MACsec interface must be named macsecN</constraintErrorMessage>
<valueHelp>
<format>macsecN</format>
<description>MACsec interface name</description>
</valueHelp>
</properties>
<children>
#include <include/address-ipv4-ipv6.xml.i>
#include <include/interface-ipv4-options.xml.i>
#include <include/interface-ipv6-options.xml.i>
<node name="security">
<properties>
<help>Security/Encryption Settings</help>
</properties>
<children>
<leafNode name="cipher">
<properties>
<help>Cipher suite used</help>
<completionHelp>
- <list>gcm-aes-128</list>
+ <list>gcm-aes-128 gcm-aes-256</list>
</completionHelp>
<valueHelp>
<format>gcm-aes-128</format>
<description>Galois/Counter Mode of AES cipher with 128-bit key (default)</description>
</valueHelp>
+ <valueHelp>
+ <format>gcm-aes-256</format>
+ <description>Galois/Counter Mode of AES cipher with 256-bit key</description>
+ </valueHelp>
<constraint>
- <regex>(gcm-aes-128)</regex>
+ <regex>^(gcm-aes-128|gcm-aes-256)$</regex>
</constraint>
</properties>
</leafNode>
<leafNode name="encrypt">
<properties>
<help>Enable optional MACsec encryption</help>
<valueless/>
</properties>
</leafNode>
<node name="mka">
<properties>
<help>MACsec Key Agreement protocol (MKA)</help>
</properties>
<children>
<leafNode name="cak">
<properties>
<help>Secure Connectivity Association Key</help>
<valueHelp>
<format>key</format>
<description>16-byte (128-bit) hex-string (32 hex-digits)</description>
</valueHelp>
<constraint>
<regex>^[A-Fa-f0-9]{32}$</regex>
</constraint>
</properties>
</leafNode>
<leafNode name="ckn">
<properties>
<help>Secure Connectivity Association Key Name</help>
<valueHelp>
<format>key</format>
<description>32-byte (256-bit) hex-string (64 hex-digits)</description>
</valueHelp>
<constraint>
<regex>^[A-Fa-f0-9]{64}$</regex>
</constraint>
</properties>
</leafNode>
<leafNode name="priority">
<properties>
<help>Priority of MACsec Key Agreement protocol (MKA) actor (default: 255)</help>
<valueHelp>
<format>0-255</format>
<description>MACsec Key Agreement protocol (MKA) priority</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-255" />
</constraint>
</properties>
<defaultValue>255</defaultValue>
</leafNode>
</children>
</node>
<leafNode name="replay-window">
<properties>
<help>IEEE 802.1X/MACsec replay protection window</help>
<valueHelp>
<format>0</format>
<description>No replay window, strict check</description>
</valueHelp>
<valueHelp>
<format>1-4294967295</format>
<description>Number of packets that could be misordered</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-4294967295" />
</constraint>
</properties>
</leafNode>
</children>
</node>
#include <include/interface-description.xml.i>
#include <include/interface-disable.xml.i>
#include <include/interface-mtu-68-16000.xml.i>
<leafNode name="mtu">
<defaultValue>1460</defaultValue>
</leafNode>
#include <include/source-interface-ethernet.xml.i>
#include <include/interface-vrf.xml.i>
</children>
</tagNode>
</children>
</node>
</interfaceDefinition>
diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py
index 3a3e7bff3..d6bef993a 100755
--- a/smoketest/scripts/cli/test_interfaces_macsec.py
+++ b/smoketest/scripts/cli/test_interfaces_macsec.py
@@ -1,158 +1,193 @@
#!/usr/bin/env python3
#
# Copyright (C) 2020-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
import re
import unittest
from base_interfaces_test import BasicInterfaceTest
from netifaces import interfaces
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
+from vyos.util import cmd
from vyos.util import read_file
from vyos.util import process_named_running
def get_config_value(interface, key):
tmp = read_file(f'/run/wpa_supplicant/{interface}.conf')
tmp = re.findall(r'\n?{}=(.*)'.format(key), tmp)
return tmp[0]
+def get_cipher(interface):
+ """ Returns the used encapsulation protocol for given interface.
+ If interface does not exist, None is returned.
+ """
+ if not os.path.exists(f'/sys/class/net/{interface}'):
+ return None
+ from json import loads
+ tmp = loads(cmd(f'ip -d -j link show {interface}'))[0]
+ return tmp['linkinfo']['info_data']['cipher_suite'].lower()
+
class MACsecInterfaceTest(BasicInterfaceTest.BaseTest):
@classmethod
def setUpClass(cls):
cls._test_ip = True
cls._test_ipv6 = True
cls._base_path = ['interfaces', 'macsec']
cls._options = { 'macsec0': ['source-interface eth0', 'security cipher gcm-aes-128'] }
# if we have a physical eth1 interface, add a second macsec instance
if 'eth1' in Section.interfaces('ethernet'):
macsec = { 'macsec1': [f'source-interface eth1', 'security cipher gcm-aes-128'] }
cls._options.update(macsec)
cls._interfaces = list(cls._options)
def test_macsec_encryption(self):
# MACsec can be operating in authentication and encryption mode - both
# using different mandatory settings, lets test encryption as the basic
# authentication test has been performed using the base class tests
mak_cak = '232e44b7fda6f8e2d88a07bf78a7aff4'
mak_ckn = '40916f4b23e3d548ad27eedd2d10c6f98c2d21684699647d63d41b500dfe8836'
replay_window = '64'
for interface, option_value in self._options.items():
for option in option_value:
if option.split()[0] == 'source-interface':
src_interface = option.split()[1]
self.session.set(self._base_path + [interface] + option.split())
# Encrypt link
self.session.set(self._base_path + [interface, 'security', 'encrypt'])
# check validate() - Physical source interface MTU must be higher then our MTU
self.session.set(self._base_path + [interface, 'mtu', '1500'])
with self.assertRaises(ConfigSessionError):
self.session.commit()
self.session.delete(self._base_path + [interface, 'mtu'])
# check validate() - MACsec security keys mandartory when encryption is enabled
with self.assertRaises(ConfigSessionError):
self.session.commit()
self.session.set(self._base_path + [interface, 'security', 'mka', 'cak', mak_cak])
# check validate() - MACsec security keys mandartory when encryption is enabled
with self.assertRaises(ConfigSessionError):
self.session.commit()
self.session.set(self._base_path + [interface, 'security', 'mka', 'ckn', mak_ckn])
self.session.set(self._base_path + [interface, 'security', 'replay-window', replay_window])
# final commit of settings
self.session.commit()
tmp = get_config_value(src_interface, 'macsec_integ_only')
self.assertTrue("0" in tmp)
tmp = get_config_value(src_interface, 'mka_cak')
self.assertTrue(mak_cak in tmp)
tmp = get_config_value(src_interface, 'mka_ckn')
self.assertTrue(mak_ckn in tmp)
# check that the default priority of 255 is programmed
tmp = get_config_value(src_interface, 'mka_priority')
self.assertTrue("255" in tmp)
tmp = get_config_value(src_interface, 'macsec_replay_window')
self.assertTrue(replay_window in tmp)
tmp = read_file(f'/sys/class/net/{interface}/mtu')
self.assertEqual(tmp, '1460')
# Check for running process
self.assertTrue(process_named_running('wpa_supplicant'))
- def test_macsec_mandatory_options(self):
+ def test_macsec_gcm_aes_128(self):
interface = 'macsec1'
+ cipher = 'gcm-aes-128'
+ self.session.set(self._base_path + [interface])
+
+ # check validate() - source interface is mandatory
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'source-interface', 'eth0'])
+
+ # check validate() - cipher is mandatory
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'security', 'cipher', cipher])
+
+ # final commit and verify
+ self.session.commit()
+ self.assertIn(interface, interfaces())
+ self.assertIn(interface, interfaces())
+ self.assertEqual(cipher, get_cipher(interface))
+
+ def test_macsec_gcm_aes_256(self):
+ interface = 'macsec4'
+ cipher = 'gcm-aes-256'
self.session.set(self._base_path + [interface])
# check validate() - source interface is mandatory
with self.assertRaises(ConfigSessionError):
self.session.commit()
self.session.set(self._base_path + [interface, 'source-interface', 'eth0'])
# check validate() - cipher is mandatory
with self.assertRaises(ConfigSessionError):
self.session.commit()
- self.session.set(self._base_path + [interface, 'security', 'cipher', 'gcm-aes-128'])
+ self.session.set(self._base_path + [interface, 'security', 'cipher', cipher])
# final commit and verify
self.session.commit()
self.assertIn(interface, interfaces())
+ self.assertEqual(cipher, get_cipher(interface))
def test_macsec_source_interface(self):
# Ensure source-interface can bot be part of any other bond or bridge
base_bridge = ['interfaces', 'bridge', 'br200']
base_bond = ['interfaces', 'bonding', 'bond200']
for interface, option_value in self._options.items():
for option in option_value:
self.session.set(self._base_path + [interface] + option.split())
if option.split()[0] == 'source-interface':
src_interface = option.split()[1]
self.session.set(base_bridge + ['member', 'interface', src_interface])
# check validate() - Source interface must not already be a member of a bridge
with self.assertRaises(ConfigSessionError):
self.session.commit()
self.session.delete(base_bridge)
self.session.set(base_bond + ['member', 'interface', src_interface])
# check validate() - Source interface must not already be a member of a bridge
with self.assertRaises(ConfigSessionError):
self.session.commit()
self.session.delete(base_bond)
# final commit and verify
self.session.commit()
self.assertIn(interface, interfaces())
if __name__ == '__main__':
unittest.main(verbosity=2)
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Nov 6, 8:53 AM (1 d, 8 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4641695
Default Alt Text
(13 KB)
Attached To
Mode
rVYOSONEX vyos-1x
Attached
Detach File
Event Timeline
Log In to Comment