xref: /linux/tools/testing/selftests/drivers/net/psp.py (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
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#
113# Test case boiler plate
114#
115
116def _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
139def 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
151def 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
159def 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
170def 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
180def 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
202def 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
222def 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
234def 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
248def 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
262def 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
279def 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
292def 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
318def 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
352def _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
386def __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
401def 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
416def 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
435def _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
474def 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
504def __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
517def 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
537def 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
559def 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
568def 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
577def 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
626if __name__ == "__main__":
627    main()
628