[docker-macsec]: MACsec CLI Plugin (#9390)
#### Why I did it To provide MACsec config and show CLI for manipulating MACsec #### How I did it Add `config macsec` and `show macsec`. #### How to verify it This PR includes unittest for MACsec CLI, check Azp status. - Add MACsec profile ``` admin@sonic:~$ sudo config macsec profile add --help Usage: config macsec profile add [OPTIONS] <profile_name> Add MACsec profile Options: --priority <priority> For Key server election. In 0-255 range with 0 being the highest priority. [default: 255] --cipher_suite <cipher_suite> The cipher suite for MACsec. [default: GCM- AES-128] --primary_cak <primary_cak> Primary Connectivity Association Key. [required] --primary_ckn <primary_cak> Primary CAK Name. [required] --policy <policy> MACsec policy. INTEGRITY_ONLY: All traffic, except EAPOL, will be converted to MACsec packets without encryption. SECURITY: All traffic, except EAPOL, will be encrypted by SecY. [default: security] --enable_replay_protect / --disable_replay_protect Whether enable replay protect. [default: False] --replay_window <enable_replay_protect> Replay window size that is the number of packets that could be out of order. This field works only if ENABLE_REPLAY_PROTECT is true. [default: 0] --send_sci / --no_send_sci Send SCI in SecTAG field of MACsec header. [default: True] --rekey_period <rekey_period> The period of proactively refresh (Unit second). [default: 0] -?, -h, --help Show this message and exit. ``` - Delete MACsec profile ``` admin@sonic:~$ sudo config macsec profile del --help Usage: config macsec profile del [OPTIONS] <profile_name> Delete MACsec profile Options: -?, -h, --help Show this message and exit. ``` - Enable MACsec on the port ``` admin@sonic:~$ sudo config macsec port add --help Usage: config macsec port add [OPTIONS] <port_name> <profile_name> Add MACsec port Options: -?, -h, --help Show this message and exit. ``` - Disable MACsec on the port ``` admin@sonic:~$ sudo config macsec port del --help Usage: config macsec port del [OPTIONS] <port_name> Delete MACsec port Options: -?, -h, --help Show this message and exit. ``` Show MACsec ``` MACsec port(Ethernet0) --------------------- ----------- cipher_suite GCM-AES-256 enable true enable_encrypt true enable_protect true enable_replay_protect false replay_window 0 send_sci true --------------------- ----------- MACsec Egress SC (5254008f4f1c0001) ----------- - encoding_an 2 ----------- - MACsec Egress SA (1) ------------------------------------- ---------------------------------------------------------------- auth_key 849B69D363E2B0AA154BEBBD7C1D9487 next_pn 1 sak AE8C9BB36EA44B60375E84BC8E778596289E79240FDFA6D7BA33D3518E705A5E salt 000000000000000000000000 ssci 0 SAI_MACSEC_SA_ATTR_CURRENT_XPN 179 SAI_MACSEC_SA_STAT_OCTETS_ENCRYPTED 0 SAI_MACSEC_SA_STAT_OCTETS_PROTECTED 0 SAI_MACSEC_SA_STAT_OUT_PKTS_ENCRYPTED 0 SAI_MACSEC_SA_STAT_OUT_PKTS_PROTECTED 0 ------------------------------------- ---------------------------------------------------------------- MACsec Egress SA (2) ------------------------------------- ---------------------------------------------------------------- auth_key 5A8B8912139551D3678B43DD0F10FFA5 next_pn 1 sak 7F2651140F12C434F782EF9AD7791EE2CFE2BF315A568A48785E35FC803C9DB6 salt 000000000000000000000000 ssci 0 SAI_MACSEC_SA_ATTR_CURRENT_XPN 87185 SAI_MACSEC_SA_STAT_OCTETS_ENCRYPTED 0 SAI_MACSEC_SA_STAT_OCTETS_PROTECTED 0 SAI_MACSEC_SA_STAT_OUT_PKTS_ENCRYPTED 0 SAI_MACSEC_SA_STAT_OUT_PKTS_PROTECTED 0 ------------------------------------- ---------------------------------------------------------------- MACsec Ingress SC (525400edac5b0001) MACsec Ingress SA (1) --------------------------------------- ---------------------------------------------------------------- active true auth_key 849B69D363E2B0AA154BEBBD7C1D9487 lowest_acceptable_pn 1 sak AE8C9BB36EA44B60375E84BC8E778596289E79240FDFA6D7BA33D3518E705A5E salt 000000000000000000000000 ssci 0 SAI_MACSEC_SA_ATTR_CURRENT_XPN 103 SAI_MACSEC_SA_STAT_IN_PKTS_DELAYED 0 SAI_MACSEC_SA_STAT_IN_PKTS_INVALID 0 SAI_MACSEC_SA_STAT_IN_PKTS_LATE 0 SAI_MACSEC_SA_STAT_IN_PKTS_NOT_USING_SA 0 SAI_MACSEC_SA_STAT_IN_PKTS_NOT_VALID 0 SAI_MACSEC_SA_STAT_IN_PKTS_OK 0 SAI_MACSEC_SA_STAT_IN_PKTS_UNCHECKED 0 SAI_MACSEC_SA_STAT_IN_PKTS_UNUSED_SA 0 SAI_MACSEC_SA_STAT_OCTETS_ENCRYPTED 0 SAI_MACSEC_SA_STAT_OCTETS_PROTECTED 0 --------------------------------------- ---------------------------------------------------------------- MACsec Ingress SA (2) --------------------------------------- ---------------------------------------------------------------- active true auth_key 5A8B8912139551D3678B43DD0F10FFA5 lowest_acceptable_pn 1 sak 7F2651140F12C434F782EF9AD7791EE2CFE2BF315A568A48785E35FC803C9DB6 salt 000000000000000000000000 ssci 0 SAI_MACSEC_SA_ATTR_CURRENT_XPN 91824 SAI_MACSEC_SA_STAT_IN_PKTS_DELAYED 0 SAI_MACSEC_SA_STAT_IN_PKTS_INVALID 0 SAI_MACSEC_SA_STAT_IN_PKTS_LATE 0 SAI_MACSEC_SA_STAT_IN_PKTS_NOT_USING_SA 0 SAI_MACSEC_SA_STAT_IN_PKTS_NOT_VALID 0 SAI_MACSEC_SA_STAT_IN_PKTS_OK 0 SAI_MACSEC_SA_STAT_IN_PKTS_UNCHECKED 0 SAI_MACSEC_SA_STAT_IN_PKTS_UNUSED_SA 0 SAI_MACSEC_SA_STAT_OCTETS_ENCRYPTED 0 SAI_MACSEC_SA_STAT_OCTETS_PROTECTED 0 --------------------------------------- ---------------------------------------------------------------- MACsec port(Ethernet1) --------------------- ----------- cipher_suite GCM-AES-256 enable true enable_encrypt true enable_protect true enable_replay_protect false replay_window 0 send_sci true --------------------- ----------- MACsec Egress SC (5254008f4f1c0001) ----------- - encoding_an 1 ----------- - MACsec Egress SA (1) ------------------------------------- ---------------------------------------------------------------- auth_key 35FC8F2C81BCA28A95845A4D2A1EE6EF next_pn 1 sak 1EC8572B75A840BA6B3833DC550C620D2C65BBDDAD372D27A1DFEB0CD786671B salt 000000000000000000000000 ssci 0 SAI_MACSEC_SA_ATTR_CURRENT_XPN 4809 SAI_MACSEC_SA_STAT_OCTETS_ENCRYPTED 0 SAI_MACSEC_SA_STAT_OCTETS_PROTECTED 0 SAI_MACSEC_SA_STAT_OUT_PKTS_ENCRYPTED 0 SAI_MACSEC_SA_STAT_OUT_PKTS_PROTECTED 0 ------------------------------------- ---------------------------------------------------------------- MACsec Ingress SC (525400edac5b0001) MACsec Ingress SA (1) --------------------------------------- ---------------------------------------------------------------- active true auth_key 35FC8F2C81BCA28A95845A4D2A1EE6EF lowest_acceptable_pn 1 sak 1EC8572B75A840BA6B3833DC550C620D2C65BBDDAD372D27A1DFEB0CD786671B salt 000000000000000000000000 ssci 0 SAI_MACSEC_SA_ATTR_CURRENT_XPN 5033 SAI_MACSEC_SA_STAT_IN_PKTS_DELAYED 0 SAI_MACSEC_SA_STAT_IN_PKTS_INVALID 0 SAI_MACSEC_SA_STAT_IN_PKTS_LATE 0 SAI_MACSEC_SA_STAT_IN_PKTS_NOT_USING_SA 0 SAI_MACSEC_SA_STAT_IN_PKTS_NOT_VALID 0 SAI_MACSEC_SA_STAT_IN_PKTS_OK 0 SAI_MACSEC_SA_STAT_IN_PKTS_UNCHECKED 0 SAI_MACSEC_SA_STAT_IN_PKTS_UNUSED_SA 0 SAI_MACSEC_SA_STAT_OCTETS_ENCRYPTED 0 SAI_MACSEC_SA_STAT_OCTETS_PROTECTED 0 --------------------------------------- ---------------------------------------------------------------- ```
This commit is contained in:
Родитель
0cc9fdc69b
Коммит
910e1c6eb4
|
@ -354,6 +354,7 @@ SONIC_BUILD_INSTRUCTION := make \
|
|||
SONIC_CONFIG_USE_NATIVE_DOCKERD_FOR_BUILD=$(SONIC_CONFIG_USE_NATIVE_DOCKERD_FOR_BUILD) \
|
||||
SONIC_INCLUDE_SYSTEM_TELEMETRY=$(INCLUDE_SYSTEM_TELEMETRY) \
|
||||
INCLUDE_DHCP_RELAY=$(INCLUDE_DHCP_RELAY) \
|
||||
INCLUDE_MACSEC=$(INCLUDE_MACSEC) \
|
||||
SONIC_INCLUDE_RESTAPI=$(INCLUDE_RESTAPI) \
|
||||
SONIC_INCLUDE_MUX=$(INCLUDE_MUX) \
|
||||
TELEMETRY_WRITABLE=$(TELEMETRY_WRITABLE) \
|
||||
|
|
|
@ -27,5 +27,6 @@ COPY ["supervisord.conf", "/etc/supervisor/conf.d/"]
|
|||
COPY ["files/supervisor-proc-exit-listener", "/usr/bin"]
|
||||
COPY ["critical_processes", "/etc/supervisor"]
|
||||
COPY ["etc/wpa_supplicant.conf", "/etc/wpa_supplicant.conf"]
|
||||
COPY ["cli", "/cli/"]
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/supervisord"]
|
||||
|
|
|
@ -0,0 +1,247 @@
|
|||
{
|
||||
"MACSEC_EGRESS_SA_TABLE:Ethernet1:5254008f4f1c0001:1": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"sak": "1EC8572B75A840BA6B3833DC550C620D2C65BBDDAD372D27A1DFEB0CD786671B",
|
||||
"auth_key": "35FC8F2C81BCA28A95845A4D2A1EE6EF",
|
||||
"next_pn": "1",
|
||||
"ssci": "0",
|
||||
"salt": "000000000000000000000000"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2301455
|
||||
},
|
||||
"MACSEC_PORT_TABLE:Ethernet5": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"enable": "true",
|
||||
"cipher_suite": "GCM-AES-256",
|
||||
"send_sci": "true",
|
||||
"enable_protect": "true",
|
||||
"enable_encrypt": "true",
|
||||
"enable_replay_protect": "false",
|
||||
"replay_window": "0"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2302043
|
||||
},
|
||||
"MACSEC_EGRESS_SC_TABLE:Ethernet1:5254008f4f1c0001": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"encoding_an": "1"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2302194
|
||||
},
|
||||
"MACSEC_INGRESS_SA_TABLE:Ethernet1:525400edac5b0001:1": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"active": "true",
|
||||
"sak": "1EC8572B75A840BA6B3833DC550C620D2C65BBDDAD372D27A1DFEB0CD786671B",
|
||||
"auth_key": "35FC8F2C81BCA28A95845A4D2A1EE6EF",
|
||||
"lowest_acceptable_pn": "1",
|
||||
"ssci": "0",
|
||||
"salt": "000000000000000000000000"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2302353
|
||||
},
|
||||
"MACSEC_INGRESS_SC_TABLE:Ethernet1:525400edac5b0001": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"Null": "Null"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2302475
|
||||
},
|
||||
"MACSEC_INGRESS_SC_TABLE:Ethernet0:525400edac5b0001": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"Null": "Null"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.230258
|
||||
},
|
||||
"MACSEC_EGRESS_SA_TABLE:Ethernet5:5254008f4f1c0001:2": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"sak": "3BEBB5BB2539D7231EB95F312B843966180B6C941750B9F1A08AF71BA4508599",
|
||||
"auth_key": "7C59E0CD393A3BA36B8DDC4C663A11FC",
|
||||
"next_pn": "1",
|
||||
"ssci": "0",
|
||||
"salt": "000000000000000000000000"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2302718
|
||||
},
|
||||
"MACSEC_INGRESS_SA_TABLE:Ethernet0:525400edac5b0001:2": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"active": "true",
|
||||
"sak": "7F2651140F12C434F782EF9AD7791EE2CFE2BF315A568A48785E35FC803C9DB6",
|
||||
"auth_key": "5A8B8912139551D3678B43DD0F10FFA5",
|
||||
"lowest_acceptable_pn": "1",
|
||||
"ssci": "0",
|
||||
"salt": "000000000000000000000000"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.230298
|
||||
},
|
||||
"MACSEC_EGRESS_SC_TABLE:Ethernet5:5254008f4f1c0001": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"encoding_an": "2"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2303102
|
||||
},
|
||||
"MACSEC_PORT_TABLE:Ethernet0": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"enable": "true",
|
||||
"cipher_suite": "GCM-AES-256",
|
||||
"send_sci": "true",
|
||||
"enable_protect": "true",
|
||||
"enable_encrypt": "true",
|
||||
"enable_replay_protect": "false",
|
||||
"replay_window": "0"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.23036
|
||||
},
|
||||
"MACSEC_INGRESS_SA_TABLE:Ethernet5:5254002003660001:2": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"active": "true",
|
||||
"sak": "3BEBB5BB2539D7231EB95F312B843966180B6C941750B9F1A08AF71BA4508599",
|
||||
"auth_key": "7C59E0CD393A3BA36B8DDC4C663A11FC",
|
||||
"lowest_acceptable_pn": "1",
|
||||
"ssci": "0",
|
||||
"salt": "000000000000000000000000"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2304032
|
||||
},
|
||||
"MACSEC_PORT_TABLE:Ethernet4": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"enable": "true",
|
||||
"cipher_suite": "GCM-AES-256",
|
||||
"send_sci": "true",
|
||||
"enable_protect": "true",
|
||||
"enable_encrypt": "true",
|
||||
"enable_replay_protect": "false",
|
||||
"replay_window": "0"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2304454
|
||||
},
|
||||
"MACSEC_EGRESS_SA_TABLE:Ethernet4:5254008f4f1c0001:1": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"sak": "234128B1F6A679E02759D521C1FF448D5CE47B2E691852281EE8E34690B348DD",
|
||||
"auth_key": "575FC253C395DFC3E1EE42C3DB665913",
|
||||
"next_pn": "1",
|
||||
"ssci": "0",
|
||||
"salt": "000000000000000000000000"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2304764
|
||||
},
|
||||
"MACSEC_INGRESS_SA_TABLE:Ethernet0:525400edac5b0001:1": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"active": "true",
|
||||
"sak": "AE8C9BB36EA44B60375E84BC8E778596289E79240FDFA6D7BA33D3518E705A5E",
|
||||
"auth_key": "849B69D363E2B0AA154BEBBD7C1D9487",
|
||||
"lowest_acceptable_pn": "1",
|
||||
"ssci": "0",
|
||||
"salt": "000000000000000000000000"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.230506
|
||||
},
|
||||
"MACSEC_EGRESS_SC_TABLE:Ethernet0:5254008f4f1c0001": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"encoding_an": "2"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2305164
|
||||
},
|
||||
"MACSEC_INGRESS_SA_TABLE:Ethernet4:5254002003660001:1": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"active": "true",
|
||||
"sak": "234128B1F6A679E02759D521C1FF448D5CE47B2E691852281EE8E34690B348DD",
|
||||
"auth_key": "575FC253C395DFC3E1EE42C3DB665913",
|
||||
"lowest_acceptable_pn": "1",
|
||||
"ssci": "0",
|
||||
"salt": "000000000000000000000000"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2305293
|
||||
},
|
||||
"MACSEC_EGRESS_SA_TABLE:Ethernet0:5254008f4f1c0001:2": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"sak": "7F2651140F12C434F782EF9AD7791EE2CFE2BF315A568A48785E35FC803C9DB6",
|
||||
"auth_key": "5A8B8912139551D3678B43DD0F10FFA5",
|
||||
"next_pn": "1",
|
||||
"ssci": "0",
|
||||
"salt": "000000000000000000000000"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2305422
|
||||
},
|
||||
"MACSEC_INGRESS_SC_TABLE:Ethernet5:5254002003660001": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"Null": "Null"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2305527
|
||||
},
|
||||
"MACSEC_INGRESS_SC_TABLE:Ethernet4:5254002003660001": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"Null": "Null"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2305627
|
||||
},
|
||||
"MACSEC_PORT_TABLE:Ethernet1": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"enable": "true",
|
||||
"cipher_suite": "GCM-AES-256",
|
||||
"send_sci": "true",
|
||||
"enable_protect": "true",
|
||||
"enable_encrypt": "true",
|
||||
"enable_replay_protect": "false",
|
||||
"replay_window": "0"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2305753
|
||||
},
|
||||
"MACSEC_EGRESS_SA_TABLE:Ethernet0:5254008f4f1c0001:1": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"sak": "AE8C9BB36EA44B60375E84BC8E778596289E79240FDFA6D7BA33D3518E705A5E",
|
||||
"auth_key": "849B69D363E2B0AA154BEBBD7C1D9487",
|
||||
"next_pn": "1",
|
||||
"ssci": "0",
|
||||
"salt": "000000000000000000000000"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2305882
|
||||
},
|
||||
"MACSEC_EGRESS_SC_TABLE:Ethernet4:5254008f4f1c0001": {
|
||||
"type": "hash",
|
||||
"value": {
|
||||
"encoding_an": "1"
|
||||
},
|
||||
"ttl": -0.001,
|
||||
"expireat": 1651807960.2305987
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import pytest
|
||||
import mock_tables # lgtm [py/unused-import]
|
||||
from unittest import mock
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def mock_cfgdb():
|
||||
cfgdb = mock.Mock()
|
||||
CONFIG = {
|
||||
'PORT': {
|
||||
'Ethernet0': {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def get_entry(table, key):
|
||||
if table not in CONFIG or key not in CONFIG[table]:
|
||||
return {}
|
||||
return CONFIG[table][key]
|
||||
|
||||
def set_entry(table, key, data):
|
||||
CONFIG.setdefault(table, {})
|
||||
CONFIG[table].setdefault(key, {})
|
||||
CONFIG[table][key] = data
|
||||
|
||||
def get_keys(table):
|
||||
return CONFIG[table].keys()
|
||||
|
||||
cfgdb.get_entry = mock.Mock(side_effect=get_entry)
|
||||
cfgdb.set_entry = mock.Mock(side_effect=set_entry)
|
||||
cfgdb.get_keys = mock.Mock(side_effect=get_keys)
|
||||
|
||||
yield cfgdb
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,124 @@
|
|||
# MONKEY PATCH!!!
|
||||
import json
|
||||
import os
|
||||
from unittest import mock
|
||||
|
||||
import mockredis
|
||||
import redis
|
||||
import swsssdk
|
||||
from sonic_py_common import multi_asic
|
||||
from swsssdk import SonicV2Connector, ConfigDBConnector, ConfigDBPipeConnector
|
||||
from swsscommon import swsscommon
|
||||
|
||||
|
||||
dedicated_dbs = {}
|
||||
|
||||
|
||||
_old_connect_SonicV2Connector = SonicV2Connector.connect
|
||||
|
||||
def connect_SonicV2Connector(self, db_name, retry_on=True):
|
||||
# add the namespace to kwargs for testing multi asic
|
||||
self.dbintf.redis_kwargs['namespace'] = self.namespace
|
||||
# Mock DB filename for unit-test
|
||||
global dedicated_dbs
|
||||
if dedicated_dbs and dedicated_dbs.get(db_name):
|
||||
self.dbintf.redis_kwargs['db_name'] = dedicated_dbs[db_name]
|
||||
else:
|
||||
self.dbintf.redis_kwargs['db_name'] = db_name
|
||||
self.dbintf.redis_kwargs['decode_responses'] = True
|
||||
_old_connect_SonicV2Connector(self, db_name, retry_on)
|
||||
|
||||
def _subscribe_keyspace_notification(self, db_name, client):
|
||||
pass
|
||||
|
||||
|
||||
def config_set(self, *args):
|
||||
pass
|
||||
|
||||
|
||||
class MockPubSub:
|
||||
def get_message(self):
|
||||
return None
|
||||
|
||||
def psubscribe(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self
|
||||
|
||||
def listen(self):
|
||||
return []
|
||||
|
||||
def punsubscribe(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def clear(self):
|
||||
pass
|
||||
|
||||
INPUT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
class SwssSyncClient(mockredis.MockRedis):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SwssSyncClient, self).__init__(strict=True, *args, **kwargs)
|
||||
# Namespace is added in kwargs specifically for unit-test
|
||||
# to identify the file path to load the db json files.
|
||||
namespace = kwargs.pop('namespace')
|
||||
db_name = kwargs.pop('db_name')
|
||||
self.decode_responses = kwargs.pop('decode_responses', False) == True
|
||||
fname = db_name.lower() + ".json"
|
||||
self.pubsub = MockPubSub()
|
||||
|
||||
if namespace is not None and namespace is not multi_asic.DEFAULT_NAMESPACE:
|
||||
fname = os.path.join(INPUT_DIR, namespace, fname)
|
||||
else:
|
||||
fname = os.path.join(INPUT_DIR, fname)
|
||||
|
||||
if os.path.exists(fname):
|
||||
with open(fname) as f:
|
||||
js = json.load(f)
|
||||
for k, v in js.items():
|
||||
if 'expireat' in v and 'ttl' in v and 'type' in v and 'value' in v:
|
||||
# database is in redis-dump format
|
||||
if v['type'] == 'hash':
|
||||
# ignore other types for now since sonic has hset keys only in the db
|
||||
for attr, value in v['value'].items():
|
||||
self.hset(k, attr, value)
|
||||
else:
|
||||
for attr, value in v.items():
|
||||
self.hset(k, attr, value)
|
||||
|
||||
# Patch mockredis/mockredis/client.py
|
||||
# The offical implementation assume decode_responses=False
|
||||
# Here we detect the option and decode after doing encode
|
||||
def _encode(self, value):
|
||||
"Return a bytestring representation of the value. Taken from redis-py connection.py"
|
||||
|
||||
value = super(SwssSyncClient, self)._encode(value)
|
||||
|
||||
if self.decode_responses:
|
||||
return value.decode('utf-8')
|
||||
|
||||
# Patch mockredis/mockredis/client.py
|
||||
# The official implementation will filter out keys with a slash '/'
|
||||
# ref: https://github.com/locationlabs/mockredis/blob/master/mockredis/client.py
|
||||
def keys(self, pattern='*'):
|
||||
"""Emulate keys."""
|
||||
import fnmatch
|
||||
import re
|
||||
|
||||
# Make regex out of glob styled pattern.
|
||||
regex = fnmatch.translate(pattern)
|
||||
regex = re.compile(regex)
|
||||
|
||||
# Find every key that matches the pattern
|
||||
return [key for key in self.redis if regex.match(key)]
|
||||
|
||||
|
||||
swsssdk.interface.DBInterface._subscribe_keyspace_notification = _subscribe_keyspace_notification
|
||||
mockredis.MockRedis.config_set = config_set
|
||||
redis.StrictRedis = SwssSyncClient
|
||||
SonicV2Connector.connect = connect_SonicV2Connector
|
||||
swsscommon.SonicV2Connector = SonicV2Connector
|
||||
swsscommon.ConfigDBConnector = ConfigDBConnector
|
||||
swsscommon.ConfigDBPipeConnector = ConfigDBPipeConnector
|
|
@ -0,0 +1,2 @@
|
|||
[pytest]
|
||||
addopts = --cov-config=.coveragerc --cov --cov-report html --cov-report term --cov-report xml --junitxml=test-results.xml -vv
|
|
@ -0,0 +1,146 @@
|
|||
import sys
|
||||
|
||||
from unittest import mock
|
||||
from click.testing import CliRunner
|
||||
from utilities_common.db import Db
|
||||
|
||||
sys.path.append('../cli/config/plugins/')
|
||||
import macsec
|
||||
|
||||
|
||||
profile_name = "test"
|
||||
primary_cak = "01234567890123456789012345678912"
|
||||
primary_ckn = "01234567890123456789012345678912"
|
||||
|
||||
|
||||
class TestConfigMACsec(object):
|
||||
def test_plugin_registration(self):
|
||||
cli = mock.MagicMock()
|
||||
macsec.register(cli)
|
||||
cli.add_command.assert_called_once_with(macsec.macsec)
|
||||
|
||||
def test_default_profile(self, mock_cfgdb):
|
||||
runner = CliRunner()
|
||||
db = Db()
|
||||
db.cfgdb = mock_cfgdb
|
||||
result = runner.invoke(macsec.macsec.commands["profile"].commands["add"],
|
||||
[profile_name, "--primary_cak=" + primary_cak,"--primary_ckn=" + primary_ckn],
|
||||
obj=db)
|
||||
assert result.exit_code == 0
|
||||
profile_table = db.cfgdb.get_entry("MACSEC_PROFILE", profile_name)
|
||||
assert profile_table
|
||||
assert profile_table["priority"] == "255"
|
||||
assert profile_table["cipher_suite"] == "GCM-AES-128"
|
||||
assert profile_table["primary_cak"] == primary_cak
|
||||
assert profile_table["primary_ckn"] == primary_ckn
|
||||
assert profile_table["policy"] == "security"
|
||||
assert "enable_replay_protect" not in profile_table
|
||||
assert "replay_window" not in profile_table
|
||||
assert profile_table["send_sci"] == "true"
|
||||
assert "rekey_period" not in profile_table
|
||||
|
||||
result = runner.invoke(macsec.macsec.commands["profile"].commands["del"], [profile_name], obj=db)
|
||||
assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
|
||||
profile_table = db.cfgdb.get_entry("MACSEC_PROFILE", profile_name)
|
||||
assert not profile_table
|
||||
|
||||
def test_macsec_valid_profile(self, mock_cfgdb):
|
||||
runner = CliRunner()
|
||||
db = Db()
|
||||
db.cfgdb = mock_cfgdb
|
||||
|
||||
profile_name = "test"
|
||||
profile_map = {
|
||||
"primary_cak": "0123456789012345678901234567891201234567890123456789012345678912",
|
||||
"primary_ckn": "01234567890123456789012345678912",
|
||||
"priority": 64,
|
||||
"cipher_suite": "GCM-AES-XPN-256",
|
||||
"policy": "integrity_only",
|
||||
"enable_replay_protect": None,
|
||||
"replay_window": 100,
|
||||
"no_send_sci": None,
|
||||
"rekey_period": 30 * 60,
|
||||
}
|
||||
options = [profile_name]
|
||||
for k, v in profile_map.items():
|
||||
options.append("--" + k)
|
||||
if v is not None:
|
||||
options[-1] += "=" + str(v)
|
||||
|
||||
result = runner.invoke(macsec.macsec.commands["profile"].commands["add"], options, obj=db)
|
||||
assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
|
||||
profile_table = db.cfgdb.get_entry("MACSEC_PROFILE", profile_name)
|
||||
assert profile_table
|
||||
assert profile_table["priority"] == str(profile_map["priority"])
|
||||
assert profile_table["cipher_suite"] == profile_map["cipher_suite"]
|
||||
assert profile_table["primary_cak"] == profile_map["primary_cak"]
|
||||
assert profile_table["primary_ckn"] == profile_map["primary_ckn"]
|
||||
assert profile_table["policy"] == profile_map["policy"]
|
||||
if "enable_replay_protect" in profile_map:
|
||||
assert "enable_replay_protect" in profile_table and profile_table["enable_replay_protect"] == "true"
|
||||
assert profile_table["replay_window"] == str(profile_map["replay_window"])
|
||||
if "send_sci" in profile_map:
|
||||
assert profile_table["send_sci"] == "true"
|
||||
if "no_send_sci" in profile_map:
|
||||
assert profile_table["send_sci"] == "false"
|
||||
if "rekey_period" in profile_map:
|
||||
assert profile_table["rekey_period"] == str(profile_map["rekey_period"])
|
||||
|
||||
def test_macsec_invalid_profile(self, mock_cfgdb):
|
||||
runner = CliRunner()
|
||||
db = Db()
|
||||
db.cfgdb = mock_cfgdb
|
||||
|
||||
# Loss primary cak and primary ckn
|
||||
result = runner.invoke(macsec.macsec.commands["profile"].commands["add"], ["test"], obj=db)
|
||||
assert result.exit_code != 0
|
||||
|
||||
# Invalid primary cak
|
||||
result = runner.invoke(macsec.macsec.commands["profile"].commands["add"], ["test", "--primary_cak=abcdfghjk90123456789012345678912","--primary_ckn=01234567890123456789012345678912", "--cipher_suite=GCM-AES-128"], obj=db)
|
||||
assert result.exit_code != 0
|
||||
|
||||
# Invalid primary cak length
|
||||
result = runner.invoke(macsec.macsec.commands["profile"].commands["add"], ["test", "--primary_cak=01234567890123456789012345678912","--primary_ckn=01234567890123456789012345678912", "--cipher_suite=GCM-AES-256"], obj=db)
|
||||
assert result.exit_code != 0
|
||||
|
||||
|
||||
def test_macsec_port(self, mock_cfgdb):
|
||||
runner = CliRunner()
|
||||
db = Db()
|
||||
db.cfgdb = mock_cfgdb
|
||||
|
||||
result = runner.invoke(macsec.macsec.commands["profile"].commands["add"], ["test", "--primary_cak=01234567890123456789012345678912","--primary_ckn=01234567890123456789012345678912"], obj=db)
|
||||
assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
|
||||
result = runner.invoke(macsec.macsec.commands["port"].commands["add"], ["Ethernet0", "test"], obj=db)
|
||||
assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
|
||||
port_table = db.cfgdb.get_entry("PORT", "Ethernet0")
|
||||
assert port_table
|
||||
assert port_table["macsec"] == "test"
|
||||
|
||||
result = runner.invoke(macsec.macsec.commands["profile"].commands["del"], ["test"], obj=db)
|
||||
assert result.exit_code != 0
|
||||
|
||||
result = runner.invoke(macsec.macsec.commands["port"].commands["del"], ["Ethernet0"], obj=db)
|
||||
assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
|
||||
port_table = db.cfgdb.get_entry("PORT", "Ethernet0")
|
||||
assert not port_table["macsec"]
|
||||
|
||||
|
||||
def test_macsec_invalid_operation(self, mock_cfgdb):
|
||||
runner = CliRunner()
|
||||
db = Db()
|
||||
db.cfgdb = mock_cfgdb
|
||||
|
||||
# Enable nonexisted profile
|
||||
result = runner.invoke(macsec.macsec.commands["port"].commands["add"], ["Ethernet0", "test"], obj=db)
|
||||
assert result.exit_code != 0
|
||||
|
||||
# Delete nonexisted profile
|
||||
result = runner.invoke(macsec.macsec.commands["profile"].commands["del"], ["test"], obj=db)
|
||||
assert result.exit_code != 0
|
||||
|
||||
result = runner.invoke(macsec.macsec.commands["profile"].commands["add"], ["test", "--primary_cak=01234567890123456789012345678912","--primary_ckn=01234567890123456789012345678912"], obj=db)
|
||||
assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
|
||||
# Repeat add profile
|
||||
result = runner.invoke(macsec.macsec.commands["profile"].commands["add"], ["test", "--primary_cak=01234567890123456789012345678912","--primary_ckn=01234567890123456789012345678912"], obj=db)
|
||||
assert result.exit_code != 0
|
|
@ -0,0 +1,25 @@
|
|||
import sys
|
||||
from unittest import mock
|
||||
|
||||
from click.testing import CliRunner
|
||||
|
||||
sys.path.append('../cli/show/plugins/')
|
||||
import show_macsec
|
||||
|
||||
|
||||
class TestShowMACsec(object):
|
||||
def test_plugin_registration(self):
|
||||
cli = mock.MagicMock()
|
||||
show_macsec.register(cli)
|
||||
cli.add_command.assert_called_once_with(show_macsec.macsec)
|
||||
|
||||
def test_show_all(self):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(show_macsec.macsec,[])
|
||||
assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
|
||||
|
||||
def test_show_one_port(self):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(show_macsec.macsec,["Ethernet1"])
|
||||
assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info)
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
import click
|
||||
import utilities_common.cli as clicommon
|
||||
|
||||
#
|
||||
# 'macsec' group ('config macsec ...')
|
||||
#
|
||||
@click.group(cls=clicommon.AbbreviationGroup, name='macsec')
|
||||
def macsec():
|
||||
"""MACsec-related configuration tasks"""
|
||||
pass
|
||||
|
||||
|
||||
#
|
||||
# 'port' group ('config macsec port ...')
|
||||
#
|
||||
@macsec.group(cls=clicommon.AbbreviationGroup, name='port')
|
||||
def macsec_port():
|
||||
"""Enable MACsec or disable MACsec on the specified port"""
|
||||
pass
|
||||
|
||||
#
|
||||
# 'add' command ('config macsec port add ...')
|
||||
#
|
||||
@macsec_port.command('add')
|
||||
@click.argument('port', metavar='<port_name>', required=True)
|
||||
@click.argument('profile', metavar='<profile_name>', required=True)
|
||||
@clicommon.pass_db
|
||||
def add_port(db, port, profile):
|
||||
"""
|
||||
Add MACsec port
|
||||
"""
|
||||
ctx = click.get_current_context()
|
||||
|
||||
if clicommon.get_interface_naming_mode() == "alias":
|
||||
alias = port
|
||||
iface_alias_converter = clicommon.InterfaceAliasConverter(db)
|
||||
port = iface_alias_converter.alias_to_name(alias)
|
||||
if port is None:
|
||||
ctx.fail("cannot find port name for alias {}".format(alias))
|
||||
|
||||
profile_entry = db.cfgdb.get_entry('MACSEC_PROFILE', profile)
|
||||
if len(profile_entry) == 0:
|
||||
ctx.fail("profile {} doesn't exist".format(profile))
|
||||
|
||||
db.cfgdb.set_entry("PORT", port, {'macsec': profile})
|
||||
|
||||
|
||||
#
|
||||
# 'del' command ('config macsec port del ...')
|
||||
#
|
||||
@macsec_port.command('del')
|
||||
@click.argument('port', metavar='<port_name>', required=True)
|
||||
@clicommon.pass_db
|
||||
def del_port(db, port):
|
||||
"""
|
||||
Delete MACsec port
|
||||
"""
|
||||
ctx = click.get_current_context()
|
||||
|
||||
if clicommon.get_interface_naming_mode() == "alias":
|
||||
alias = port
|
||||
iface_alias_converter = clicommon.InterfaceAliasConverter(db)
|
||||
port = iface_alias_converter.alias_to_name(alias)
|
||||
if port is None:
|
||||
ctx.fail("cannot find port name for alias {}".format(alias))
|
||||
|
||||
db.cfgdb.set_entry("PORT", port, {'macsec': ""})
|
||||
|
||||
|
||||
#
|
||||
# 'profile' group ('config macsec profile ...')
|
||||
#
|
||||
@macsec.group(cls=clicommon.AbbreviationGroup, name='profile')
|
||||
def macsec_profile():
|
||||
pass
|
||||
|
||||
|
||||
def is_hexstring(hexstring: str):
|
||||
try:
|
||||
int(hexstring, 16)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
#
|
||||
# 'add' command ('config macsec profile add ...')
|
||||
#
|
||||
@macsec_profile.command('add')
|
||||
@click.argument('profile', metavar='<profile_name>', required=True)
|
||||
@click.option('--priority', metavar='<priority>', required=False, default=255, show_default=True, type=click.IntRange(0, 255), help="For Key server election. In 0-255 range with 0 being the highest priority.")
|
||||
@click.option('--cipher_suite', metavar='<cipher_suite>', required=False, default="GCM-AES-128", show_default=True, type=click.Choice(["GCM-AES-128", "GCM-AES-256", "GCM-AES-XPN-128", "GCM-AES-XPN-256"]), help="The cipher suite for MACsec.")
|
||||
@click.option('--primary_cak', metavar='<primary_cak>', required=True, type=str, help="Primary Connectivity Association Key.")
|
||||
@click.option('--primary_ckn', metavar='<primary_cak>', required=True, type=str, help="Primary CAK Name.")
|
||||
@click.option('--policy', metavar='<policy>', required=False, default="security", show_default=True, type=click.Choice(["integrity_only", "security"]), help="MACsec policy. INTEGRITY_ONLY: All traffic, except EAPOL, will be converted to MACsec packets without encryption. SECURITY: All traffic, except EAPOL, will be encrypted by SecY.")
|
||||
@click.option('--enable_replay_protect/--disable_replay_protect', metavar='<replay_protect>', required=False, default=False, show_default=True, is_flag=True, help="Whether enable replay protect.")
|
||||
@click.option('--replay_window', metavar='<enable_replay_protect>', required=False, default=0, show_default=True, type=click.IntRange(0, 2**32), help="Replay window size that is the number of packets that could be out of order. This field works only if ENABLE_REPLAY_PROTECT is true.")
|
||||
@click.option('--send_sci/--no_send_sci', metavar='<send_sci>', required=False, default=True, show_default=True, is_flag=True, help="Send SCI in SecTAG field of MACsec header.")
|
||||
@click.option('--rekey_period', metavar='<rekey_period>', required=False, default=0, show_default=True, type=click.IntRange(min=0), help="The period of proactively refresh (Unit second).")
|
||||
@clicommon.pass_db
|
||||
def add_profile(db, profile, priority, cipher_suite, primary_cak, primary_ckn, policy, enable_replay_protect, replay_window, send_sci, rekey_period):
|
||||
"""
|
||||
Add MACsec profile
|
||||
"""
|
||||
ctx = click.get_current_context()
|
||||
profile_entry = db.cfgdb.get_entry('MACSEC_PROFILE', profile)
|
||||
if not len(profile_entry) == 0:
|
||||
ctx.fail("{} already exists".format(profile))
|
||||
|
||||
profile_table = {}
|
||||
|
||||
profile_table["priority"] = priority
|
||||
|
||||
profile_table["cipher_suite"] = cipher_suite
|
||||
|
||||
if "128" in cipher_suite:
|
||||
if len(primary_cak) != 32:
|
||||
ctx.fail("Expect the length of CAK is 32, but got {}".format(len(primary_cak)))
|
||||
elif "256" in cipher_suite:
|
||||
if len(primary_cak) != 64:
|
||||
ctx.fail("Expect the length of CAK is 64, but got {}".format(len(primary_cak)))
|
||||
if not is_hexstring(primary_cak):
|
||||
ctx.fail("Expect the primary_cak is valid hex string")
|
||||
if not is_hexstring(primary_ckn):
|
||||
ctx.fail("Expect the primary_ckn is valid hex string")
|
||||
profile_table["primary_cak"] = primary_cak
|
||||
profile_table["primary_ckn"] = primary_ckn
|
||||
|
||||
profile_table["policy"] = policy
|
||||
|
||||
if enable_replay_protect and replay_window > 0:
|
||||
profile_table["enable_replay_protect"] = enable_replay_protect
|
||||
profile_table["replay_window"] = replay_window
|
||||
|
||||
profile_table["send_sci"] = send_sci
|
||||
|
||||
if rekey_period > 0:
|
||||
profile_table["rekey_period"] = rekey_period
|
||||
|
||||
for k, v in profile_table.items():
|
||||
if isinstance(v, bool):
|
||||
if v:
|
||||
profile_table[k] = "true"
|
||||
else:
|
||||
profile_table[k] = "false"
|
||||
else:
|
||||
profile_table[k] = str(v)
|
||||
db.cfgdb.set_entry("MACSEC_PROFILE", profile, profile_table)
|
||||
|
||||
|
||||
#
|
||||
# 'del' command ('config macsec profile del ...')
|
||||
#
|
||||
@macsec_profile.command('del')
|
||||
@click.argument('profile', metavar='<profile_name>', required=True)
|
||||
@clicommon.pass_db
|
||||
def del_profile(db, profile):
|
||||
"""
|
||||
Delete MACsec profile
|
||||
"""
|
||||
ctx = click.get_current_context()
|
||||
|
||||
profile_entry = db.cfgdb.get_entry('MACSEC_PROFILE', profile)
|
||||
if len(profile_entry) == 0:
|
||||
ctx.fail("{} doesn't exist".format(profile))
|
||||
|
||||
# Check if the profile is being used by any port
|
||||
for port in db.cfgdb.get_keys('PORT'):
|
||||
attr = db.cfgdb.get_entry('PORT', port)
|
||||
if 'macsec' in attr and attr['macsec'] == profile:
|
||||
ctx.fail("{} is being used by port {}, Please remove the MACsec from the port firstly".format(profile, port))
|
||||
|
||||
db.cfgdb.set_entry("MACSEC_PROFILE", profile, None)
|
||||
|
||||
|
||||
def register(cli):
|
||||
cli.add_command(macsec)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
macsec()
|
|
@ -0,0 +1,217 @@
|
|||
import typing
|
||||
from natsort import natsorted
|
||||
|
||||
import click
|
||||
from tabulate import tabulate
|
||||
|
||||
from swsscommon.swsscommon import SonicV2Connector
|
||||
|
||||
|
||||
DB_CONNECTOR = SonicV2Connector(use_unix_socket_path=False)
|
||||
DB_CONNECTOR.connect(DB_CONNECTOR.APPL_DB)
|
||||
DB_CONNECTOR.connect(DB_CONNECTOR.COUNTERS_DB)
|
||||
|
||||
|
||||
class MACsecAppMeta(object):
|
||||
SEPARATOR = DB_CONNECTOR.get_db_separator(DB_CONNECTOR.APPL_DB)
|
||||
|
||||
def __init__(self, *args) -> None:
|
||||
key = self.__class__.get_appl_table_name() + MACsecAppMeta.SEPARATOR + \
|
||||
MACsecAppMeta.SEPARATOR.join(args)
|
||||
self.meta = DB_CONNECTOR.get_all(
|
||||
DB_CONNECTOR.APPL_DB, key)
|
||||
if len(self.meta) == 0:
|
||||
raise ValueError("No such MACsecAppMeta: {}".format(key))
|
||||
for k, v in self.meta.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
|
||||
class MACsecCounters(object):
|
||||
def __init__(self, *args) -> None:
|
||||
key = ":".join(args)
|
||||
counters_id = DB_CONNECTOR.get(
|
||||
DB_CONNECTOR.COUNTERS_DB, self.__class__.get_counter_table_name(), key)
|
||||
counter_key = "COUNTERS:" + counters_id
|
||||
self.counters = DB_CONNECTOR.get_all(
|
||||
DB_CONNECTOR.COUNTERS_DB, counter_key)
|
||||
|
||||
|
||||
class MACsecSA(MACsecAppMeta, MACsecCounters):
|
||||
def __init__(self, port_name: str, sci: str, an: str) -> None:
|
||||
self.port_name = port_name
|
||||
self.sci = sci
|
||||
self.an = an
|
||||
MACsecAppMeta.__init__(self, port_name, sci, an)
|
||||
MACsecCounters.__init__(self, port_name, sci, an)
|
||||
|
||||
def dump_str(self) -> str:
|
||||
buffer = self.get_header()
|
||||
meta = sorted(self.meta.items(), key=lambda x: x[0])
|
||||
counters = sorted(self.counters.items(), key=lambda x: x[0])
|
||||
buffer += tabulate(meta + counters)
|
||||
buffer = "\n".join(["\t\t" + line for line in buffer.splitlines()])
|
||||
return buffer
|
||||
|
||||
|
||||
class MACsecIngressSA(MACsecSA):
|
||||
def __init__(self, port_name: str, sci: str, an: str) -> None:
|
||||
super(MACsecIngressSA, self).__init__(port_name, sci, an)
|
||||
|
||||
@classmethod
|
||||
def get_appl_table_name(cls) -> str:
|
||||
return "MACSEC_INGRESS_SA_TABLE"
|
||||
|
||||
@classmethod
|
||||
def get_counter_table_name(cls) -> str:
|
||||
return "COUNTERS_MACSEC_SA_RX_NAME_MAP"
|
||||
|
||||
def get_header(self):
|
||||
return "MACsec Ingress SA ({})\n".format(self.an)
|
||||
|
||||
|
||||
class MACsecEgressSA(MACsecSA):
|
||||
def __init__(self, port_name: str, sci: str, an: str) -> None:
|
||||
super(MACsecEgressSA, self).__init__(port_name, sci, an)
|
||||
|
||||
@classmethod
|
||||
def get_appl_table_name(cls) -> str:
|
||||
return "MACSEC_EGRESS_SA_TABLE"
|
||||
|
||||
@classmethod
|
||||
def get_counter_table_name(cls) -> str:
|
||||
return "COUNTERS_MACSEC_SA_TX_NAME_MAP"
|
||||
|
||||
def get_header(self):
|
||||
return "MACsec Egress SA ({})\n".format(self.an)
|
||||
|
||||
|
||||
class MACsecSC(MACsecAppMeta):
|
||||
def __init__(self, port_name: str, sci: str) -> None:
|
||||
self.port_name = port_name
|
||||
self.sci = sci
|
||||
super(MACsecSC, self).__init__(port_name, sci)
|
||||
|
||||
|
||||
class MACsecIngressSC(MACsecSC):
|
||||
def __init__(self, port_name: str, sci: str) -> None:
|
||||
super(MACsecIngressSC, self).__init__(port_name, sci)
|
||||
|
||||
@classmethod
|
||||
def get_appl_table_name(cls) -> str:
|
||||
return "MACSEC_INGRESS_SC_TABLE"
|
||||
|
||||
def dump_str(self) -> str:
|
||||
buffer = self.get_header()
|
||||
buffer = "\n".join(["\t" + line for line in buffer.splitlines()])
|
||||
return buffer
|
||||
|
||||
def get_header(self):
|
||||
return "MACsec Ingress SC ({})\n".format(self.sci)
|
||||
|
||||
|
||||
class MACsecEgressSC(MACsecSC):
|
||||
def __init__(self, port_name: str, sci: str) -> None:
|
||||
super(MACsecEgressSC, self).__init__(port_name, sci)
|
||||
|
||||
@classmethod
|
||||
def get_appl_table_name(cls) -> str:
|
||||
return "MACSEC_EGRESS_SC_TABLE"
|
||||
|
||||
def dump_str(self) -> str:
|
||||
buffer = self.get_header()
|
||||
buffer += tabulate(sorted(self.meta.items(), key=lambda x: x[0]))
|
||||
buffer = "\n".join(["\t" + line for line in buffer.splitlines()])
|
||||
return buffer
|
||||
|
||||
def get_header(self):
|
||||
return "MACsec Egress SC ({})\n".format(self.sci)
|
||||
|
||||
|
||||
class MACsecPort(MACsecAppMeta):
|
||||
def __init__(self, port_name: str) -> None:
|
||||
self.port_name = port_name
|
||||
super(MACsecPort, self).__init__(port_name)
|
||||
|
||||
@classmethod
|
||||
def get_appl_table_name(cls) -> str:
|
||||
return "MACSEC_PORT_TABLE"
|
||||
|
||||
def dump_str(self) -> str:
|
||||
buffer = self.get_header()
|
||||
buffer += tabulate(sorted(self.meta.items(), key=lambda x: x[0]))
|
||||
return buffer
|
||||
|
||||
def get_header(self) -> str:
|
||||
return "MACsec port({})\n".format(self.port_name)
|
||||
|
||||
|
||||
def create_macsec_obj(key: str) -> MACsecAppMeta:
|
||||
attr = key.split(":")
|
||||
try:
|
||||
if attr[0] == MACsecPort.get_appl_table_name():
|
||||
return MACsecPort(attr[1])
|
||||
elif attr[0] == MACsecIngressSC.get_appl_table_name():
|
||||
return MACsecIngressSC(attr[1], attr[2])
|
||||
elif attr[0] == MACsecEgressSC.get_appl_table_name():
|
||||
return MACsecEgressSC(attr[1], attr[2])
|
||||
elif attr[0] == MACsecIngressSA.get_appl_table_name():
|
||||
return MACsecIngressSA(attr[1], attr[2], attr[3])
|
||||
elif attr[0] == MACsecEgressSA.get_appl_table_name():
|
||||
return MACsecEgressSA(attr[1], attr[2], attr[3])
|
||||
raise TypeError("Unknown MACsec object type")
|
||||
except ValueError as e:
|
||||
return None
|
||||
|
||||
def create_macsec_objs(interface_name: str) -> typing.List[MACsecAppMeta]:
|
||||
objs = []
|
||||
objs.append(create_macsec_obj(MACsecPort.get_appl_table_name() + ":" + interface_name))
|
||||
egress_scs = DB_CONNECTOR.keys(DB_CONNECTOR.APPL_DB, MACsecEgressSC.get_appl_table_name() + ":" + interface_name + ":*")
|
||||
for sc_name in natsorted(egress_scs):
|
||||
sc = create_macsec_obj(sc_name)
|
||||
if sc is None:
|
||||
continue
|
||||
objs.append(sc)
|
||||
egress_sas = DB_CONNECTOR.keys(DB_CONNECTOR.APPL_DB, MACsecEgressSA.get_appl_table_name() + ":" + ":".join(sc_name.split(":")[1:]) + ":*")
|
||||
for sa_name in natsorted(egress_sas):
|
||||
sa = create_macsec_obj(sa_name)
|
||||
if sa is None:
|
||||
continue
|
||||
objs.append(sa)
|
||||
ingress_scs = DB_CONNECTOR.keys(DB_CONNECTOR.APPL_DB, MACsecIngressSC.get_appl_table_name() + ":" + interface_name + ":*")
|
||||
for sc_name in natsorted(ingress_scs):
|
||||
sc = create_macsec_obj(sc_name)
|
||||
if sc is None:
|
||||
continue
|
||||
objs.append(sc)
|
||||
ingress_sas = DB_CONNECTOR.keys(DB_CONNECTOR.APPL_DB, MACsecIngressSA.get_appl_table_name() + ":" + ":".join(sc_name.split(":")[1:]) + ":*")
|
||||
for sa_name in natsorted(ingress_sas):
|
||||
sa = create_macsec_obj(sa_name)
|
||||
if sa is None:
|
||||
continue
|
||||
objs.append(sa)
|
||||
return objs
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument('interface_name', required=False)
|
||||
def macsec(interface_name):
|
||||
ctx = click.get_current_context()
|
||||
objs = []
|
||||
interface_names = [name.split(":")[1] for name in DB_CONNECTOR.keys(DB_CONNECTOR.APPL_DB, "MACSEC_PORT*")]
|
||||
if interface_name is not None:
|
||||
if interface_name not in interface_names:
|
||||
ctx.fail("Cannot find the port {} in MACsec port lists {}".format(interface_name, interface_names))
|
||||
else:
|
||||
interface_names = [interface_name]
|
||||
for interface_name in natsorted(interface_names):
|
||||
objs += create_macsec_objs(interface_name)
|
||||
for obj in objs:
|
||||
print(obj.dump_str())
|
||||
|
||||
|
||||
def register(cli):
|
||||
cli.add_command(macsec)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
macsec(None)
|
|
@ -14,14 +14,25 @@ $(DOCKER_MACSEC)_DBG_IMAGE_PACKAGES = $($(DOCKER_CONFIG_ENGINE_BULLSEYE)_DBG_IMA
|
|||
|
||||
$(DOCKER_MACSEC)_LOAD_DOCKERS += $(DOCKER_CONFIG_ENGINE_BULLSEYE)
|
||||
|
||||
$(DOCKER_MACSEC)_INSTALL_PYTHON_WHEELS = $(SONIC_UTILITIES_PY3)
|
||||
$(DOCKER_MACSEC)_INSTALL_DEBS = $(PYTHON3_SWSSCOMMON) $(LIBYANG_PY3)
|
||||
|
||||
SONIC_DOCKER_IMAGES += $(DOCKER_MACSEC)
|
||||
ifeq ($(INCLUDE_MACSEC), y)
|
||||
SONIC_INSTALL_DOCKER_IMAGES += $(DOCKER_MACSEC)
|
||||
SONIC_DOCKER_DBG_IMAGES += $(DOCKER_MACSEC_DBG)
|
||||
|
||||
ifeq ($(INCLUDE_KUBERNETES),y)
|
||||
$(DOCKER_MACSEC)_DEFAULT_FEATURE_OWNER = kube
|
||||
endif
|
||||
|
||||
SONIC_DOCKER_DBG_IMAGES += $(DOCKER_MACSEC_DBG)
|
||||
ifeq ($(INCLUDE_MACSEC), y)
|
||||
SONIC_INSTALL_DOCKER_DBG_IMAGES += $(DOCKER_MACSEC_DBG)
|
||||
$(DOCKER_MACSEC)_DEFAULT_FEATURE_STATE_ENABLED = y
|
||||
|
||||
|
||||
ifeq ($(INCLUDE_MACSEC),y)
|
||||
ifeq ($(INSTALL_DEBUG_TOOLS),y)
|
||||
SONIC_PACKAGES_LOCAL += $(DOCKER_MACSEC_DBG)
|
||||
else
|
||||
SONIC_PACKAGES_LOCAL += $(DOCKER_MACSEC)
|
||||
endif
|
||||
endif
|
||||
|
||||
$(DOCKER_MACSEC)_CONTAINER_NAME = macsec
|
||||
|
@ -31,7 +42,7 @@ $(DOCKER_MACSEC)_RUN_OPT += --privileged -t
|
|||
$(DOCKER_MACSEC)_RUN_OPT += -v /etc/sonic:/etc/sonic:ro
|
||||
$(DOCKER_MACSEC)_RUN_OPT += -v /host/warmboot:/var/warmboot
|
||||
|
||||
$(DOCKER_MACSEC)_FILES += $(SUPERVISOR_PROC_EXIT_LISTENER_SCRIPT)
|
||||
$(DOCKER_MACSEC)_CLI_CONFIG_PLUGIN = /cli/config/plugins/macsec.py
|
||||
$(DOCKER_MACSEC)_CLI_SHOW_PLUGIN = /cli/show/plugins/show_macsec.py
|
||||
|
||||
SONIC_BULLSEYE_DOCKERS += $(DOCKER_MACSEC)
|
||||
SONIC_BULLSEYE_DBG_DOCKERS += $(DOCKER_MACSEC_DBG)
|
||||
$(DOCKER_MACSEC)_FILES += $(SUPERVISOR_PROC_EXIT_LISTENER_SCRIPT)
|
||||
|
|
Загрузка…
Ссылка в новой задаче