Path: blob/master/tools/testing/selftests/drivers/net/macsec.py
170997 views
#!/usr/bin/env python31# SPDX-License-Identifier: GPL-2.023"""MACsec tests."""45import os67from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_raises8from lib.py import ksft_variants, KsftNamedVariant9from lib.py import CmdExitFailure, KsftSkipEx10from lib.py import NetDrvEpEnv11from lib.py import cmd, ip, defer, ethtool1213MACSEC_KEY = "12345678901234567890123456789012"14MACSEC_VLAN_VID = 101516# Unique prefix per run to avoid collisions in the shared netns.17# Keep it short: IFNAMSIZ is 16 (incl. NUL), and VLAN names append ".<vid>".18MACSEC_PFX = f"ms{os.getpid()}_"192021def _macsec_name(idx=0):22return f"{MACSEC_PFX}{idx}"232425def _get_macsec_offload(dev):26"""Returns macsec offload mode string from ip -d link show."""27info = ip(f"-d link show dev {dev}", json=True)[0]28return info.get("linkinfo", {}).get("info_data", {}).get("offload")293031def _get_features(dev):32"""Returns ethtool features dict for a device."""33return ethtool(f"-k {dev}", json=True)[0]343536def _require_ip_macsec(cfg):37"""SKIP if iproute2 on local or remote lacks 'ip macsec' support."""38for host in [None, cfg.remote]:39out = cmd("ip macsec help", fail=False, host=host)40if "Usage" not in out.stdout + out.stderr:41where = "remote" if host else "local"42raise KsftSkipEx(f"iproute2 too old on {where},"43" missing macsec support")444546def _require_ip_macsec_offload():47"""SKIP if local iproute2 doesn't understand 'ip macsec offload'."""48out = cmd("ip macsec help", fail=False)49if "offload" not in out.stdout + out.stderr:50raise KsftSkipEx("iproute2 too old, missing macsec offload")515253def _require_macsec_offload(cfg):54"""SKIP if local device doesn't support macsec-hw-offload."""55_require_ip_macsec_offload()56try:57feat = ethtool(f"-k {cfg.ifname}", json=True)[0]58except (CmdExitFailure, IndexError) as e:59raise KsftSkipEx(60f"can't query features: {e}") from e61if not feat.get("macsec-hw-offload", {}).get("active"):62raise KsftSkipEx("macsec-hw-offload not supported")636465def _get_mac(ifname, host=None):66"""Gets MAC address of an interface."""67dev = ip(f"link show dev {ifname}", json=True, host=host)68return dev[0]["address"]697071def _setup_macsec_sa(cfg, name):72"""Adds matching TX/RX SAs on both ends."""73local_mac = _get_mac(name)74remote_mac = _get_mac(name, host=cfg.remote)7576ip(f"macsec add {name} tx sa 0 pn 1 on key 01 {MACSEC_KEY}")77ip(f"macsec add {name} rx port 1 address {remote_mac}")78ip(f"macsec add {name} rx port 1 address {remote_mac} "79f"sa 0 pn 1 on key 02 {MACSEC_KEY}")8081ip(f"macsec add {name} tx sa 0 pn 1 on key 02 {MACSEC_KEY}",82host=cfg.remote)83ip(f"macsec add {name} rx port 1 address {local_mac}", host=cfg.remote)84ip(f"macsec add {name} rx port 1 address {local_mac} "85f"sa 0 pn 1 on key 01 {MACSEC_KEY}", host=cfg.remote)868788def _setup_macsec_devs(cfg, name, offload):89"""Creates macsec devices on both ends.9091Only the local device gets HW offload; the remote always uses software92MACsec since it may not support offload at all.93"""94offload_arg = "mac" if offload else "off"9596ip(f"link add link {cfg.ifname} {name} "97f"type macsec encrypt on offload {offload_arg}")98defer(ip, f"link del {name}")99ip(f"link add link {cfg.remote_ifname} {name} "100f"type macsec encrypt on", host=cfg.remote)101defer(ip, f"link del {name}", host=cfg.remote)102103104def _set_offload(name, offload):105"""Sets offload on the local macsec device only."""106offload_arg = "mac" if offload else "off"107108ip(f"link set {name} type macsec encrypt on offload {offload_arg}")109110111def _setup_vlans(cfg, name, vid):112"""Adds VLANs on top of existing macsec devs."""113vlan_name = f"{name}.{vid}"114115ip(f"link add link {name} {vlan_name} type vlan id {vid}")116defer(ip, f"link del {vlan_name}")117ip(f"link add link {name} {vlan_name} type vlan id {vid}", host=cfg.remote)118defer(ip, f"link del {vlan_name}", host=cfg.remote)119120121def _setup_vlan_ips(cfg, name, vid):122"""Adds VLANs and IPs and brings up the macsec + VLAN devices."""123local_ip = "198.51.100.1"124remote_ip = "198.51.100.2"125vlan_name = f"{name}.{vid}"126127ip(f"addr add {local_ip}/24 dev {vlan_name}")128ip(f"addr add {remote_ip}/24 dev {vlan_name}", host=cfg.remote)129ip(f"link set {name} up")130ip(f"link set {name} up", host=cfg.remote)131ip(f"link set {vlan_name} up")132ip(f"link set {vlan_name} up", host=cfg.remote)133134return vlan_name, remote_ip135136137def test_offload_api(cfg) -> None:138"""MACsec offload API: create SecY, add SA/rx, toggle offload."""139140_require_macsec_offload(cfg)141ms0 = _macsec_name(0)142ms1 = _macsec_name(1)143ms2 = _macsec_name(2)144145# Create 3 SecY with offload146ip(f"link add link {cfg.ifname} {ms0} type macsec "147f"port 4 encrypt on offload mac")148defer(ip, f"link del {ms0}")149150ip(f"link add link {cfg.ifname} {ms1} type macsec "151f"address aa:bb:cc:dd:ee:ff port 5 encrypt on offload mac")152defer(ip, f"link del {ms1}")153154ip(f"link add link {cfg.ifname} {ms2} type macsec "155f"sci abbacdde01020304 encrypt on offload mac")156defer(ip, f"link del {ms2}")157158# Add TX SA159ip(f"macsec add {ms0} tx sa 0 pn 1024 on "160"key 01 12345678901234567890123456789012")161162# Add RX SC + SA163ip(f"macsec add {ms0} rx port 1234 address 1c:ed:de:ad:be:ef")164ip(f"macsec add {ms0} rx port 1234 address 1c:ed:de:ad:be:ef "165"sa 0 pn 1 on key 00 0123456789abcdef0123456789abcdef")166167# Can't disable offload when SAs are configured168with ksft_raises(CmdExitFailure):169ip(f"link set {ms0} type macsec offload off")170with ksft_raises(CmdExitFailure):171ip(f"macsec offload {ms0} off")172173# Toggle offload via rtnetlink on SA-free device174ip(f"link set {ms2} type macsec offload off")175ip(f"link set {ms2} type macsec encrypt on offload mac")176177# Toggle offload via genetlink178ip(f"macsec offload {ms2} off")179ip(f"macsec offload {ms2} mac")180181182def test_max_secy(cfg) -> None:183"""nsim-only test for max number of SecYs."""184185cfg.require_nsim()186_require_ip_macsec_offload()187ms0 = _macsec_name(0)188ms1 = _macsec_name(1)189ms2 = _macsec_name(2)190ms3 = _macsec_name(3)191192ip(f"link add link {cfg.ifname} {ms0} type macsec "193f"port 4 encrypt on offload mac")194defer(ip, f"link del {ms0}")195196ip(f"link add link {cfg.ifname} {ms1} type macsec "197f"address aa:bb:cc:dd:ee:ff port 5 encrypt on offload mac")198defer(ip, f"link del {ms1}")199200ip(f"link add link {cfg.ifname} {ms2} type macsec "201f"sci abbacdde01020304 encrypt on offload mac")202defer(ip, f"link del {ms2}")203with ksft_raises(CmdExitFailure):204ip(f"link add link {cfg.ifname} {ms3} "205f"type macsec port 8 encrypt on offload mac")206207208def test_max_sc(cfg) -> None:209"""nsim-only test for max number of SCs."""210211cfg.require_nsim()212_require_ip_macsec_offload()213ms0 = _macsec_name(0)214215ip(f"link add link {cfg.ifname} {ms0} type macsec "216f"port 4 encrypt on offload mac")217defer(ip, f"link del {ms0}")218ip(f"macsec add {ms0} rx port 1234 address 1c:ed:de:ad:be:ef")219with ksft_raises(CmdExitFailure):220ip(f"macsec add {ms0} rx port 1235 address 1c:ed:de:ad:be:ef")221222223def test_offload_state(cfg) -> None:224"""Offload state reflects configuration changes."""225226_require_macsec_offload(cfg)227ms0 = _macsec_name(0)228229# Create with offload on230ip(f"link add link {cfg.ifname} {ms0} type macsec "231f"encrypt on offload mac")232cleanup = defer(ip, f"link del {ms0}")233234ksft_eq(_get_macsec_offload(ms0), "mac",235"created with offload: should be mac")236feats_on_1 = _get_features(ms0)237238ip(f"link set {ms0} type macsec offload off")239ksft_eq(_get_macsec_offload(ms0), "off",240"offload disabled: should be off")241feats_off_1 = _get_features(ms0)242243ip(f"link set {ms0} type macsec encrypt on offload mac")244ksft_eq(_get_macsec_offload(ms0), "mac",245"offload re-enabled: should be mac")246ksft_eq(_get_features(ms0), feats_on_1,247"features should match first offload-on snapshot")248249# Delete and recreate without offload250cleanup.exec()251ip(f"link add link {cfg.ifname} {ms0} type macsec")252defer(ip, f"link del {ms0}")253ksft_eq(_get_macsec_offload(ms0), "off",254"created without offload: should be off")255ksft_eq(_get_features(ms0), feats_off_1,256"features should match first offload-off snapshot")257258ip(f"link set {ms0} type macsec encrypt on offload mac")259ksft_eq(_get_macsec_offload(ms0), "mac",260"offload enabled after create: should be mac")261ksft_eq(_get_features(ms0), feats_on_1,262"features should match first offload-on snapshot")263264265def _check_nsim_vid(cfg, vid, expected) -> None:266"""Checks if a VLAN is present. Only works on netdevsim."""267268nsim = cfg.get_local_nsim_dev()269if not nsim:270return271272vlan_path = os.path.join(nsim.nsims[0].dfs_dir, "vlan")273with open(vlan_path, encoding="utf-8") as f:274vids = f.read()275found = f"ctag {vid}\n" in vids276ksft_eq(found, expected,277f"VLAN {vid} {'expected' if expected else 'not expected'}"278f" in debugfs")279280281@ksft_variants([282KsftNamedVariant("offloaded", True),283KsftNamedVariant("software", False),284])285def test_vlan(cfg, offload) -> None:286"""Ping through VLAN-over-macsec."""287288_require_ip_macsec(cfg)289if offload:290_require_macsec_offload(cfg)291else:292_require_ip_macsec_offload()293name = _macsec_name()294_setup_macsec_devs(cfg, name, offload=offload)295_setup_macsec_sa(cfg, name)296_setup_vlans(cfg, name, MACSEC_VLAN_VID)297vlan_name, remote_ip = _setup_vlan_ips(cfg, name, MACSEC_VLAN_VID)298_check_nsim_vid(cfg, MACSEC_VLAN_VID, offload)299# nsim doesn't handle the data path for offloaded macsec, so skip300# the ping when offloaded on nsim.301if not offload or not cfg.get_local_nsim_dev():302cmd(f"ping -I {vlan_name} -c 1 -W 5 {remote_ip}")303304305@ksft_variants([306KsftNamedVariant("on_to_off", True),307KsftNamedVariant("off_to_on", False),308])309def test_vlan_toggle(cfg, offload) -> None:310"""Toggle offload: VLAN filters propagate/remove correctly."""311312_require_ip_macsec(cfg)313_require_macsec_offload(cfg)314name = _macsec_name()315_setup_macsec_devs(cfg, name, offload=offload)316_setup_vlans(cfg, name, MACSEC_VLAN_VID)317_check_nsim_vid(cfg, MACSEC_VLAN_VID, offload)318_set_offload(name, offload=not offload)319_check_nsim_vid(cfg, MACSEC_VLAN_VID, not offload)320vlan_name, remote_ip = _setup_vlan_ips(cfg, name, MACSEC_VLAN_VID)321_setup_macsec_sa(cfg, name)322# nsim doesn't handle the data path for offloaded macsec, so skip323# the ping when the final state is offloaded on nsim.324if offload or not cfg.get_local_nsim_dev():325cmd(f"ping -I {vlan_name} -c 1 -W 5 {remote_ip}")326327328def main() -> None:329"""Main program."""330with NetDrvEpEnv(__file__) as cfg:331ksft_run([test_offload_api,332test_max_secy,333test_max_sc,334test_offload_state,335test_vlan,336test_vlan_toggle,337], args=(cfg,))338ksft_exit()339340341if __name__ == "__main__":342main()343344345