Path: blob/master/tools/testing/selftests/drivers/net/psp.py
49639 views
#!/usr/bin/env python31# SPDX-License-Identifier: GPL-2.023"""Test suite for PSP capable drivers."""45import errno6import fcntl7import socket8import struct9import termios10import time1112from lib.py import defer13from lib.py import ksft_run, ksft_exit, ksft_pr14from lib.py import ksft_true, ksft_eq, ksft_ne, ksft_gt, ksft_raises15from lib.py import ksft_not_none16from lib.py import KsftSkipEx17from lib.py import NetDrvEpEnv, PSPFamily, NlError18from lib.py import bkg, rand_port, wait_port_listen192021def _get_outq(s):22one = b'\0' * 423outq = fcntl.ioctl(s.fileno(), termios.TIOCOUTQ, one)24return struct.unpack("I", outq)[0]252627def _send_with_ack(cfg, msg):28cfg.comm_sock.send(msg)29response = cfg.comm_sock.recv(4)30if response != b'ack\0':31raise RuntimeError("Unexpected server response", response)323334def _remote_read_len(cfg):35cfg.comm_sock.send(b'read len\0')36return int(cfg.comm_sock.recv(1024)[:-1].decode('utf-8'))373839def _make_clr_conn(cfg, ipver=None):40_send_with_ack(cfg, b'conn clr\0')41remote_addr = cfg.remote_addr_v[ipver] if ipver else cfg.remote_addr42s = socket.create_connection((remote_addr, cfg.comm_port), )43return s444546def _make_psp_conn(cfg, version=0, ipver=None):47_send_with_ack(cfg, b'conn psp\0' + struct.pack('BB', version, version))48remote_addr = cfg.remote_addr_v[ipver] if ipver else cfg.remote_addr49s = socket.create_connection((remote_addr, cfg.comm_port), )50return s515253def _close_conn(cfg, s):54_send_with_ack(cfg, b'data close\0')55s.close()565758def _close_psp_conn(cfg, s):59_close_conn(cfg, s)606162def _spi_xchg(s, rx):63s.send(struct.pack('I', rx['spi']) + rx['key'])64tx = s.recv(4 + len(rx['key']))65return {66'spi': struct.unpack('I', tx[:4])[0],67'key': tx[4:]68}697071def _send_careful(cfg, s, rounds):72data = b'0123456789' * 20073for i in range(rounds):74n = 075for _ in range(10): # allow 10 retries76try:77n += s.send(data[n:], socket.MSG_DONTWAIT)78if n == len(data):79break80except BlockingIOError:81time.sleep(0.05)82else:83rlen = _remote_read_len(cfg)84outq = _get_outq(s)85report = f'sent: {i * len(data) + n} remote len: {rlen} outq: {outq}'86raise RuntimeError(report)8788return len(data) * rounds899091def _check_data_rx(cfg, exp_len):92read_len = -193for _ in range(30):94cfg.comm_sock.send(b'read len\0')95read_len = int(cfg.comm_sock.recv(1024)[:-1].decode('utf-8'))96if read_len == exp_len:97break98time.sleep(0.01)99ksft_eq(read_len, exp_len)100101102def _check_data_outq(s, exp_len, force_wait=False):103outq = 0104for _ in range(10):105outq = _get_outq(s)106if not force_wait and outq == exp_len:107break108time.sleep(0.01)109ksft_eq(outq, exp_len)110111112def _get_stat(cfg, key):113return cfg.pspnl.get_stats({'dev-id': cfg.psp_dev_id})[key]114115#116# Test case boiler plate117#118119def _init_psp_dev(cfg):120if not hasattr(cfg, 'psp_dev_id'):121# Figure out which local device we are testing against122for dev in cfg.pspnl.dev_get({}, dump=True):123if dev['ifindex'] == cfg.ifindex:124cfg.psp_info = dev125cfg.psp_dev_id = cfg.psp_info['id']126break127else:128raise KsftSkipEx("No PSP devices found")129130# Enable PSP if necessary131cap = cfg.psp_info['psp-versions-cap']132ena = cfg.psp_info['psp-versions-ena']133if cap != ena:134cfg.pspnl.dev_set({'id': cfg.psp_dev_id, 'psp-versions-ena': cap})135defer(cfg.pspnl.dev_set, {'id': cfg.psp_dev_id,136'psp-versions-ena': ena })137138#139# Test cases140#141142def dev_list_devices(cfg):143""" Dump all devices """144_init_psp_dev(cfg)145146devices = cfg.pspnl.dev_get({}, dump=True)147148found = False149for dev in devices:150found |= dev['id'] == cfg.psp_dev_id151ksft_true(found)152153154def dev_get_device(cfg):155""" Get the device we intend to use """156_init_psp_dev(cfg)157158dev = cfg.pspnl.dev_get({'id': cfg.psp_dev_id})159ksft_eq(dev['id'], cfg.psp_dev_id)160161162def dev_get_device_bad(cfg):163""" Test getting device which doesn't exist """164raised = False165try:166cfg.pspnl.dev_get({'id': 1234567})167except NlError as e:168ksft_eq(e.nl_msg.error, -errno.ENODEV)169raised = True170ksft_true(raised)171172173def dev_rotate(cfg):174""" Test key rotation """175_init_psp_dev(cfg)176177prev_rotations = _get_stat(cfg, 'key-rotations')178179rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})180ksft_eq(rot['id'], cfg.psp_dev_id)181rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})182ksft_eq(rot['id'], cfg.psp_dev_id)183184cur_rotations = _get_stat(cfg, 'key-rotations')185ksft_eq(cur_rotations, prev_rotations + 2)186187188def dev_rotate_spi(cfg):189""" Test key rotation and SPI check """190_init_psp_dev(cfg)191192top_a = top_b = 0193with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:194assoc_a = cfg.pspnl.rx_assoc({"version": 0,195"dev-id": cfg.psp_dev_id,196"sock-fd": s.fileno()})197top_a = assoc_a['rx-key']['spi'] >> 31198s.close()199rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})200with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:201ksft_eq(rot['id'], cfg.psp_dev_id)202assoc_b = cfg.pspnl.rx_assoc({"version": 0,203"dev-id": cfg.psp_dev_id,204"sock-fd": s.fileno()})205top_b = assoc_b['rx-key']['spi'] >> 31206s.close()207ksft_ne(top_a, top_b)208209210def assoc_basic(cfg):211""" Test creating associations """212_init_psp_dev(cfg)213214with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:215assoc = cfg.pspnl.rx_assoc({"version": 0,216"dev-id": cfg.psp_dev_id,217"sock-fd": s.fileno()})218ksft_eq(assoc['dev-id'], cfg.psp_dev_id)219ksft_gt(assoc['rx-key']['spi'], 0)220ksft_eq(len(assoc['rx-key']['key']), 16)221222assoc = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,223"version": 0,224"tx-key": assoc['rx-key'],225"sock-fd": s.fileno()})226ksft_eq(len(assoc), 0)227s.close()228229230def assoc_bad_dev(cfg):231""" Test creating associations with bad device ID """232_init_psp_dev(cfg)233234with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:235with ksft_raises(NlError) as cm:236cfg.pspnl.rx_assoc({"version": 0,237"dev-id": cfg.psp_dev_id + 1234567,238"sock-fd": s.fileno()})239ksft_eq(cm.exception.nl_msg.error, -errno.ENODEV)240241242def assoc_sk_only_conn(cfg):243""" Test creating associations based on socket """244_init_psp_dev(cfg)245246with _make_clr_conn(cfg) as s:247assoc = cfg.pspnl.rx_assoc({"version": 0,248"sock-fd": s.fileno()})249ksft_eq(assoc['dev-id'], cfg.psp_dev_id)250cfg.pspnl.tx_assoc({"version": 0,251"tx-key": assoc['rx-key'],252"sock-fd": s.fileno()})253_close_conn(cfg, s)254255256def assoc_sk_only_mismatch(cfg):257""" Test creating associations based on socket (dev mismatch) """258_init_psp_dev(cfg)259260with _make_clr_conn(cfg) as s:261with ksft_raises(NlError) as cm:262cfg.pspnl.rx_assoc({"version": 0,263"dev-id": cfg.psp_dev_id + 1234567,264"sock-fd": s.fileno()})265the_exception = cm.exception266ksft_eq(the_exception.nl_msg.extack['bad-attr'], ".dev-id")267ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)268269270def assoc_sk_only_mismatch_tx(cfg):271""" Test creating associations based on socket (dev mismatch) """272_init_psp_dev(cfg)273274with _make_clr_conn(cfg) as s:275with ksft_raises(NlError) as cm:276assoc = cfg.pspnl.rx_assoc({"version": 0,277"sock-fd": s.fileno()})278cfg.pspnl.tx_assoc({"version": 0,279"tx-key": assoc['rx-key'],280"dev-id": cfg.psp_dev_id + 1234567,281"sock-fd": s.fileno()})282the_exception = cm.exception283ksft_eq(the_exception.nl_msg.extack['bad-attr'], ".dev-id")284ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)285286287def assoc_sk_only_unconn(cfg):288""" Test creating associations based on socket (unconnected, should fail) """289_init_psp_dev(cfg)290291with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:292with ksft_raises(NlError) as cm:293cfg.pspnl.rx_assoc({"version": 0,294"sock-fd": s.fileno()})295the_exception = cm.exception296ksft_eq(the_exception.nl_msg.extack['miss-type'], "dev-id")297ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)298299300def assoc_version_mismatch(cfg):301""" Test creating associations where Rx and Tx PSP versions do not match """302_init_psp_dev(cfg)303304versions = list(cfg.psp_info['psp-versions-cap'])305if len(versions) < 2:306raise KsftSkipEx("Not enough PSP versions supported by the device for the test")307308# Translate versions to integers309versions = [cfg.pspnl.consts["version"].entries[v].value for v in versions]310311with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:312rx = cfg.pspnl.rx_assoc({"version": versions[0],313"dev-id": cfg.psp_dev_id,314"sock-fd": s.fileno()})315316for version in versions[1:]:317with ksft_raises(NlError) as cm:318cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,319"version": version,320"tx-key": rx['rx-key'],321"sock-fd": s.fileno()})322the_exception = cm.exception323ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)324325326def assoc_twice(cfg):327""" Test reusing Tx assoc for two sockets """328_init_psp_dev(cfg)329330def rx_assoc_check(s):331assoc = cfg.pspnl.rx_assoc({"version": 0,332"dev-id": cfg.psp_dev_id,333"sock-fd": s.fileno()})334ksft_eq(assoc['dev-id'], cfg.psp_dev_id)335ksft_gt(assoc['rx-key']['spi'], 0)336ksft_eq(len(assoc['rx-key']['key']), 16)337338return assoc339340with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:341assoc = rx_assoc_check(s)342tx = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,343"version": 0,344"tx-key": assoc['rx-key'],345"sock-fd": s.fileno()})346ksft_eq(len(tx), 0)347348# Use the same Tx assoc second time349with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s2:350rx_assoc_check(s2)351tx = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,352"version": 0,353"tx-key": assoc['rx-key'],354"sock-fd": s2.fileno()})355ksft_eq(len(tx), 0)356357s.close()358359360def _data_basic_send(cfg, version, ipver):361""" Test basic data send """362_init_psp_dev(cfg)363364# Version 0 is required by spec, don't let it skip365if version:366name = cfg.pspnl.consts["version"].entries_by_val[version].name367if name not in cfg.psp_info['psp-versions-cap']:368with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:369with ksft_raises(NlError) as cm:370cfg.pspnl.rx_assoc({"version": version,371"dev-id": cfg.psp_dev_id,372"sock-fd": s.fileno()})373ksft_eq(cm.exception.nl_msg.error, -errno.EOPNOTSUPP)374raise KsftSkipEx("PSP version not supported", name)375376s = _make_psp_conn(cfg, version, ipver)377378rx_assoc = cfg.pspnl.rx_assoc({"version": version,379"dev-id": cfg.psp_dev_id,380"sock-fd": s.fileno()})381rx = rx_assoc['rx-key']382tx = _spi_xchg(s, rx)383384cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,385"version": version,386"tx-key": tx,387"sock-fd": s.fileno()})388389data_len = _send_careful(cfg, s, 100)390_check_data_rx(cfg, data_len)391_close_psp_conn(cfg, s)392393394def __bad_xfer_do(cfg, s, tx, version='hdr0-aes-gcm-128'):395# Make sure we accept the ACK for the SPI before we seal with the bad assoc396_check_data_outq(s, 0)397398cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,399"version": version,400"tx-key": tx,401"sock-fd": s.fileno()})402403data_len = _send_careful(cfg, s, 20)404_check_data_outq(s, data_len, force_wait=True)405_check_data_rx(cfg, 0)406_close_psp_conn(cfg, s)407408409def data_send_bad_key(cfg):410""" Test send data with bad key """411_init_psp_dev(cfg)412413s = _make_psp_conn(cfg)414415rx_assoc = cfg.pspnl.rx_assoc({"version": 0,416"dev-id": cfg.psp_dev_id,417"sock-fd": s.fileno()})418rx = rx_assoc['rx-key']419tx = _spi_xchg(s, rx)420tx['key'] = (tx['key'][0] ^ 0xff).to_bytes(1, 'little') + tx['key'][1:]421__bad_xfer_do(cfg, s, tx)422423424def data_send_disconnect(cfg):425""" Test socket close after sending data """426_init_psp_dev(cfg)427428with _make_psp_conn(cfg) as s:429assoc = cfg.pspnl.rx_assoc({"version": 0,430"sock-fd": s.fileno()})431tx = _spi_xchg(s, assoc['rx-key'])432cfg.pspnl.tx_assoc({"version": 0,433"tx-key": tx,434"sock-fd": s.fileno()})435436data_len = _send_careful(cfg, s, 100)437_check_data_rx(cfg, data_len)438439s.shutdown(socket.SHUT_RDWR)440s.close()441442443def _data_mss_adjust(cfg, ipver):444_init_psp_dev(cfg)445446# First figure out what the MSS would be without any adjustments447s = _make_clr_conn(cfg, ipver)448s.send(b"0123456789abcdef" * 1024)449_check_data_rx(cfg, 16 * 1024)450mss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)451_close_conn(cfg, s)452453s = _make_psp_conn(cfg, 0, ipver)454try:455rx_assoc = cfg.pspnl.rx_assoc({"version": 0,456"dev-id": cfg.psp_dev_id,457"sock-fd": s.fileno()})458rx = rx_assoc['rx-key']459tx = _spi_xchg(s, rx)460461rxmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)462ksft_eq(mss, rxmss)463464cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,465"version": 0,466"tx-key": tx,467"sock-fd": s.fileno()})468469txmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)470ksft_eq(mss, txmss + 40)471472data_len = _send_careful(cfg, s, 100)473_check_data_rx(cfg, data_len)474_check_data_outq(s, 0)475476txmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)477ksft_eq(mss, txmss + 40)478finally:479_close_psp_conn(cfg, s)480481482def data_stale_key(cfg):483""" Test send on a double-rotated key """484_init_psp_dev(cfg)485486prev_stale = _get_stat(cfg, 'stale-events')487s = _make_psp_conn(cfg)488try:489rx_assoc = cfg.pspnl.rx_assoc({"version": 0,490"dev-id": cfg.psp_dev_id,491"sock-fd": s.fileno()})492rx = rx_assoc['rx-key']493tx = _spi_xchg(s, rx)494495cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,496"version": 0,497"tx-key": tx,498"sock-fd": s.fileno()})499500data_len = _send_careful(cfg, s, 100)501_check_data_rx(cfg, data_len)502_check_data_outq(s, 0)503504cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})505cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})506507cur_stale = _get_stat(cfg, 'stale-events')508ksft_gt(cur_stale, prev_stale)509510s.send(b'0123456789' * 200)511_check_data_outq(s, 2000, force_wait=True)512finally:513_close_psp_conn(cfg, s)514515516def __nsim_psp_rereg(cfg):517# The PSP dev ID will change, remember what was there before518before = set([x['id'] for x in cfg.pspnl.dev_get({}, dump=True)])519520cfg._ns.nsims[0].dfs_write('psp_rereg', '1')521522after = set([x['id'] for x in cfg.pspnl.dev_get({}, dump=True)])523524new_devs = list(after - before)525ksft_eq(len(new_devs), 1)526cfg.psp_dev_id = list(after - before)[0]527528529def removal_device_rx(cfg):530""" Test removing a netdev / PSD with active Rx assoc """531532# We could technically devlink reload real devices, too533# but that kills the control socket. So test this on534# netdevsim only for now535cfg.require_nsim()536537s = _make_clr_conn(cfg)538try:539rx_assoc = cfg.pspnl.rx_assoc({"version": 0,540"dev-id": cfg.psp_dev_id,541"sock-fd": s.fileno()})542ksft_not_none(rx_assoc)543544__nsim_psp_rereg(cfg)545finally:546_close_conn(cfg, s)547548549def removal_device_bi(cfg):550""" Test removing a netdev / PSD with active Rx/Tx assoc """551552# We could technically devlink reload real devices, too553# but that kills the control socket. So test this on554# netdevsim only for now555cfg.require_nsim()556557s = _make_clr_conn(cfg)558try:559rx_assoc = cfg.pspnl.rx_assoc({"version": 0,560"dev-id": cfg.psp_dev_id,561"sock-fd": s.fileno()})562cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,563"version": 0,564"tx-key": rx_assoc['rx-key'],565"sock-fd": s.fileno()})566__nsim_psp_rereg(cfg)567finally:568_close_conn(cfg, s)569570571def psp_ip_ver_test_builder(name, test_func, psp_ver, ipver):572"""Build test cases for each combo of PSP version and IP version"""573def test_case(cfg):574cfg.require_ipver(ipver)575test_func(cfg, psp_ver, ipver)576577test_case.__name__ = f"{name}_v{psp_ver}_ip{ipver}"578return test_case579580581def ipver_test_builder(name, test_func, ipver):582"""Build test cases for each IP version"""583def test_case(cfg):584cfg.require_ipver(ipver)585test_func(cfg, ipver)586587test_case.__name__ = f"{name}_ip{ipver}"588return test_case589590591def main() -> None:592""" Ksft boiler plate main """593594with NetDrvEpEnv(__file__) as cfg:595cfg.pspnl = PSPFamily()596597# Set up responder and communication sock598responder = cfg.remote.deploy("psp_responder")599600cfg.comm_port = rand_port()601srv = None602try:603with bkg(responder + f" -p {cfg.comm_port}", host=cfg.remote,604exit_wait=True) as srv:605wait_port_listen(cfg.comm_port, host=cfg.remote)606607cfg.comm_sock = socket.create_connection((cfg.remote_addr,608cfg.comm_port),609timeout=1)610611cases = [612psp_ip_ver_test_builder(613"data_basic_send", _data_basic_send, version, ipver614)615for version in range(0, 4)616for ipver in ("4", "6")617]618cases += [619ipver_test_builder("data_mss_adjust", _data_mss_adjust, ipver)620for ipver in ("4", "6")621]622623ksft_run(cases=cases, globs=globals(),624case_pfx={"dev_", "data_", "assoc_", "removal_"},625args=(cfg, ))626627cfg.comm_sock.send(b"exit\0")628cfg.comm_sock.close()629finally:630if srv and (srv.stdout or srv.stderr):631ksft_pr("")632ksft_pr(f"Responder logs ({srv.ret}):")633if srv and srv.stdout:634ksft_pr("STDOUT:\n# " + srv.stdout.strip().replace("\n", "\n# "))635if srv and srv.stderr:636ksft_pr("STDERR:\n# " + srv.stderr.strip().replace("\n", "\n# "))637ksft_exit()638639640if __name__ == "__main__":641main()642643644