diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e306f4e..8d96932 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,9 +62,13 @@ jobs: - name: Install p4p run: pip install p4p - - name: Run p4p interop tests + - name: Build workspace binaries + run: cargo build --all + + - name: Run p4p provider matrix env: PVA_TEST_P4P: "1" P4P_PROVIDER_CMD: "python3 ${{ github.workspace }}/spvirit-tools/tests/interop/p4p_server.py" P4P_PROVIDER_READY_MS: "5000" - run: cargo test --all + P4P_TEST_SERVER: "127.0.0.1:5075" + run: cargo test --package spvirit-tools --test interop_tool_matrix -- p4p_provider_matrix diff --git a/spvirit-client/src/search.rs b/spvirit-client/src/search.rs index 4843de3..074c25a 100644 --- a/spvirit-client/src/search.rs +++ b/spvirit-client/src/search.rs @@ -373,35 +373,16 @@ pub async fn search_pv( let mut socket_info: Vec<(Arc, Vec, Vec)> = Vec::new(); for (bind_ip, group_targets) in &bind_groups { - let bind_addr = SocketAddr::new(*bind_ip, udp_port); + // Always use an ephemeral port for the search client socket. + // We only receive unicast replies, so sharing the server's search + // port is unnecessary — and on Linux with SO_REUSEPORT the kernel + // would route our own outbound packet back to us instead of the + // server. + let bind_addr = SocketAddr::new(*bind_ip, 0); let (std_sock, actual_bind_addr) = match bind_udp_reuse(bind_addr) { - Ok(sock) => (sock, bind_addr), - Err(err) if err.kind() == std::io::ErrorKind::AddrInUse => { - let fallback = SocketAddr::new(*bind_ip, 0); - match bind_udp_reuse(fallback) { - Ok(sock) => { - let actual = sock.local_addr().unwrap_or(fallback); - if debug_enabled { - debug!( - "pva search bind={} failed (in use), fallback bind={}", - bind_addr, actual - ); - } - (sock, actual) - } - Err(fallback_err) => { - if debug_enabled { - debug!( - "pva search skipping bind={} step=bind-fallback kind={:?} err={}", - bind_addr, - fallback_err.kind(), - fallback_err - ); - } - last_io_error = Some(fallback_err); - continue; - } - } + Ok(sock) => { + let actual = sock.local_addr().unwrap_or(bind_addr); + (sock, actual) } Err(err) => { if debug_enabled { @@ -870,35 +851,16 @@ pub async fn discover_servers( let mut socket_info: Vec<(Arc, Vec, Vec)> = Vec::new(); for (bind_ip, group_targets) in &bind_groups { - let bind_addr = SocketAddr::new(*bind_ip, udp_port); + // Always use an ephemeral port for the discovery client socket. + // We only receive unicast replies, so sharing the server's search + // port is unnecessary — and on Linux with SO_REUSEPORT the kernel + // would route our own outbound packet back to us instead of the + // server. + let bind_addr = SocketAddr::new(*bind_ip, 0); let (std_sock, actual_bind_addr) = match bind_udp_reuse(bind_addr) { - Ok(sock) => (sock, bind_addr), - Err(err) if err.kind() == std::io::ErrorKind::AddrInUse => { - let fallback = SocketAddr::new(*bind_ip, 0); - match bind_udp_reuse(fallback) { - Ok(sock) => { - let actual = sock.local_addr().unwrap_or(fallback); - if debug_enabled { - debug!( - "pva discover bind={} failed (in use), fallback bind={}", - bind_addr, actual - ); - } - (sock, actual) - } - Err(fallback_err) => { - if debug_enabled { - debug!( - "pva discover skipping bind={} step=bind-fallback kind={:?} err={}", - bind_addr, - fallback_err.kind(), - fallback_err - ); - } - last_io_error = Some(fallback_err); - continue; - } - } + Ok(sock) => { + let actual = sock.local_addr().unwrap_or(bind_addr); + (sock, actual) } Err(err) => { if debug_enabled { diff --git a/spvirit-tools/tests/interop/p4p_client_test.py b/spvirit-tools/tests/interop/p4p_client_test.py index 453f83d..9b5e1c9 100644 --- a/spvirit-tools/tests/interop/p4p_client_test.py +++ b/spvirit-tools/tests/interop/p4p_client_test.py @@ -5,8 +5,8 @@ Expects the server to already be running with the test DB (SIM:* PVs). Environment variables: - SPVIRIT_TEST_TCP_PORT – TCP port of the spvirit server (required) - SPVIRIT_TEST_UDP_PORT – UDP port of the spvirit server (required) + SPVIRIT_TEST_TCP_PORT - TCP port of the spvirit server (required) + SPVIRIT_TEST_UDP_PORT - UDP port of the spvirit server (required) Exit code 0 on success, 1 on any failure. """