Skip to content

Commit 8f90dc6

Browse files
kuba-mooPaolo Abeni
authored andcommitted
selftests: drv-net: psp: add basic data transfer and key rotation tests
Add basic tests for sending data over PSP and making sure that key rotation toggles the MSB of the spi. Deploy PSP responder on the remote end. We also need a healthy dose of common helpers for setting up the connections, assertions and interrogating socket state on the Python side. Signed-off-by: Jakub Kicinski <[email protected]> Signed-off-by: Daniel Zahka <[email protected]> Link: https://patch.msgid.link/[email protected] Reviewed-by: Willem de Bruijn <[email protected]> Signed-off-by: Paolo Abeni <[email protected]>
1 parent 2aeb71b commit 8f90dc6

File tree

1 file changed

+191
-3
lines changed
  • tools/testing/selftests/drivers/net

1 file changed

+191
-3
lines changed

tools/testing/selftests/drivers/net/psp.py

Lines changed: 191 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,92 @@
44
"""Test suite for PSP capable drivers."""
55

66
import errno
7+
import fcntl
8+
import socket
9+
import struct
10+
import termios
11+
import time
712

813
from lib.py import defer
9-
from lib.py import ksft_run, ksft_exit
10-
from lib.py import ksft_true, ksft_eq
14+
from lib.py import ksft_run, ksft_exit, ksft_pr
15+
from lib.py import ksft_true, ksft_eq, ksft_ne, ksft_raises
1116
from lib.py import KsftSkipEx
1217
from lib.py import NetDrvEpEnv, PSPFamily, NlError
18+
from lib.py import bkg, rand_port, wait_port_listen
19+
20+
21+
def _get_outq(s):
22+
one = b'\0' * 4
23+
outq = fcntl.ioctl(s.fileno(), termios.TIOCOUTQ, one)
24+
return struct.unpack("I", outq)[0]
25+
26+
27+
def _send_with_ack(cfg, msg):
28+
cfg.comm_sock.send(msg)
29+
response = cfg.comm_sock.recv(4)
30+
if response != b'ack\0':
31+
raise RuntimeError("Unexpected server response", response)
32+
33+
34+
def _remote_read_len(cfg):
35+
cfg.comm_sock.send(b'read len\0')
36+
return int(cfg.comm_sock.recv(1024)[:-1].decode('utf-8'))
37+
38+
39+
def _make_psp_conn(cfg, version=0, ipver=None):
40+
_send_with_ack(cfg, b'conn psp\0' + struct.pack('BB', version, version))
41+
remote_addr = cfg.remote_addr_v[ipver] if ipver else cfg.remote_addr
42+
s = socket.create_connection((remote_addr, cfg.comm_port), )
43+
return s
44+
45+
46+
def _close_conn(cfg, s):
47+
_send_with_ack(cfg, b'data close\0')
48+
s.close()
49+
50+
51+
def _close_psp_conn(cfg, s):
52+
_close_conn(cfg, s)
53+
54+
55+
def _spi_xchg(s, rx):
56+
s.send(struct.pack('I', rx['spi']) + rx['key'])
57+
tx = s.recv(4 + len(rx['key']))
58+
return {
59+
'spi': struct.unpack('I', tx[:4])[0],
60+
'key': tx[4:]
61+
}
62+
63+
64+
def _send_careful(cfg, s, rounds):
65+
data = b'0123456789' * 200
66+
for i in range(rounds):
67+
n = 0
68+
for _ in range(10): # allow 10 retries
69+
try:
70+
n += s.send(data[n:], socket.MSG_DONTWAIT)
71+
if n == len(data):
72+
break
73+
except BlockingIOError:
74+
time.sleep(0.05)
75+
else:
76+
rlen = _remote_read_len(cfg)
77+
outq = _get_outq(s)
78+
report = f'sent: {i * len(data) + n} remote len: {rlen} outq: {outq}'
79+
raise RuntimeError(report)
80+
81+
return len(data) * rounds
82+
83+
84+
def _check_data_rx(cfg, exp_len):
85+
read_len = -1
86+
for _ in range(30):
87+
cfg.comm_sock.send(b'read len\0')
88+
read_len = int(cfg.comm_sock.recv(1024)[:-1].decode('utf-8'))
89+
if read_len == exp_len:
90+
break
91+
time.sleep(0.01)
92+
ksft_eq(read_len, exp_len)
1393

1494
#
1595
# Test case boiler plate
@@ -69,13 +149,121 @@ def dev_get_device_bad(cfg):
69149
ksft_true(raised)
70150

71151

152+
def dev_rotate(cfg):
153+
""" Test key rotation """
154+
_init_psp_dev(cfg)
155+
156+
rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})
157+
ksft_eq(rot['id'], cfg.psp_dev_id)
158+
rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})
159+
ksft_eq(rot['id'], cfg.psp_dev_id)
160+
161+
162+
def dev_rotate_spi(cfg):
163+
""" Test key rotation and SPI check """
164+
_init_psp_dev(cfg)
165+
166+
top_a = top_b = 0
167+
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
168+
assoc_a = cfg.pspnl.rx_assoc({"version": 0,
169+
"dev-id": cfg.psp_dev_id,
170+
"sock-fd": s.fileno()})
171+
top_a = assoc_a['rx-key']['spi'] >> 31
172+
s.close()
173+
rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})
174+
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
175+
ksft_eq(rot['id'], cfg.psp_dev_id)
176+
assoc_b = cfg.pspnl.rx_assoc({"version": 0,
177+
"dev-id": cfg.psp_dev_id,
178+
"sock-fd": s.fileno()})
179+
top_b = assoc_b['rx-key']['spi'] >> 31
180+
s.close()
181+
ksft_ne(top_a, top_b)
182+
183+
184+
def _data_basic_send(cfg, version, ipver):
185+
""" Test basic data send """
186+
_init_psp_dev(cfg)
187+
188+
# Version 0 is required by spec, don't let it skip
189+
if version:
190+
name = cfg.pspnl.consts["version"].entries_by_val[version].name
191+
if name not in cfg.psp_info['psp-versions-cap']:
192+
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
193+
with ksft_raises(NlError) as cm:
194+
cfg.pspnl.rx_assoc({"version": version,
195+
"dev-id": cfg.psp_dev_id,
196+
"sock-fd": s.fileno()})
197+
ksft_eq(cm.exception.nl_msg.error, -errno.EOPNOTSUPP)
198+
raise KsftSkipEx("PSP version not supported", name)
199+
200+
s = _make_psp_conn(cfg, version, ipver)
201+
202+
rx_assoc = cfg.pspnl.rx_assoc({"version": version,
203+
"dev-id": cfg.psp_dev_id,
204+
"sock-fd": s.fileno()})
205+
rx = rx_assoc['rx-key']
206+
tx = _spi_xchg(s, rx)
207+
208+
cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
209+
"version": version,
210+
"tx-key": tx,
211+
"sock-fd": s.fileno()})
212+
213+
data_len = _send_careful(cfg, s, 100)
214+
_check_data_rx(cfg, data_len)
215+
_close_psp_conn(cfg, s)
216+
217+
218+
def psp_ip_ver_test_builder(name, test_func, psp_ver, ipver):
219+
"""Build test cases for each combo of PSP version and IP version"""
220+
def test_case(cfg):
221+
cfg.require_ipver(ipver)
222+
test_case.__name__ = f"{name}_v{psp_ver}_ip{ipver}"
223+
test_func(cfg, psp_ver, ipver)
224+
return test_case
225+
226+
72227
def main() -> None:
73228
""" Ksft boiler plate main """
74229

75230
with NetDrvEpEnv(__file__) as cfg:
76231
cfg.pspnl = PSPFamily()
77232

78-
ksft_run(globs=globals(), case_pfx={"dev_",}, args=(cfg, ))
233+
# Set up responder and communication sock
234+
responder = cfg.remote.deploy("psp_responder")
235+
236+
cfg.comm_port = rand_port()
237+
srv = None
238+
try:
239+
with bkg(responder + f" -p {cfg.comm_port}", host=cfg.remote,
240+
exit_wait=True) as srv:
241+
wait_port_listen(cfg.comm_port, host=cfg.remote)
242+
243+
cfg.comm_sock = socket.create_connection((cfg.remote_addr,
244+
cfg.comm_port),
245+
timeout=1)
246+
247+
cases = [
248+
psp_ip_ver_test_builder(
249+
"data_basic_send", _data_basic_send, version, ipver
250+
)
251+
for version in range(0, 4)
252+
for ipver in ("4", "6")
253+
]
254+
255+
ksft_run(cases=cases, globs=globals(), case_pfx={"dev_",}, args=(cfg, ))
256+
257+
cfg.comm_sock.send(b"exit\0")
258+
cfg.comm_sock.close()
259+
finally:
260+
if srv and (srv.stdout or srv.stderr):
261+
ksft_pr("")
262+
ksft_pr(f"Responder logs ({srv.ret}):")
263+
if srv and srv.stdout:
264+
ksft_pr("STDOUT:\n# " + srv.stdout.strip().replace("\n", "\n# "))
265+
if srv and srv.stderr:
266+
ksft_pr("STDERR:\n# " + srv.stderr.strip().replace("\n", "\n# "))
79267
ksft_exit()
80268

81269

0 commit comments

Comments
 (0)