Path: blob/master/tools/testing/selftests/drivers/net/psp.py
29270 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)110111#112# Test case boiler plate113#114115def _init_psp_dev(cfg):116if not hasattr(cfg, 'psp_dev_id'):117# Figure out which local device we are testing against118for dev in cfg.pspnl.dev_get({}, dump=True):119if dev['ifindex'] == cfg.ifindex:120cfg.psp_info = dev121cfg.psp_dev_id = cfg.psp_info['id']122break123else:124raise KsftSkipEx("No PSP devices found")125126# Enable PSP if necessary127cap = cfg.psp_info['psp-versions-cap']128ena = cfg.psp_info['psp-versions-ena']129if cap != ena:130cfg.pspnl.dev_set({'id': cfg.psp_dev_id, 'psp-versions-ena': cap})131defer(cfg.pspnl.dev_set, {'id': cfg.psp_dev_id,132'psp-versions-ena': ena })133134#135# Test cases136#137138def dev_list_devices(cfg):139""" Dump all devices """140_init_psp_dev(cfg)141142devices = cfg.pspnl.dev_get({}, dump=True)143144found = False145for dev in devices:146found |= dev['id'] == cfg.psp_dev_id147ksft_true(found)148149150def dev_get_device(cfg):151""" Get the device we intend to use """152_init_psp_dev(cfg)153154dev = cfg.pspnl.dev_get({'id': cfg.psp_dev_id})155ksft_eq(dev['id'], cfg.psp_dev_id)156157158def dev_get_device_bad(cfg):159""" Test getting device which doesn't exist """160raised = False161try:162cfg.pspnl.dev_get({'id': 1234567})163except NlError as e:164ksft_eq(e.nl_msg.error, -errno.ENODEV)165raised = True166ksft_true(raised)167168169def dev_rotate(cfg):170""" Test key rotation """171_init_psp_dev(cfg)172173rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})174ksft_eq(rot['id'], cfg.psp_dev_id)175rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})176ksft_eq(rot['id'], cfg.psp_dev_id)177178179def dev_rotate_spi(cfg):180""" Test key rotation and SPI check """181_init_psp_dev(cfg)182183top_a = top_b = 0184with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:185assoc_a = cfg.pspnl.rx_assoc({"version": 0,186"dev-id": cfg.psp_dev_id,187"sock-fd": s.fileno()})188top_a = assoc_a['rx-key']['spi'] >> 31189s.close()190rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})191with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:192ksft_eq(rot['id'], cfg.psp_dev_id)193assoc_b = cfg.pspnl.rx_assoc({"version": 0,194"dev-id": cfg.psp_dev_id,195"sock-fd": s.fileno()})196top_b = assoc_b['rx-key']['spi'] >> 31197s.close()198ksft_ne(top_a, top_b)199200201def assoc_basic(cfg):202""" Test creating associations """203_init_psp_dev(cfg)204205with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:206assoc = cfg.pspnl.rx_assoc({"version": 0,207"dev-id": cfg.psp_dev_id,208"sock-fd": s.fileno()})209ksft_eq(assoc['dev-id'], cfg.psp_dev_id)210ksft_gt(assoc['rx-key']['spi'], 0)211ksft_eq(len(assoc['rx-key']['key']), 16)212213assoc = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,214"version": 0,215"tx-key": assoc['rx-key'],216"sock-fd": s.fileno()})217ksft_eq(len(assoc), 0)218s.close()219220221def assoc_bad_dev(cfg):222""" Test creating associations with bad device ID """223_init_psp_dev(cfg)224225with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:226with ksft_raises(NlError) as cm:227cfg.pspnl.rx_assoc({"version": 0,228"dev-id": cfg.psp_dev_id + 1234567,229"sock-fd": s.fileno()})230ksft_eq(cm.exception.nl_msg.error, -errno.ENODEV)231232233def assoc_sk_only_conn(cfg):234""" Test creating associations based on socket """235_init_psp_dev(cfg)236237with _make_clr_conn(cfg) as s:238assoc = cfg.pspnl.rx_assoc({"version": 0,239"sock-fd": s.fileno()})240ksft_eq(assoc['dev-id'], cfg.psp_dev_id)241cfg.pspnl.tx_assoc({"version": 0,242"tx-key": assoc['rx-key'],243"sock-fd": s.fileno()})244_close_conn(cfg, s)245246247def assoc_sk_only_mismatch(cfg):248""" Test creating associations based on socket (dev mismatch) """249_init_psp_dev(cfg)250251with _make_clr_conn(cfg) as s:252with ksft_raises(NlError) as cm:253cfg.pspnl.rx_assoc({"version": 0,254"dev-id": cfg.psp_dev_id + 1234567,255"sock-fd": s.fileno()})256the_exception = cm.exception257ksft_eq(the_exception.nl_msg.extack['bad-attr'], ".dev-id")258ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)259260261def assoc_sk_only_mismatch_tx(cfg):262""" Test creating associations based on socket (dev mismatch) """263_init_psp_dev(cfg)264265with _make_clr_conn(cfg) as s:266with ksft_raises(NlError) as cm:267assoc = cfg.pspnl.rx_assoc({"version": 0,268"sock-fd": s.fileno()})269cfg.pspnl.tx_assoc({"version": 0,270"tx-key": assoc['rx-key'],271"dev-id": cfg.psp_dev_id + 1234567,272"sock-fd": s.fileno()})273the_exception = cm.exception274ksft_eq(the_exception.nl_msg.extack['bad-attr'], ".dev-id")275ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)276277278def assoc_sk_only_unconn(cfg):279""" Test creating associations based on socket (unconnected, should fail) """280_init_psp_dev(cfg)281282with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:283with ksft_raises(NlError) as cm:284cfg.pspnl.rx_assoc({"version": 0,285"sock-fd": s.fileno()})286the_exception = cm.exception287ksft_eq(the_exception.nl_msg.extack['miss-type'], "dev-id")288ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)289290291def assoc_version_mismatch(cfg):292""" Test creating associations where Rx and Tx PSP versions do not match """293_init_psp_dev(cfg)294295versions = list(cfg.psp_info['psp-versions-cap'])296if len(versions) < 2:297raise KsftSkipEx("Not enough PSP versions supported by the device for the test")298299# Translate versions to integers300versions = [cfg.pspnl.consts["version"].entries[v].value for v in versions]301302with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:303rx = cfg.pspnl.rx_assoc({"version": versions[0],304"dev-id": cfg.psp_dev_id,305"sock-fd": s.fileno()})306307for version in versions[1:]:308with ksft_raises(NlError) as cm:309cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,310"version": version,311"tx-key": rx['rx-key'],312"sock-fd": s.fileno()})313the_exception = cm.exception314ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)315316317def assoc_twice(cfg):318""" Test reusing Tx assoc for two sockets """319_init_psp_dev(cfg)320321def rx_assoc_check(s):322assoc = cfg.pspnl.rx_assoc({"version": 0,323"dev-id": cfg.psp_dev_id,324"sock-fd": s.fileno()})325ksft_eq(assoc['dev-id'], cfg.psp_dev_id)326ksft_gt(assoc['rx-key']['spi'], 0)327ksft_eq(len(assoc['rx-key']['key']), 16)328329return assoc330331with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:332assoc = rx_assoc_check(s)333tx = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,334"version": 0,335"tx-key": assoc['rx-key'],336"sock-fd": s.fileno()})337ksft_eq(len(tx), 0)338339# Use the same Tx assoc second time340with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s2:341rx_assoc_check(s2)342tx = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,343"version": 0,344"tx-key": assoc['rx-key'],345"sock-fd": s2.fileno()})346ksft_eq(len(tx), 0)347348s.close()349350351def _data_basic_send(cfg, version, ipver):352""" Test basic data send """353_init_psp_dev(cfg)354355# Version 0 is required by spec, don't let it skip356if version:357name = cfg.pspnl.consts["version"].entries_by_val[version].name358if name not in cfg.psp_info['psp-versions-cap']:359with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:360with ksft_raises(NlError) as cm:361cfg.pspnl.rx_assoc({"version": version,362"dev-id": cfg.psp_dev_id,363"sock-fd": s.fileno()})364ksft_eq(cm.exception.nl_msg.error, -errno.EOPNOTSUPP)365raise KsftSkipEx("PSP version not supported", name)366367s = _make_psp_conn(cfg, version, ipver)368369rx_assoc = cfg.pspnl.rx_assoc({"version": version,370"dev-id": cfg.psp_dev_id,371"sock-fd": s.fileno()})372rx = rx_assoc['rx-key']373tx = _spi_xchg(s, rx)374375cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,376"version": version,377"tx-key": tx,378"sock-fd": s.fileno()})379380data_len = _send_careful(cfg, s, 100)381_check_data_rx(cfg, data_len)382_close_psp_conn(cfg, s)383384385def __bad_xfer_do(cfg, s, tx, version='hdr0-aes-gcm-128'):386# Make sure we accept the ACK for the SPI before we seal with the bad assoc387_check_data_outq(s, 0)388389cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,390"version": version,391"tx-key": tx,392"sock-fd": s.fileno()})393394data_len = _send_careful(cfg, s, 20)395_check_data_outq(s, data_len, force_wait=True)396_check_data_rx(cfg, 0)397_close_psp_conn(cfg, s)398399400def data_send_bad_key(cfg):401""" Test send data with bad key """402_init_psp_dev(cfg)403404s = _make_psp_conn(cfg)405406rx_assoc = cfg.pspnl.rx_assoc({"version": 0,407"dev-id": cfg.psp_dev_id,408"sock-fd": s.fileno()})409rx = rx_assoc['rx-key']410tx = _spi_xchg(s, rx)411tx['key'] = (tx['key'][0] ^ 0xff).to_bytes(1, 'little') + tx['key'][1:]412__bad_xfer_do(cfg, s, tx)413414415def data_send_disconnect(cfg):416""" Test socket close after sending data """417_init_psp_dev(cfg)418419with _make_psp_conn(cfg) as s:420assoc = cfg.pspnl.rx_assoc({"version": 0,421"sock-fd": s.fileno()})422tx = _spi_xchg(s, assoc['rx-key'])423cfg.pspnl.tx_assoc({"version": 0,424"tx-key": tx,425"sock-fd": s.fileno()})426427data_len = _send_careful(cfg, s, 100)428_check_data_rx(cfg, data_len)429430s.shutdown(socket.SHUT_RDWR)431s.close()432433434def _data_mss_adjust(cfg, ipver):435_init_psp_dev(cfg)436437# First figure out what the MSS would be without any adjustments438s = _make_clr_conn(cfg, ipver)439s.send(b"0123456789abcdef" * 1024)440_check_data_rx(cfg, 16 * 1024)441mss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)442_close_conn(cfg, s)443444s = _make_psp_conn(cfg, 0, ipver)445try:446rx_assoc = cfg.pspnl.rx_assoc({"version": 0,447"dev-id": cfg.psp_dev_id,448"sock-fd": s.fileno()})449rx = rx_assoc['rx-key']450tx = _spi_xchg(s, rx)451452rxmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)453ksft_eq(mss, rxmss)454455cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,456"version": 0,457"tx-key": tx,458"sock-fd": s.fileno()})459460txmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)461ksft_eq(mss, txmss + 40)462463data_len = _send_careful(cfg, s, 100)464_check_data_rx(cfg, data_len)465_check_data_outq(s, 0)466467txmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)468ksft_eq(mss, txmss + 40)469finally:470_close_psp_conn(cfg, s)471472473def data_stale_key(cfg):474""" Test send on a double-rotated key """475_init_psp_dev(cfg)476477s = _make_psp_conn(cfg)478try:479rx_assoc = cfg.pspnl.rx_assoc({"version": 0,480"dev-id": cfg.psp_dev_id,481"sock-fd": s.fileno()})482rx = rx_assoc['rx-key']483tx = _spi_xchg(s, rx)484485cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,486"version": 0,487"tx-key": tx,488"sock-fd": s.fileno()})489490data_len = _send_careful(cfg, s, 100)491_check_data_rx(cfg, data_len)492_check_data_outq(s, 0)493494cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})495cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})496497s.send(b'0123456789' * 200)498_check_data_outq(s, 2000, force_wait=True)499finally:500_close_psp_conn(cfg, s)501502503def __nsim_psp_rereg(cfg):504# The PSP dev ID will change, remember what was there before505before = set([x['id'] for x in cfg.pspnl.dev_get({}, dump=True)])506507cfg._ns.nsims[0].dfs_write('psp_rereg', '1')508509after = set([x['id'] for x in cfg.pspnl.dev_get({}, dump=True)])510511new_devs = list(after - before)512ksft_eq(len(new_devs), 1)513cfg.psp_dev_id = list(after - before)[0]514515516def removal_device_rx(cfg):517""" Test removing a netdev / PSD with active Rx assoc """518519# We could technically devlink reload real devices, too520# but that kills the control socket. So test this on521# netdevsim only for now522cfg.require_nsim()523524s = _make_clr_conn(cfg)525try:526rx_assoc = cfg.pspnl.rx_assoc({"version": 0,527"dev-id": cfg.psp_dev_id,528"sock-fd": s.fileno()})529ksft_not_none(rx_assoc)530531__nsim_psp_rereg(cfg)532finally:533_close_conn(cfg, s)534535536def removal_device_bi(cfg):537""" Test removing a netdev / PSD with active Rx/Tx assoc """538539# We could technically devlink reload real devices, too540# but that kills the control socket. So test this on541# netdevsim only for now542cfg.require_nsim()543544s = _make_clr_conn(cfg)545try:546rx_assoc = cfg.pspnl.rx_assoc({"version": 0,547"dev-id": cfg.psp_dev_id,548"sock-fd": s.fileno()})549cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,550"version": 0,551"tx-key": rx_assoc['rx-key'],552"sock-fd": s.fileno()})553__nsim_psp_rereg(cfg)554finally:555_close_conn(cfg, s)556557558def psp_ip_ver_test_builder(name, test_func, psp_ver, ipver):559"""Build test cases for each combo of PSP version and IP version"""560def test_case(cfg):561cfg.require_ipver(ipver)562test_case.__name__ = f"{name}_v{psp_ver}_ip{ipver}"563test_func(cfg, psp_ver, ipver)564return test_case565566567def ipver_test_builder(name, test_func, ipver):568"""Build test cases for each IP version"""569def test_case(cfg):570cfg.require_ipver(ipver)571test_case.__name__ = f"{name}_ip{ipver}"572test_func(cfg, ipver)573return test_case574575576def main() -> None:577""" Ksft boiler plate main """578579with NetDrvEpEnv(__file__) as cfg:580cfg.pspnl = PSPFamily()581582# Set up responder and communication sock583responder = cfg.remote.deploy("psp_responder")584585cfg.comm_port = rand_port()586srv = None587try:588with bkg(responder + f" -p {cfg.comm_port}", host=cfg.remote,589exit_wait=True) as srv:590wait_port_listen(cfg.comm_port, host=cfg.remote)591592cfg.comm_sock = socket.create_connection((cfg.remote_addr,593cfg.comm_port),594timeout=1)595596cases = [597psp_ip_ver_test_builder(598"data_basic_send", _data_basic_send, version, ipver599)600for version in range(0, 4)601for ipver in ("4", "6")602]603cases += [604ipver_test_builder("data_mss_adjust", _data_mss_adjust, ipver)605for ipver in ("4", "6")606]607608ksft_run(cases=cases, globs=globals(),609case_pfx={"dev_", "data_", "assoc_", "removal_"},610args=(cfg, ))611612cfg.comm_sock.send(b"exit\0")613cfg.comm_sock.close()614finally:615if srv and (srv.stdout or srv.stderr):616ksft_pr("")617ksft_pr(f"Responder logs ({srv.ret}):")618if srv and srv.stdout:619ksft_pr("STDOUT:\n# " + srv.stdout.strip().replace("\n", "\n# "))620if srv and srv.stderr:621ksft_pr("STDERR:\n# " + srv.stderr.strip().replace("\n", "\n# "))622ksft_exit()623624625if __name__ == "__main__":626main()627628629