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 os 9import socket 10import struct 11import termios 12import time 13 14from lib.py import defer 15from lib.py import ksft_run, ksft_exit, ksft_pr 16from lib.py import ksft_true, ksft_eq, ksft_ne, ksft_gt, ksft_raises 17from lib.py import ksft_not_none 18from lib.py import ksft_variants, KsftNamedVariant 19from lib.py import KsftSkipEx, KsftFailEx 20from lib.py import NetDrvEpEnv, NetDrvContEnv 21from lib.py import Netlink, NlError, PSPFamily, RtnlFamily 22from lib.py import NetNSEnter 23from lib.py import bkg, rand_port, wait_port_listen 24from lib.py import ip 25 26TCP_ULP = 31 27 28 29def _get_outq(s): 30 one = b'\0' * 4 31 outq = fcntl.ioctl(s.fileno(), termios.TIOCOUTQ, one) 32 return struct.unpack("I", outq)[0] 33 34 35def _send_with_ack(cfg, msg): 36 cfg.comm_sock.send(msg) 37 response = cfg.comm_sock.recv(4) 38 if response != b'ack\0': 39 raise RuntimeError("Unexpected server response", response) 40 41 42def _remote_read_len(cfg): 43 cfg.comm_sock.send(b'read len\0') 44 return int(cfg.comm_sock.recv(1024)[:-1].decode('utf-8')) 45 46 47def _make_clr_conn(cfg, ipver=None): 48 _send_with_ack(cfg, b'conn clr\0') 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 _make_psp_conn(cfg, version=0, ipver=None): 55 _send_with_ack(cfg, b'conn psp\0' + struct.pack('BB', version, version)) 56 remote_addr = cfg.remote_addr_v[ipver] if ipver else cfg.remote_addr 57 s = socket.create_connection((remote_addr, cfg.comm_port), ) 58 return s 59 60 61def _close_conn(cfg, s): 62 _send_with_ack(cfg, b'data close\0') 63 s.close() 64 65 66def _close_psp_conn(cfg, s): 67 _close_conn(cfg, s) 68 69 70def _spi_xchg(s, rx): 71 s.send(struct.pack('I', rx['spi']) + rx['key']) 72 tx = s.recv(4 + len(rx['key'])) 73 return { 74 'spi': struct.unpack('I', tx[:4])[0], 75 'key': tx[4:] 76 } 77 78 79def _send_careful(cfg, s, rounds): 80 data = b'0123456789' * 200 81 for i in range(rounds): 82 n = 0 83 for _ in range(10): # allow 10 retries 84 try: 85 n += s.send(data[n:], socket.MSG_DONTWAIT) 86 if n == len(data): 87 break 88 except BlockingIOError: 89 time.sleep(0.05) 90 else: 91 rlen = _remote_read_len(cfg) 92 outq = _get_outq(s) 93 report = f'sent: {i * len(data) + n} remote len: {rlen} outq: {outq}' 94 raise RuntimeError(report) 95 96 return len(data) * rounds 97 98 99def _check_data_rx(cfg, exp_len): 100 read_len = -1 101 for _ in range(30): 102 cfg.comm_sock.send(b'read len\0') 103 read_len = int(cfg.comm_sock.recv(1024)[:-1].decode('utf-8')) 104 if read_len == exp_len: 105 break 106 time.sleep(0.01) 107 ksft_eq(read_len, exp_len) 108 109 110def _check_data_outq(s, exp_len, force_wait=False): 111 outq = 0 112 for _ in range(10): 113 outq = _get_outq(s) 114 if not force_wait and outq == exp_len: 115 break 116 time.sleep(0.01) 117 ksft_eq(outq, exp_len) 118 119 120def _get_stat(cfg, key): 121 return cfg.pspnl.get_stats({'dev-id': cfg.psp_dev_id})[key] 122 123# 124# Test case boiler plate 125# 126 127def _init_psp_dev(cfg, use_psp_ifindex=False): 128 if not hasattr(cfg, 'psp_dev_id'): 129 # Figure out which local device we are testing against 130 # For NetDrvContEnv: use psp_ifindex instead of ifindex 131 target_ifindex = cfg.psp_ifindex if use_psp_ifindex else cfg.ifindex 132 for dev in cfg.pspnl.dev_get({}, dump=True): 133 if dev['ifindex'] == target_ifindex: 134 cfg.psp_info = dev 135 cfg.psp_dev_id = cfg.psp_info['id'] 136 break 137 else: 138 raise KsftSkipEx("No PSP devices found") 139 140 # Enable PSP if necessary 141 cap = cfg.psp_info['psp-versions-cap'] 142 ena = cfg.psp_info['psp-versions-ena'] 143 if cap != ena: 144 cfg.pspnl.dev_set({'id': cfg.psp_dev_id, 'psp-versions-ena': cap}) 145 defer(cfg.pspnl.dev_set, {'id': cfg.psp_dev_id, 146 'psp-versions-ena': ena }) 147 148# 149# Test cases 150# 151 152def dev_list_devices(cfg): 153 """ Dump all devices """ 154 _init_psp_dev(cfg) 155 156 devices = cfg.pspnl.dev_get({}, dump=True) 157 158 found = False 159 for dev in devices: 160 found |= dev['id'] == cfg.psp_dev_id 161 ksft_true(found) 162 163 164def dev_get_device(cfg): 165 """ Get the device we intend to use """ 166 _init_psp_dev(cfg) 167 168 dev = cfg.pspnl.dev_get({'id': cfg.psp_dev_id}) 169 ksft_eq(dev['id'], cfg.psp_dev_id) 170 171 172def dev_get_device_bad(cfg): 173 """ Test getting device which doesn't exist """ 174 raised = False 175 try: 176 cfg.pspnl.dev_get({'id': 1234567}) 177 except NlError as e: 178 ksft_eq(e.nl_msg.error, -errno.ENODEV) 179 raised = True 180 ksft_true(raised) 181 182 183def dev_rotate(cfg): 184 """ Test key rotation """ 185 _init_psp_dev(cfg) 186 187 prev_rotations = _get_stat(cfg, 'key-rotations') 188 189 rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id}) 190 ksft_eq(rot['id'], cfg.psp_dev_id) 191 rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id}) 192 ksft_eq(rot['id'], cfg.psp_dev_id) 193 194 cur_rotations = _get_stat(cfg, 'key-rotations') 195 ksft_eq(cur_rotations, prev_rotations + 2) 196 197 198def dev_rotate_spi(cfg): 199 """ Test key rotation and SPI check """ 200 _init_psp_dev(cfg) 201 202 top_a = top_b = 0 203 with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 204 assoc_a = cfg.pspnl.rx_assoc({"version": 0, 205 "dev-id": cfg.psp_dev_id, 206 "sock-fd": s.fileno()}) 207 top_a = assoc_a['rx-key']['spi'] >> 31 208 s.close() 209 rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id}) 210 with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 211 ksft_eq(rot['id'], cfg.psp_dev_id) 212 assoc_b = cfg.pspnl.rx_assoc({"version": 0, 213 "dev-id": cfg.psp_dev_id, 214 "sock-fd": s.fileno()}) 215 top_b = assoc_b['rx-key']['spi'] >> 31 216 s.close() 217 ksft_ne(top_a, top_b) 218 219 220def assoc_basic(cfg): 221 """ Test creating associations """ 222 _init_psp_dev(cfg) 223 224 with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 225 assoc = cfg.pspnl.rx_assoc({"version": 0, 226 "dev-id": cfg.psp_dev_id, 227 "sock-fd": s.fileno()}) 228 ksft_eq(assoc['dev-id'], cfg.psp_dev_id) 229 ksft_gt(assoc['rx-key']['spi'], 0) 230 ksft_eq(len(assoc['rx-key']['key']), 16) 231 232 assoc = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 233 "version": 0, 234 "tx-key": assoc['rx-key'], 235 "sock-fd": s.fileno()}) 236 ksft_eq(len(assoc), 0) 237 s.close() 238 239 240def assoc_bad_dev(cfg): 241 """ Test creating associations with bad device ID """ 242 _init_psp_dev(cfg) 243 244 with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 245 with ksft_raises(NlError) as cm: 246 cfg.pspnl.rx_assoc({"version": 0, 247 "dev-id": cfg.psp_dev_id + 1234567, 248 "sock-fd": s.fileno()}) 249 ksft_eq(cm.exception.nl_msg.error, -errno.ENODEV) 250 251 252def assoc_sk_only_conn(cfg): 253 """ Test creating associations based on socket """ 254 _init_psp_dev(cfg) 255 256 with _make_clr_conn(cfg) as s: 257 assoc = cfg.pspnl.rx_assoc({"version": 0, 258 "sock-fd": s.fileno()}) 259 ksft_eq(assoc['dev-id'], cfg.psp_dev_id) 260 cfg.pspnl.tx_assoc({"version": 0, 261 "tx-key": assoc['rx-key'], 262 "sock-fd": s.fileno()}) 263 _close_conn(cfg, s) 264 265 266def assoc_sk_only_mismatch(cfg): 267 """ Test creating associations based on socket (dev mismatch) """ 268 _init_psp_dev(cfg) 269 270 with _make_clr_conn(cfg) as s: 271 with ksft_raises(NlError) as cm: 272 cfg.pspnl.rx_assoc({"version": 0, 273 "dev-id": cfg.psp_dev_id + 1234567, 274 "sock-fd": s.fileno()}) 275 the_exception = cm.exception 276 ksft_eq(the_exception.nl_msg.extack['bad-attr'], ".dev-id") 277 ksft_eq(the_exception.nl_msg.error, -errno.EINVAL) 278 _close_conn(cfg, s) 279 280 281def assoc_sk_only_mismatch_tx(cfg): 282 """ Test creating associations based on socket (dev mismatch) """ 283 _init_psp_dev(cfg) 284 285 with _make_clr_conn(cfg) as s: 286 with ksft_raises(NlError) as cm: 287 assoc = cfg.pspnl.rx_assoc({"version": 0, 288 "sock-fd": s.fileno()}) 289 cfg.pspnl.tx_assoc({"version": 0, 290 "tx-key": assoc['rx-key'], 291 "dev-id": cfg.psp_dev_id + 1234567, 292 "sock-fd": s.fileno()}) 293 the_exception = cm.exception 294 ksft_eq(the_exception.nl_msg.extack['bad-attr'], ".dev-id") 295 ksft_eq(the_exception.nl_msg.error, -errno.EINVAL) 296 _close_conn(cfg, s) 297 298 299def assoc_sk_only_unconn(cfg): 300 """ Test creating associations based on socket (unconnected, should fail) """ 301 _init_psp_dev(cfg) 302 303 with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 304 with ksft_raises(NlError) as cm: 305 cfg.pspnl.rx_assoc({"version": 0, 306 "sock-fd": s.fileno()}) 307 the_exception = cm.exception 308 ksft_eq(the_exception.nl_msg.extack['miss-type'], "dev-id") 309 ksft_eq(the_exception.nl_msg.error, -errno.EINVAL) 310 311 312def assoc_version_mismatch(cfg): 313 """ Test creating associations where Rx and Tx PSP versions do not match """ 314 _init_psp_dev(cfg) 315 316 versions = list(cfg.psp_info['psp-versions-cap']) 317 if len(versions) < 2: 318 raise KsftSkipEx("Not enough PSP versions supported by the device for the test") 319 320 # Translate versions to integers 321 versions = [cfg.pspnl.consts["version"].entries[v].value for v in versions] 322 323 with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 324 rx = cfg.pspnl.rx_assoc({"version": versions[0], 325 "dev-id": cfg.psp_dev_id, 326 "sock-fd": s.fileno()}) 327 328 for version in versions[1:]: 329 with ksft_raises(NlError) as cm: 330 cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 331 "version": version, 332 "tx-key": rx['rx-key'], 333 "sock-fd": s.fileno()}) 334 the_exception = cm.exception 335 ksft_eq(the_exception.nl_msg.error, -errno.EINVAL) 336 337 338def _require_tls_ulp(): 339 with socket.create_server(("localhost", 0)) as srv, \ 340 socket.create_connection(srv.getsockname()) as s: 341 try: 342 s.setsockopt(socket.SOL_TCP, TCP_ULP, b"tls") 343 except OSError as exc: 344 raise KsftSkipEx("kTLS not available") from exc 345 346 347def assoc_psp_ulp_exclusive(cfg): 348 """ Test that a TCP ULP cannot be attached to a PSP socket """ 349 _init_psp_dev(cfg) 350 _require_tls_ulp() 351 352 with _make_clr_conn(cfg) as s: 353 try: 354 cfg.pspnl.rx_assoc({"version": 0, 355 "dev-id": cfg.psp_dev_id, 356 "sock-fd": s.fileno()}) 357 with ksft_raises(OSError) as cm: 358 s.setsockopt(socket.SOL_TCP, TCP_ULP, b"tls") 359 ksft_eq(cm.exception.errno, errno.EINVAL) 360 finally: 361 _close_conn(cfg, s) 362 363 364def assoc_ulp_psp_exclusive(cfg): 365 """ Test that a PSP assoc cannot be added to a socket with a TCP ULP """ 366 _init_psp_dev(cfg) 367 _require_tls_ulp() 368 369 with _make_clr_conn(cfg) as s: 370 try: 371 s.setsockopt(socket.SOL_TCP, TCP_ULP, b"tls") 372 with ksft_raises(NlError) as cm: 373 cfg.pspnl.rx_assoc({"version": 0, 374 "dev-id": cfg.psp_dev_id, 375 "sock-fd": s.fileno()}) 376 ksft_eq(cm.exception.nl_msg.error, -errno.EINVAL) 377 ksft_eq(cm.exception.nl_msg.extack['bad-attr'], ".sock-fd") 378 finally: 379 _close_conn(cfg, s) 380 381 382def assoc_twice(cfg): 383 """ Test reusing Tx assoc for two sockets """ 384 _init_psp_dev(cfg) 385 386 def rx_assoc_check(s): 387 assoc = cfg.pspnl.rx_assoc({"version": 0, 388 "dev-id": cfg.psp_dev_id, 389 "sock-fd": s.fileno()}) 390 ksft_eq(assoc['dev-id'], cfg.psp_dev_id) 391 ksft_gt(assoc['rx-key']['spi'], 0) 392 ksft_eq(len(assoc['rx-key']['key']), 16) 393 394 return assoc 395 396 with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 397 assoc = rx_assoc_check(s) 398 tx = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 399 "version": 0, 400 "tx-key": assoc['rx-key'], 401 "sock-fd": s.fileno()}) 402 ksft_eq(len(tx), 0) 403 404 # Use the same Tx assoc second time 405 with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s2: 406 rx_assoc_check(s2) 407 tx = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 408 "version": 0, 409 "tx-key": assoc['rx-key'], 410 "sock-fd": s2.fileno()}) 411 ksft_eq(len(tx), 0) 412 413 s.close() 414 415 416def _data_basic_send(cfg, version, ipver): 417 """ Test basic data send """ 418 _init_psp_dev(cfg) 419 420 # Version 0 is required by spec, don't let it skip 421 if version: 422 name = cfg.pspnl.consts["version"].entries_by_val[version].name 423 if name not in cfg.psp_info['psp-versions-cap']: 424 with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: 425 with ksft_raises(NlError) as cm: 426 cfg.pspnl.rx_assoc({"version": version, 427 "dev-id": cfg.psp_dev_id, 428 "sock-fd": s.fileno()}) 429 ksft_eq(cm.exception.nl_msg.error, -errno.EOPNOTSUPP) 430 raise KsftSkipEx("PSP version not supported", name) 431 432 s = _make_psp_conn(cfg, version, ipver) 433 434 rx_assoc = cfg.pspnl.rx_assoc({"version": version, 435 "dev-id": cfg.psp_dev_id, 436 "sock-fd": s.fileno()}) 437 rx = rx_assoc['rx-key'] 438 tx = _spi_xchg(s, rx) 439 440 cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 441 "version": version, 442 "tx-key": tx, 443 "sock-fd": s.fileno()}) 444 445 data_len = _send_careful(cfg, s, 100) 446 _check_data_rx(cfg, data_len) 447 _close_psp_conn(cfg, s) 448 449 450def __bad_xfer_do(cfg, s, tx, version='hdr0-aes-gcm-128'): 451 # Make sure we accept the ACK for the SPI before we seal with the bad assoc 452 _check_data_outq(s, 0) 453 454 cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 455 "version": version, 456 "tx-key": tx, 457 "sock-fd": s.fileno()}) 458 459 data_len = _send_careful(cfg, s, 20) 460 _check_data_outq(s, data_len, force_wait=True) 461 _check_data_rx(cfg, 0) 462 _close_psp_conn(cfg, s) 463 464 465def data_send_bad_key(cfg): 466 """ Test send data with bad key """ 467 _init_psp_dev(cfg) 468 469 s = _make_psp_conn(cfg) 470 471 rx_assoc = cfg.pspnl.rx_assoc({"version": 0, 472 "dev-id": cfg.psp_dev_id, 473 "sock-fd": s.fileno()}) 474 rx = rx_assoc['rx-key'] 475 tx = _spi_xchg(s, rx) 476 tx['key'] = (tx['key'][0] ^ 0xff).to_bytes(1, 'little') + tx['key'][1:] 477 __bad_xfer_do(cfg, s, tx) 478 479 480def data_send_disconnect(cfg): 481 """ Test socket close after sending data """ 482 _init_psp_dev(cfg) 483 484 with _make_psp_conn(cfg) as s: 485 assoc = cfg.pspnl.rx_assoc({"version": 0, 486 "sock-fd": s.fileno()}) 487 tx = _spi_xchg(s, assoc['rx-key']) 488 cfg.pspnl.tx_assoc({"version": 0, 489 "tx-key": tx, 490 "sock-fd": s.fileno()}) 491 492 data_len = _send_careful(cfg, s, 100) 493 _check_data_rx(cfg, data_len) 494 495 s.shutdown(socket.SHUT_RDWR) 496 s.close() 497 498 499def _data_mss_adjust(cfg, ipver): 500 _init_psp_dev(cfg) 501 502 # First figure out what the MSS would be without any adjustments 503 s = _make_clr_conn(cfg, ipver) 504 s.send(b"0123456789abcdef" * 1024) 505 _check_data_rx(cfg, 16 * 1024) 506 mss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG) 507 _close_conn(cfg, s) 508 509 s = _make_psp_conn(cfg, 0, ipver) 510 try: 511 rx_assoc = cfg.pspnl.rx_assoc({"version": 0, 512 "dev-id": cfg.psp_dev_id, 513 "sock-fd": s.fileno()}) 514 rx = rx_assoc['rx-key'] 515 tx = _spi_xchg(s, rx) 516 517 rxmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG) 518 ksft_eq(mss, rxmss) 519 520 cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 521 "version": 0, 522 "tx-key": tx, 523 "sock-fd": s.fileno()}) 524 525 txmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG) 526 ksft_eq(mss, txmss + 40) 527 528 data_len = _send_careful(cfg, s, 100) 529 _check_data_rx(cfg, data_len) 530 _check_data_outq(s, 0) 531 532 txmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG) 533 ksft_eq(mss, txmss + 40) 534 finally: 535 _close_psp_conn(cfg, s) 536 537 538def data_stale_key(cfg): 539 """ Test send on a double-rotated key """ 540 _init_psp_dev(cfg) 541 542 prev_stale = _get_stat(cfg, 'stale-events') 543 s = _make_psp_conn(cfg) 544 try: 545 rx_assoc = cfg.pspnl.rx_assoc({"version": 0, 546 "dev-id": cfg.psp_dev_id, 547 "sock-fd": s.fileno()}) 548 rx = rx_assoc['rx-key'] 549 tx = _spi_xchg(s, rx) 550 551 cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 552 "version": 0, 553 "tx-key": tx, 554 "sock-fd": s.fileno()}) 555 556 data_len = _send_careful(cfg, s, 100) 557 _check_data_rx(cfg, data_len) 558 _check_data_outq(s, 0) 559 560 cfg.pspnl.key_rotate({"id": cfg.psp_dev_id}) 561 cfg.pspnl.key_rotate({"id": cfg.psp_dev_id}) 562 563 cur_stale = _get_stat(cfg, 'stale-events') 564 ksft_gt(cur_stale, prev_stale) 565 566 s.send(b'0123456789' * 200) 567 _check_data_outq(s, 2000, force_wait=True) 568 finally: 569 _close_psp_conn(cfg, s) 570 571 572def __nsim_psp_rereg(cfg): 573 # The PSP dev ID will change, remember what was there before 574 before = set([x['id'] for x in cfg.pspnl.dev_get({}, dump=True)]) 575 576 cfg._ns.nsims[0].dfs_write('psp_rereg', '1') 577 578 after = set([x['id'] for x in cfg.pspnl.dev_get({}, dump=True)]) 579 580 new_devs = list(after - before) 581 ksft_eq(len(new_devs), 1) 582 cfg.psp_dev_id = list(after - before)[0] 583 584 585def removal_device_rx(cfg): 586 """ Test removing a netdev / PSD with active Rx assoc """ 587 588 # We could technically devlink reload real devices, too 589 # but that kills the control socket. So test this on 590 # netdevsim only for now 591 cfg.require_nsim() 592 593 s = _make_clr_conn(cfg) 594 try: 595 rx_assoc = cfg.pspnl.rx_assoc({"version": 0, 596 "dev-id": cfg.psp_dev_id, 597 "sock-fd": s.fileno()}) 598 ksft_not_none(rx_assoc) 599 600 __nsim_psp_rereg(cfg) 601 finally: 602 _close_conn(cfg, s) 603 604 605def removal_device_bi(cfg): 606 """ Test removing a netdev / PSD with active Rx/Tx assoc """ 607 608 # We could technically devlink reload real devices, too 609 # but that kills the control socket. So test this on 610 # netdevsim only for now 611 cfg.require_nsim() 612 613 s = _make_clr_conn(cfg) 614 try: 615 rx_assoc = cfg.pspnl.rx_assoc({"version": 0, 616 "dev-id": cfg.psp_dev_id, 617 "sock-fd": s.fileno()}) 618 cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 619 "version": 0, 620 "tx-key": rx_assoc['rx-key'], 621 "sock-fd": s.fileno()}) 622 __nsim_psp_rereg(cfg) 623 finally: 624 _close_conn(cfg, s) 625 626 627def _get_psp_ver_ip_variants(): 628 for ver in range(4): 629 for ipv in ("4", "6"): 630 yield KsftNamedVariant(f"v{ver}_ip{ipv}", ver, ipv) 631 632 633def _get_ip_variants(): 634 for ipv in ("4", "6"): 635 yield KsftNamedVariant(f"ip{ipv}", ipv) 636 637 638@ksft_variants(_get_psp_ver_ip_variants()) 639def data_basic_send(cfg, version, ipver): 640 """Test basic PSP data send.""" 641 cfg.require_ipver(ipver) 642 _data_basic_send(cfg, version, ipver) 643 644 645@ksft_variants(_get_ip_variants()) 646def data_mss_adjust(cfg, ipver): 647 """Test MSS adjustment with PSP.""" 648 cfg.require_ipver(ipver) 649 _data_mss_adjust(cfg, ipver) 650 651 652def _check_assoc_list(cfg, psp_dev_id, ifindex, nsid=None): 653 """Verify assoc-list contains device with given ifindex, no duplicates.""" 654 dev_info = cfg.pspnl.dev_get({'id': psp_dev_id}) 655 656 ksft_true('assoc-list' in dev_info, 657 "No assoc-list in dev_get() response after association") 658 found = False 659 for assoc in dev_info['assoc-list']: 660 if assoc['ifindex'] != ifindex: 661 continue 662 if nsid is not None and assoc['nsid'] != nsid: 663 continue 664 ksft_eq(found, False, "Duplicate assoc entry found") 665 found = True 666 ksft_eq(found, True, 667 "Associated device not found in dev_get() response") 668 669 670def _data_basic_send_netkit_psp_assoc(cfg, version, ipver): 671 """ 672 Test basic data send with netkit interface associated with PSP dev. 673 """ 674 _assoc_nk_guest(cfg) 675 676 # Enter guest namespace (netns) to run PSP test 677 with NetNSEnter(cfg.netns.name): 678 cfg.pspnl = PSPFamily() 679 680 sock = _make_psp_conn(cfg, version, ipver) 681 682 rx_assoc = cfg.pspnl.rx_assoc({"version": version, 683 "dev-id": cfg.psp_dev_id, 684 "sock-fd": sock.fileno()}) 685 rx_key = rx_assoc['rx-key'] 686 tx_key = _spi_xchg(sock, rx_key) 687 688 cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, 689 "version": version, 690 "tx-key": tx_key, 691 "sock-fd": sock.fileno()}) 692 693 data_len = _send_careful(cfg, sock, 100) 694 _check_data_rx(cfg, data_len) 695 _close_psp_conn(cfg, sock) 696 697 698def _assoc_check_list(cfg): 699 """Test that assoc-list is correctly populated after dev-assoc.""" 700 _assoc_nk_guest(cfg) 701 _check_assoc_list(cfg, cfg.psp_dev_id, cfg.nk_guest_ifindex, 702 cfg.psp_dev_peer_nsid) 703 704 705def _get_psp_ver_ip6_variants(): 706 for ver in range(4): 707 yield KsftNamedVariant(f"v{ver}_ip6", ver, "6") 708 709 710@ksft_variants(_get_psp_ver_ip6_variants()) 711def data_basic_send_netkit_psp_assoc(cfg, version, ipver): 712 """Test PSP data send via netkit with dev-assoc.""" 713 cfg.require_ipver(ipver) 714 _data_basic_send_netkit_psp_assoc(cfg, version, ipver) 715 716 717def _key_rotation_notify_multi_ns_netkit(cfg): 718 """ Test key rotation notifications across multiple namespaces using netkit """ 719 _assoc_nk_guest(cfg) 720 721 # Create listener in guest namespace; socket stays bound to that ns 722 with NetNSEnter(cfg.netns.name): 723 peer_pspnl = PSPFamily() 724 peer_pspnl.ntf_subscribe('use') 725 726 # Create listener in main namespace 727 main_pspnl = PSPFamily() 728 main_pspnl.ntf_subscribe('use') 729 730 # Trigger key rotation on the PSP device 731 cfg.pspnl.key_rotate({"id": cfg.psp_dev_id}) 732 733 # Poll both sockets from main thread 734 for pspnl, label in [(main_pspnl, "main"), (peer_pspnl, "guest")]: 735 for ntf in pspnl.poll_ntf(duration=10): 736 if ntf['msg'].get('id') == cfg.psp_dev_id: 737 break 738 else: 739 raise KsftFailEx( 740 f"No key rotation notification received" 741 f" in {label} namespace") 742 743 744def _dev_change_notify_multi_ns_netkit(cfg): 745 """ Test dev_change notifications across multiple namespaces using netkit """ 746 _assoc_nk_guest(cfg) 747 748 # Create listener in guest namespace; socket stays bound to that ns 749 with NetNSEnter(cfg.netns.name): 750 peer_pspnl = PSPFamily() 751 peer_pspnl.ntf_subscribe('mgmt') 752 753 # Create listener in main namespace 754 main_pspnl = PSPFamily() 755 main_pspnl.ntf_subscribe('mgmt') 756 757 # Trigger dev_change by calling dev_set (notification is always sent) 758 cfg.pspnl.dev_set({'id': cfg.psp_dev_id, 759 'psp-versions-ena': cfg.psp_info['psp-versions-cap']}) 760 761 # Poll both sockets from main thread 762 for pspnl, label in [(main_pspnl, "main"), (peer_pspnl, "guest")]: 763 for ntf in pspnl.poll_ntf(duration=10): 764 if ntf['msg'].get('id') == cfg.psp_dev_id: 765 break 766 else: 767 raise KsftFailEx( 768 f"No dev_change notification received" 769 f" in {label} namespace") 770 771 772def _psp_dev_get_check_netkit_psp_assoc(cfg): 773 """ Check psp dev-get output with netkit interface associated with PSP dev """ 774 _assoc_nk_guest(cfg) 775 776 # Check 1: In default netns, verify dev-get has correct ifindex and assoc-list 777 dev_info = cfg.pspnl.dev_get({'id': cfg.psp_dev_id}) 778 ksft_eq(dev_info['ifindex'], cfg.psp_ifindex) 779 _check_assoc_list(cfg, cfg.psp_dev_id, cfg.nk_guest_ifindex, 780 cfg.psp_dev_peer_nsid) 781 782 # Check 2: In guest netns, verify dev-get has assoc-list with nk_guest device 783 with NetNSEnter(cfg.netns.name): 784 peer_pspnl = PSPFamily() 785 786 # Dump all devices in the guest namespace 787 peer_devices = peer_pspnl.dev_get({}, dump=True) 788 789 # Find the device with by-association flag 790 peer_dev = None 791 for dev in peer_devices: 792 if dev.get('by-association'): 793 peer_dev = dev 794 break 795 796 ksft_not_none(peer_dev, "No PSP device found with by-association flag in guest netns") 797 798 # Verify assoc-list contains the nk_guest device 799 ksft_true('assoc-list' in peer_dev and len(peer_dev['assoc-list']) > 0, 800 "Guest device should have assoc-list with local devices") 801 802 # Verify the assoc-list contains nk_guest ifindex with nsid=-1 (same namespace) 803 found = False 804 for assoc in peer_dev['assoc-list']: 805 if assoc['ifindex'] == cfg.nk_guest_ifindex: 806 ksft_eq(assoc['nsid'], -1, 807 "nsid should be -1 (NETNSA_NSID_NOT_ASSIGNED) for same-namespace device") 808 found = True 809 break 810 ksft_true(found, "nk_guest ifindex not found in assoc-list") 811 812 813def _dev_assoc_no_nsid(cfg): 814 """ Test dev-assoc and dev-disassoc without nsid attribute """ 815 _init_psp_dev(cfg, True) 816 817 # Associate without nsid - should look up ifindex in caller's netns 818 cfg.pspnl.dev_assoc({'id': cfg.psp_dev_id, 819 'ifindex': cfg.nk_host_ifindex}) 820 defer(_try_disassoc, cfg, 821 cfg.psp_dev_id, cfg.nk_host_ifindex) 822 defer(delattr, cfg, 'psp_dev_id') 823 defer(delattr, cfg, 'psp_info') 824 825 # Verify assoc-list contains the device (match by ifindex only) 826 _check_assoc_list(cfg, cfg.psp_dev_id, cfg.nk_host_ifindex) 827 828 # Disassociate without nsid - should also use caller's netns 829 cfg.pspnl.dev_disassoc({'id': cfg.psp_dev_id, 830 'ifindex': cfg.nk_host_ifindex}) 831 832 # Verify assoc-list no longer contains the device 833 dev_info = cfg.pspnl.dev_get({'id': cfg.psp_dev_id}) 834 found = False 835 if 'assoc-list' in dev_info: 836 for assoc in dev_info['assoc-list']: 837 if assoc['ifindex'] == cfg.nk_host_ifindex: 838 found = True 839 break 840 ksft_true(not found, "Device should not be in assoc-list after disassociation") 841 842 843def _psp_dev_assoc_cleanup_on_netkit_del(cfg): 844 """Test that assoc-list is cleared when associated netkit is deleted. 845 846 Creates a disposable netkit pair for this test to avoid destroying 847 the shared environment. 848 """ 849 _init_psp_dev(cfg, True) 850 defer(delattr, cfg, 'psp_dev_id') 851 defer(delattr, cfg, 'psp_info') 852 853 existing = {cfg.nk_host_ifindex, cfg.nk_guest_ifindex} 854 855 # Create a temporary netkit pair 856 tmp_host_name = "tmp_nk_host" 857 tmp_guest_name = "tmp_nk_guest" 858 rtnl = RtnlFamily() 859 rtnl.newlink( 860 { 861 "ifname": tmp_host_name, 862 "linkinfo": { 863 "kind": "netkit", 864 "data": { 865 "mode": "l2", 866 "policy": "forward", 867 "peer-policy": "forward", 868 }, 869 }, 870 }, 871 flags=[Netlink.NLM_F_CREATE, Netlink.NLM_F_EXCL], 872 ) 873 cleanup_netkit = defer(ip, f"link del {tmp_host_name}") 874 875 # Find the peer by diffing against existing netkit ifindexes 876 all_links = ip("-d link show", json=True) 877 tmp_peer = [link for link in all_links 878 if link.get('linkinfo', {}).get('info_kind') == 'netkit' 879 and link['ifindex'] not in existing 880 and link['ifname'] != tmp_host_name] 881 ksft_eq(len(tmp_peer), 1, 882 "Failed to find temporary netkit peer") 883 guest_name = tmp_peer[0]['ifname'] 884 885 # Rename and move guest end into the test namespace 886 ip(f"link set dev {guest_name} name {tmp_guest_name}") 887 ip(f"link set dev {tmp_guest_name} netns {cfg.netns.name}") 888 tmp_guest_dev = ip(f"link show dev {tmp_guest_name}", 889 json=True, ns=cfg.netns)[0] 890 tmp_guest_ifindex = tmp_guest_dev['ifindex'] 891 ip(f"link set dev {tmp_guest_name} up", ns=cfg.netns) 892 893 # Associate PSP device with the temporary guest interface 894 cfg.pspnl.dev_assoc({'id': cfg.psp_dev_id, 895 'ifindex': tmp_guest_ifindex, 896 'nsid': cfg.psp_dev_peer_nsid}) 897 898 # Verify assoc-list contains the temporary device 899 _check_assoc_list(cfg, cfg.psp_dev_id, tmp_guest_ifindex, 900 cfg.psp_dev_peer_nsid) 901 902 # Delete the temporary netkit pair (deleting one end removes both) 903 ip(f"link del {tmp_host_name}") 904 cleanup_netkit.cancel() 905 906 # Verify assoc-list is cleared after netkit deletion 907 dev_info = cfg.pspnl.dev_get({'id': cfg.psp_dev_id}) 908 ksft_true('assoc-list' not in dev_info 909 or len(dev_info['assoc-list']) == 0, 910 "assoc-list should be empty after netkit deletion") 911 912 913def _try_disassoc(cfg, psp_dev_id, ifindex, nsid=None): 914 """Best-effort disassociate, ignoring errors if already removed.""" 915 try: 916 params = {'id': psp_dev_id, 'ifindex': ifindex} 917 if nsid is not None: 918 params['nsid'] = nsid 919 cfg.pspnl.dev_disassoc(params) 920 except NlError: 921 pass 922 923 924def _assoc_nk_guest(cfg): 925 """Associate nk_guest with PSP device and register cleanup via defer().""" 926 _init_psp_dev(cfg, True) 927 928 cfg.pspnl.dev_assoc({'id': cfg.psp_dev_id, 929 'ifindex': cfg.nk_guest_ifindex, 930 'nsid': cfg.psp_dev_peer_nsid}) 931 defer(_disassoc_nk_guest, cfg, 932 cfg.psp_dev_id, cfg.nk_guest_ifindex) 933 934 935def _disassoc_nk_guest(cfg, psp_dev_id, nk_guest_ifindex): 936 """Disassociate nk_guest and reset cfg PSP state.""" 937 pspnl = PSPFamily() 938 pspnl.dev_disassoc({'id': psp_dev_id, 'ifindex': nk_guest_ifindex, 939 'nsid': cfg.psp_dev_peer_nsid}) 940 cfg.pspnl = pspnl 941 del cfg.psp_dev_id 942 del cfg.psp_info 943 944 945def _get_nsid(ns_name): 946 """Get the nsid for a namespace.""" 947 for entry in ip("netns list-id", json=True): 948 if entry.get("name") == str(ns_name): 949 return entry["nsid"] 950 raise KsftSkipEx(f"nsid not found for namespace {ns_name}") 951 952 953def _setup_psp_attributes(cfg): 954 # pylint: disable=protected-access 955 """ 956 Set up PSP-specific attributes on the environment. 957 958 This sets attributes needed for PSP tests based on whether we're using 959 netdevsim or a real NIC. 960 """ 961 if cfg._ns is not None: 962 # netdevsim case: PSP device is the local dev (in host namespace) 963 cfg.psp_dev = cfg._ns.nsims[0].dev 964 cfg.psp_ifname = cfg.psp_dev['ifname'] 965 cfg.psp_ifindex = cfg.psp_dev['ifindex'] 966 967 # PSP peer device is the remote dev (in _netns, where psp_responder runs) 968 cfg.psp_dev_peer = cfg._ns_peer.nsims[0].dev 969 cfg.psp_dev_peer_ifname = cfg.psp_dev_peer['ifname'] 970 cfg.psp_dev_peer_ifindex = cfg.psp_dev_peer['ifindex'] 971 else: 972 # Real NIC case: PSP device is the local interface 973 cfg.psp_dev = cfg.dev 974 cfg.psp_ifname = cfg.ifname 975 cfg.psp_ifindex = cfg.ifindex 976 977 # PSP peer device is the remote interface 978 cfg.psp_dev_peer = cfg.remote_dev 979 cfg.psp_dev_peer_ifname = cfg.remote_ifname 980 cfg.psp_dev_peer_ifindex = cfg.remote_ifindex 981 982 # Get nsid for the guest namespace (netns) where nk_guest is 983 cfg.psp_dev_peer_nsid = _get_nsid(cfg.netns.name) 984 985 986 987def main() -> None: 988 """ Ksft boiler plate main """ 989 990 # Make sure LOCAL_PREFIX_V6 is set 991 if "LOCAL_PREFIX_V6" not in os.environ: 992 os.environ["LOCAL_PREFIX_V6"] = "2001:db8:2::" 993 994 try: 995 env = NetDrvContEnv(__file__, primary_rx_redirect=True) 996 has_cont = True 997 except KsftSkipEx: 998 env = NetDrvEpEnv(__file__) 999 has_cont = False 1000 1001 with env as cfg: 1002 cfg.pspnl = PSPFamily() 1003 1004 if has_cont: 1005 _setup_psp_attributes(cfg) 1006 1007 # Set up responder and communication sock 1008 # psp_responder runs in _netns (remote namespace with psp_dev_peer) 1009 responder = cfg.remote.deploy("psp_responder") 1010 1011 cfg.comm_port = rand_port() 1012 srv = None 1013 try: 1014 with bkg(responder + f" -p {cfg.comm_port} -i {cfg.remote_ifindex}", 1015 host=cfg.remote, exit_wait=True) as srv: 1016 wait_port_listen(cfg.comm_port, host=cfg.remote) 1017 1018 cfg.comm_sock = socket.create_connection((cfg.remote_addr, 1019 cfg.comm_port), 1020 timeout=1) 1021 1022 cases = [data_basic_send, data_mss_adjust] 1023 1024 if has_cont: 1025 cases += [ 1026 _assoc_check_list, 1027 data_basic_send_netkit_psp_assoc, 1028 _key_rotation_notify_multi_ns_netkit, 1029 _dev_change_notify_multi_ns_netkit, 1030 _psp_dev_get_check_netkit_psp_assoc, 1031 _dev_assoc_no_nsid, 1032 _psp_dev_assoc_cleanup_on_netkit_del, 1033 ] 1034 1035 ksft_run(cases=cases, globs=globals(), 1036 case_pfx={"dev_", "data_", "assoc_", "removal_"}, 1037 args=(cfg, )) 1038 1039 cfg.comm_sock.send(b"exit\0") 1040 cfg.comm_sock.close() 1041 finally: 1042 if srv and (srv.stdout or srv.stderr): 1043 ksft_pr("") 1044 ksft_pr(f"Responder logs ({srv.ret}):") 1045 if srv and srv.stdout: 1046 ksft_pr("STDOUT:\n# " + srv.stdout.strip().replace("\n", "\n# ")) 1047 if srv and srv.stderr: 1048 ksft_pr("STDERR:\n# " + srv.stderr.strip().replace("\n", "\n# ")) 1049 ksft_exit() 1050 1051 1052if __name__ == "__main__": 1053 main() 1054