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