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() 645