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