Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/drivers/net/psp.py
29270 views
1
#!/usr/bin/env python3
2
# SPDX-License-Identifier: GPL-2.0
3
4
"""Test suite for PSP capable drivers."""
5
6
import errno
7
import fcntl
8
import socket
9
import struct
10
import termios
11
import time
12
13
from lib.py import defer
14
from lib.py import ksft_run, ksft_exit, ksft_pr
15
from lib.py import ksft_true, ksft_eq, ksft_ne, ksft_gt, ksft_raises
16
from lib.py import ksft_not_none
17
from lib.py import KsftSkipEx
18
from lib.py import NetDrvEpEnv, PSPFamily, NlError
19
from lib.py import bkg, rand_port, wait_port_listen
20
21
22
def _get_outq(s):
23
one = b'\0' * 4
24
outq = fcntl.ioctl(s.fileno(), termios.TIOCOUTQ, one)
25
return struct.unpack("I", outq)[0]
26
27
28
def _send_with_ack(cfg, msg):
29
cfg.comm_sock.send(msg)
30
response = cfg.comm_sock.recv(4)
31
if response != b'ack\0':
32
raise RuntimeError("Unexpected server response", response)
33
34
35
def _remote_read_len(cfg):
36
cfg.comm_sock.send(b'read len\0')
37
return int(cfg.comm_sock.recv(1024)[:-1].decode('utf-8'))
38
39
40
def _make_clr_conn(cfg, ipver=None):
41
_send_with_ack(cfg, b'conn clr\0')
42
remote_addr = cfg.remote_addr_v[ipver] if ipver else cfg.remote_addr
43
s = socket.create_connection((remote_addr, cfg.comm_port), )
44
return s
45
46
47
def _make_psp_conn(cfg, version=0, ipver=None):
48
_send_with_ack(cfg, b'conn psp\0' + struct.pack('BB', version, version))
49
remote_addr = cfg.remote_addr_v[ipver] if ipver else cfg.remote_addr
50
s = socket.create_connection((remote_addr, cfg.comm_port), )
51
return s
52
53
54
def _close_conn(cfg, s):
55
_send_with_ack(cfg, b'data close\0')
56
s.close()
57
58
59
def _close_psp_conn(cfg, s):
60
_close_conn(cfg, s)
61
62
63
def _spi_xchg(s, rx):
64
s.send(struct.pack('I', rx['spi']) + rx['key'])
65
tx = s.recv(4 + len(rx['key']))
66
return {
67
'spi': struct.unpack('I', tx[:4])[0],
68
'key': tx[4:]
69
}
70
71
72
def _send_careful(cfg, s, rounds):
73
data = b'0123456789' * 200
74
for i in range(rounds):
75
n = 0
76
for _ in range(10): # allow 10 retries
77
try:
78
n += s.send(data[n:], socket.MSG_DONTWAIT)
79
if n == len(data):
80
break
81
except BlockingIOError:
82
time.sleep(0.05)
83
else:
84
rlen = _remote_read_len(cfg)
85
outq = _get_outq(s)
86
report = f'sent: {i * len(data) + n} remote len: {rlen} outq: {outq}'
87
raise RuntimeError(report)
88
89
return len(data) * rounds
90
91
92
def _check_data_rx(cfg, exp_len):
93
read_len = -1
94
for _ in range(30):
95
cfg.comm_sock.send(b'read len\0')
96
read_len = int(cfg.comm_sock.recv(1024)[:-1].decode('utf-8'))
97
if read_len == exp_len:
98
break
99
time.sleep(0.01)
100
ksft_eq(read_len, exp_len)
101
102
103
def _check_data_outq(s, exp_len, force_wait=False):
104
outq = 0
105
for _ in range(10):
106
outq = _get_outq(s)
107
if not force_wait and outq == exp_len:
108
break
109
time.sleep(0.01)
110
ksft_eq(outq, exp_len)
111
112
#
113
# Test case boiler plate
114
#
115
116
def _init_psp_dev(cfg):
117
if not hasattr(cfg, 'psp_dev_id'):
118
# Figure out which local device we are testing against
119
for dev in cfg.pspnl.dev_get({}, dump=True):
120
if dev['ifindex'] == cfg.ifindex:
121
cfg.psp_info = dev
122
cfg.psp_dev_id = cfg.psp_info['id']
123
break
124
else:
125
raise KsftSkipEx("No PSP devices found")
126
127
# Enable PSP if necessary
128
cap = cfg.psp_info['psp-versions-cap']
129
ena = cfg.psp_info['psp-versions-ena']
130
if cap != ena:
131
cfg.pspnl.dev_set({'id': cfg.psp_dev_id, 'psp-versions-ena': cap})
132
defer(cfg.pspnl.dev_set, {'id': cfg.psp_dev_id,
133
'psp-versions-ena': ena })
134
135
#
136
# Test cases
137
#
138
139
def dev_list_devices(cfg):
140
""" Dump all devices """
141
_init_psp_dev(cfg)
142
143
devices = cfg.pspnl.dev_get({}, dump=True)
144
145
found = False
146
for dev in devices:
147
found |= dev['id'] == cfg.psp_dev_id
148
ksft_true(found)
149
150
151
def dev_get_device(cfg):
152
""" Get the device we intend to use """
153
_init_psp_dev(cfg)
154
155
dev = cfg.pspnl.dev_get({'id': cfg.psp_dev_id})
156
ksft_eq(dev['id'], cfg.psp_dev_id)
157
158
159
def dev_get_device_bad(cfg):
160
""" Test getting device which doesn't exist """
161
raised = False
162
try:
163
cfg.pspnl.dev_get({'id': 1234567})
164
except NlError as e:
165
ksft_eq(e.nl_msg.error, -errno.ENODEV)
166
raised = True
167
ksft_true(raised)
168
169
170
def dev_rotate(cfg):
171
""" Test key rotation """
172
_init_psp_dev(cfg)
173
174
rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})
175
ksft_eq(rot['id'], cfg.psp_dev_id)
176
rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})
177
ksft_eq(rot['id'], cfg.psp_dev_id)
178
179
180
def dev_rotate_spi(cfg):
181
""" Test key rotation and SPI check """
182
_init_psp_dev(cfg)
183
184
top_a = top_b = 0
185
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
186
assoc_a = cfg.pspnl.rx_assoc({"version": 0,
187
"dev-id": cfg.psp_dev_id,
188
"sock-fd": s.fileno()})
189
top_a = assoc_a['rx-key']['spi'] >> 31
190
s.close()
191
rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})
192
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
193
ksft_eq(rot['id'], cfg.psp_dev_id)
194
assoc_b = cfg.pspnl.rx_assoc({"version": 0,
195
"dev-id": cfg.psp_dev_id,
196
"sock-fd": s.fileno()})
197
top_b = assoc_b['rx-key']['spi'] >> 31
198
s.close()
199
ksft_ne(top_a, top_b)
200
201
202
def assoc_basic(cfg):
203
""" Test creating associations """
204
_init_psp_dev(cfg)
205
206
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
207
assoc = cfg.pspnl.rx_assoc({"version": 0,
208
"dev-id": cfg.psp_dev_id,
209
"sock-fd": s.fileno()})
210
ksft_eq(assoc['dev-id'], cfg.psp_dev_id)
211
ksft_gt(assoc['rx-key']['spi'], 0)
212
ksft_eq(len(assoc['rx-key']['key']), 16)
213
214
assoc = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
215
"version": 0,
216
"tx-key": assoc['rx-key'],
217
"sock-fd": s.fileno()})
218
ksft_eq(len(assoc), 0)
219
s.close()
220
221
222
def assoc_bad_dev(cfg):
223
""" Test creating associations with bad device ID """
224
_init_psp_dev(cfg)
225
226
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
227
with ksft_raises(NlError) as cm:
228
cfg.pspnl.rx_assoc({"version": 0,
229
"dev-id": cfg.psp_dev_id + 1234567,
230
"sock-fd": s.fileno()})
231
ksft_eq(cm.exception.nl_msg.error, -errno.ENODEV)
232
233
234
def assoc_sk_only_conn(cfg):
235
""" Test creating associations based on socket """
236
_init_psp_dev(cfg)
237
238
with _make_clr_conn(cfg) as s:
239
assoc = cfg.pspnl.rx_assoc({"version": 0,
240
"sock-fd": s.fileno()})
241
ksft_eq(assoc['dev-id'], cfg.psp_dev_id)
242
cfg.pspnl.tx_assoc({"version": 0,
243
"tx-key": assoc['rx-key'],
244
"sock-fd": s.fileno()})
245
_close_conn(cfg, s)
246
247
248
def assoc_sk_only_mismatch(cfg):
249
""" Test creating associations based on socket (dev mismatch) """
250
_init_psp_dev(cfg)
251
252
with _make_clr_conn(cfg) as s:
253
with ksft_raises(NlError) as cm:
254
cfg.pspnl.rx_assoc({"version": 0,
255
"dev-id": cfg.psp_dev_id + 1234567,
256
"sock-fd": s.fileno()})
257
the_exception = cm.exception
258
ksft_eq(the_exception.nl_msg.extack['bad-attr'], ".dev-id")
259
ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)
260
261
262
def assoc_sk_only_mismatch_tx(cfg):
263
""" Test creating associations based on socket (dev mismatch) """
264
_init_psp_dev(cfg)
265
266
with _make_clr_conn(cfg) as s:
267
with ksft_raises(NlError) as cm:
268
assoc = cfg.pspnl.rx_assoc({"version": 0,
269
"sock-fd": s.fileno()})
270
cfg.pspnl.tx_assoc({"version": 0,
271
"tx-key": assoc['rx-key'],
272
"dev-id": cfg.psp_dev_id + 1234567,
273
"sock-fd": s.fileno()})
274
the_exception = cm.exception
275
ksft_eq(the_exception.nl_msg.extack['bad-attr'], ".dev-id")
276
ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)
277
278
279
def assoc_sk_only_unconn(cfg):
280
""" Test creating associations based on socket (unconnected, should fail) """
281
_init_psp_dev(cfg)
282
283
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
284
with ksft_raises(NlError) as cm:
285
cfg.pspnl.rx_assoc({"version": 0,
286
"sock-fd": s.fileno()})
287
the_exception = cm.exception
288
ksft_eq(the_exception.nl_msg.extack['miss-type'], "dev-id")
289
ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)
290
291
292
def assoc_version_mismatch(cfg):
293
""" Test creating associations where Rx and Tx PSP versions do not match """
294
_init_psp_dev(cfg)
295
296
versions = list(cfg.psp_info['psp-versions-cap'])
297
if len(versions) < 2:
298
raise KsftSkipEx("Not enough PSP versions supported by the device for the test")
299
300
# Translate versions to integers
301
versions = [cfg.pspnl.consts["version"].entries[v].value for v in versions]
302
303
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
304
rx = cfg.pspnl.rx_assoc({"version": versions[0],
305
"dev-id": cfg.psp_dev_id,
306
"sock-fd": s.fileno()})
307
308
for version in versions[1:]:
309
with ksft_raises(NlError) as cm:
310
cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
311
"version": version,
312
"tx-key": rx['rx-key'],
313
"sock-fd": s.fileno()})
314
the_exception = cm.exception
315
ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)
316
317
318
def assoc_twice(cfg):
319
""" Test reusing Tx assoc for two sockets """
320
_init_psp_dev(cfg)
321
322
def rx_assoc_check(s):
323
assoc = cfg.pspnl.rx_assoc({"version": 0,
324
"dev-id": cfg.psp_dev_id,
325
"sock-fd": s.fileno()})
326
ksft_eq(assoc['dev-id'], cfg.psp_dev_id)
327
ksft_gt(assoc['rx-key']['spi'], 0)
328
ksft_eq(len(assoc['rx-key']['key']), 16)
329
330
return assoc
331
332
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
333
assoc = rx_assoc_check(s)
334
tx = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
335
"version": 0,
336
"tx-key": assoc['rx-key'],
337
"sock-fd": s.fileno()})
338
ksft_eq(len(tx), 0)
339
340
# Use the same Tx assoc second time
341
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s2:
342
rx_assoc_check(s2)
343
tx = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
344
"version": 0,
345
"tx-key": assoc['rx-key'],
346
"sock-fd": s2.fileno()})
347
ksft_eq(len(tx), 0)
348
349
s.close()
350
351
352
def _data_basic_send(cfg, version, ipver):
353
""" Test basic data send """
354
_init_psp_dev(cfg)
355
356
# Version 0 is required by spec, don't let it skip
357
if version:
358
name = cfg.pspnl.consts["version"].entries_by_val[version].name
359
if name not in cfg.psp_info['psp-versions-cap']:
360
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
361
with ksft_raises(NlError) as cm:
362
cfg.pspnl.rx_assoc({"version": version,
363
"dev-id": cfg.psp_dev_id,
364
"sock-fd": s.fileno()})
365
ksft_eq(cm.exception.nl_msg.error, -errno.EOPNOTSUPP)
366
raise KsftSkipEx("PSP version not supported", name)
367
368
s = _make_psp_conn(cfg, version, ipver)
369
370
rx_assoc = cfg.pspnl.rx_assoc({"version": version,
371
"dev-id": cfg.psp_dev_id,
372
"sock-fd": s.fileno()})
373
rx = rx_assoc['rx-key']
374
tx = _spi_xchg(s, rx)
375
376
cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
377
"version": version,
378
"tx-key": tx,
379
"sock-fd": s.fileno()})
380
381
data_len = _send_careful(cfg, s, 100)
382
_check_data_rx(cfg, data_len)
383
_close_psp_conn(cfg, s)
384
385
386
def __bad_xfer_do(cfg, s, tx, version='hdr0-aes-gcm-128'):
387
# Make sure we accept the ACK for the SPI before we seal with the bad assoc
388
_check_data_outq(s, 0)
389
390
cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
391
"version": version,
392
"tx-key": tx,
393
"sock-fd": s.fileno()})
394
395
data_len = _send_careful(cfg, s, 20)
396
_check_data_outq(s, data_len, force_wait=True)
397
_check_data_rx(cfg, 0)
398
_close_psp_conn(cfg, s)
399
400
401
def data_send_bad_key(cfg):
402
""" Test send data with bad key """
403
_init_psp_dev(cfg)
404
405
s = _make_psp_conn(cfg)
406
407
rx_assoc = cfg.pspnl.rx_assoc({"version": 0,
408
"dev-id": cfg.psp_dev_id,
409
"sock-fd": s.fileno()})
410
rx = rx_assoc['rx-key']
411
tx = _spi_xchg(s, rx)
412
tx['key'] = (tx['key'][0] ^ 0xff).to_bytes(1, 'little') + tx['key'][1:]
413
__bad_xfer_do(cfg, s, tx)
414
415
416
def data_send_disconnect(cfg):
417
""" Test socket close after sending data """
418
_init_psp_dev(cfg)
419
420
with _make_psp_conn(cfg) as s:
421
assoc = cfg.pspnl.rx_assoc({"version": 0,
422
"sock-fd": s.fileno()})
423
tx = _spi_xchg(s, assoc['rx-key'])
424
cfg.pspnl.tx_assoc({"version": 0,
425
"tx-key": tx,
426
"sock-fd": s.fileno()})
427
428
data_len = _send_careful(cfg, s, 100)
429
_check_data_rx(cfg, data_len)
430
431
s.shutdown(socket.SHUT_RDWR)
432
s.close()
433
434
435
def _data_mss_adjust(cfg, ipver):
436
_init_psp_dev(cfg)
437
438
# First figure out what the MSS would be without any adjustments
439
s = _make_clr_conn(cfg, ipver)
440
s.send(b"0123456789abcdef" * 1024)
441
_check_data_rx(cfg, 16 * 1024)
442
mss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)
443
_close_conn(cfg, s)
444
445
s = _make_psp_conn(cfg, 0, ipver)
446
try:
447
rx_assoc = cfg.pspnl.rx_assoc({"version": 0,
448
"dev-id": cfg.psp_dev_id,
449
"sock-fd": s.fileno()})
450
rx = rx_assoc['rx-key']
451
tx = _spi_xchg(s, rx)
452
453
rxmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)
454
ksft_eq(mss, rxmss)
455
456
cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
457
"version": 0,
458
"tx-key": tx,
459
"sock-fd": s.fileno()})
460
461
txmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)
462
ksft_eq(mss, txmss + 40)
463
464
data_len = _send_careful(cfg, s, 100)
465
_check_data_rx(cfg, data_len)
466
_check_data_outq(s, 0)
467
468
txmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)
469
ksft_eq(mss, txmss + 40)
470
finally:
471
_close_psp_conn(cfg, s)
472
473
474
def data_stale_key(cfg):
475
""" Test send on a double-rotated key """
476
_init_psp_dev(cfg)
477
478
s = _make_psp_conn(cfg)
479
try:
480
rx_assoc = cfg.pspnl.rx_assoc({"version": 0,
481
"dev-id": cfg.psp_dev_id,
482
"sock-fd": s.fileno()})
483
rx = rx_assoc['rx-key']
484
tx = _spi_xchg(s, rx)
485
486
cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
487
"version": 0,
488
"tx-key": tx,
489
"sock-fd": s.fileno()})
490
491
data_len = _send_careful(cfg, s, 100)
492
_check_data_rx(cfg, data_len)
493
_check_data_outq(s, 0)
494
495
cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})
496
cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})
497
498
s.send(b'0123456789' * 200)
499
_check_data_outq(s, 2000, force_wait=True)
500
finally:
501
_close_psp_conn(cfg, s)
502
503
504
def __nsim_psp_rereg(cfg):
505
# The PSP dev ID will change, remember what was there before
506
before = set([x['id'] for x in cfg.pspnl.dev_get({}, dump=True)])
507
508
cfg._ns.nsims[0].dfs_write('psp_rereg', '1')
509
510
after = set([x['id'] for x in cfg.pspnl.dev_get({}, dump=True)])
511
512
new_devs = list(after - before)
513
ksft_eq(len(new_devs), 1)
514
cfg.psp_dev_id = list(after - before)[0]
515
516
517
def removal_device_rx(cfg):
518
""" Test removing a netdev / PSD with active Rx assoc """
519
520
# We could technically devlink reload real devices, too
521
# but that kills the control socket. So test this on
522
# netdevsim only for now
523
cfg.require_nsim()
524
525
s = _make_clr_conn(cfg)
526
try:
527
rx_assoc = cfg.pspnl.rx_assoc({"version": 0,
528
"dev-id": cfg.psp_dev_id,
529
"sock-fd": s.fileno()})
530
ksft_not_none(rx_assoc)
531
532
__nsim_psp_rereg(cfg)
533
finally:
534
_close_conn(cfg, s)
535
536
537
def removal_device_bi(cfg):
538
""" Test removing a netdev / PSD with active Rx/Tx assoc """
539
540
# We could technically devlink reload real devices, too
541
# but that kills the control socket. So test this on
542
# netdevsim only for now
543
cfg.require_nsim()
544
545
s = _make_clr_conn(cfg)
546
try:
547
rx_assoc = cfg.pspnl.rx_assoc({"version": 0,
548
"dev-id": cfg.psp_dev_id,
549
"sock-fd": s.fileno()})
550
cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
551
"version": 0,
552
"tx-key": rx_assoc['rx-key'],
553
"sock-fd": s.fileno()})
554
__nsim_psp_rereg(cfg)
555
finally:
556
_close_conn(cfg, s)
557
558
559
def psp_ip_ver_test_builder(name, test_func, psp_ver, ipver):
560
"""Build test cases for each combo of PSP version and IP version"""
561
def test_case(cfg):
562
cfg.require_ipver(ipver)
563
test_case.__name__ = f"{name}_v{psp_ver}_ip{ipver}"
564
test_func(cfg, psp_ver, ipver)
565
return test_case
566
567
568
def ipver_test_builder(name, test_func, ipver):
569
"""Build test cases for each IP version"""
570
def test_case(cfg):
571
cfg.require_ipver(ipver)
572
test_case.__name__ = f"{name}_ip{ipver}"
573
test_func(cfg, ipver)
574
return test_case
575
576
577
def main() -> None:
578
""" Ksft boiler plate main """
579
580
with NetDrvEpEnv(__file__) as cfg:
581
cfg.pspnl = PSPFamily()
582
583
# Set up responder and communication sock
584
responder = cfg.remote.deploy("psp_responder")
585
586
cfg.comm_port = rand_port()
587
srv = None
588
try:
589
with bkg(responder + f" -p {cfg.comm_port}", host=cfg.remote,
590
exit_wait=True) as srv:
591
wait_port_listen(cfg.comm_port, host=cfg.remote)
592
593
cfg.comm_sock = socket.create_connection((cfg.remote_addr,
594
cfg.comm_port),
595
timeout=1)
596
597
cases = [
598
psp_ip_ver_test_builder(
599
"data_basic_send", _data_basic_send, version, ipver
600
)
601
for version in range(0, 4)
602
for ipver in ("4", "6")
603
]
604
cases += [
605
ipver_test_builder("data_mss_adjust", _data_mss_adjust, ipver)
606
for ipver in ("4", "6")
607
]
608
609
ksft_run(cases=cases, globs=globals(),
610
case_pfx={"dev_", "data_", "assoc_", "removal_"},
611
args=(cfg, ))
612
613
cfg.comm_sock.send(b"exit\0")
614
cfg.comm_sock.close()
615
finally:
616
if srv and (srv.stdout or srv.stderr):
617
ksft_pr("")
618
ksft_pr(f"Responder logs ({srv.ret}):")
619
if srv and srv.stdout:
620
ksft_pr("STDOUT:\n# " + srv.stdout.strip().replace("\n", "\n# "))
621
if srv and srv.stderr:
622
ksft_pr("STDERR:\n# " + srv.stderr.strip().replace("\n", "\n# "))
623
ksft_exit()
624
625
626
if __name__ == "__main__":
627
main()
628
629