feat(dgw): align gateway scans with local planning#1776
Conversation
Let maintainers know that an action is required on their side
|
f5e9736 to
eb59efa
Compare
eb59efa to
d0ef8ab
Compare
|
If I understand correctly, you used Claude to backport the changes made to the net scanner in RDM? Did you notice any improvements? |
There was a problem hiding this comment.
Pull request overview
This PR updates Devolutions Gateway’s network scan API to match the newer “local planning” model from network-scanner, including explicit target/range planning, interface-aware source selection, concurrency controls, and an opt-in v1 websocket result shape while keeping legacy behavior for existing clients.
Changes:
- Adds
/jet/net/interfacesand documents/jet/net/scan(WebSocket handshake) in OpenAPI; marks/jet/net/configas deprecated via response headers. - Introduces planning/source inventory and interface-aware execution (including optional interface binding and per-stage concurrency limits) in
network-scanner. - Refactors scan event filtering/serialization into
ScanEventFilterwith a configurable response format (legacyvsnetwork_scan_result_v1), and reorganizes unit tests undersrc/tests/.
Reviewed changes
Copilot reviewed 33 out of 34 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| devolutions-gateway/src/openapi.rs | Registers new net scan endpoints/types in the OpenAPI document. |
| devolutions-gateway/src/api/net.rs | Implements new query params, planning validation, /interfaces, scan streaming changes, and deprecates /config. |
| devolutions-gateway/Cargo.toml | Extends openapi feature to include network-scanner/openapi. |
| crates/network-scanner/src/lib.rs | Exposes new modules (planner, results, sources) and mounts in-tree tests. |
| crates/network-scanner/src/ip_utils.rs | Adds IpFamily, single, intersection, address_count, and equality traits to support planning/validation. |
| crates/network-scanner/src/planner.rs | Adds target/range + interface selection planning and structured planning errors. |
| crates/network-scanner/src/sources.rs | Models scan-capable sources with metadata and capability flags; adds system source discovery. |
| crates/network-scanner/src/scanner.rs | Refactors config into timing/limits/targeting; adds interface bind support and injected source providers. |
| crates/network-scanner/src/task_utils.rs | Switches task context creation to use plan_scan output (planned ranges + sources). |
| crates/network-scanner/src/results.rs | Adds ScanEventFilter and v1 result event model + legacy serialization. |
| crates/network-scanner/src/ping.rs | Adds ping queue events, interface binding, and optional concurrency limiting. |
| crates/network-scanner/src/port_discovery.rs | Adds interface binding, optional concurrency limiting, and richer failure classification/time measurement. |
| crates/network-scanner/src/netbios.rs | Adds timing to NetBIOS success events. |
| crates/network-scanner/src/mdns.rs | Adds timing to mDNS resolved events. |
| crates/network-scanner/src/event_bus.rs | Adds AnyScannerEvent wrapper for generic subscriptions; adjusts broadcast match for new shape. |
| crates/network-scanner/src/broadcast/mod.rs | Extends broadcast events with optional timing. |
| crates/network-scanner/src/broadcast/asynchronous.rs | Measures and emits broadcast RTT timing. |
| crates/network-scanner/examples/scan.rs | Updates example for refactored scanner config and targeting controls. |
| crates/network-scanner/examples/port_discovery.rs | Updates example for new scan_ports signature (concurrency + interface bind). |
| crates/network-scanner/examples/ping_range.rs | Updates example for new ping_range signature (concurrency + interface bind). |
| crates/network-scanner/src/tests/mod.rs | Adds src/tests module root for in-tree unit tests. |
| crates/network-scanner/src/tests/ip_utils.rs | Moves/expands unit coverage for IP range utilities. |
| crates/network-scanner/src/tests/planner.rs | Adds unit tests for planning/source selection and policy combinations. |
| crates/network-scanner/src/tests/results.rs | Adds unit tests for legacy/v1 serialization and filter matrix behavior. |
| crates/network-scanner/src/tests/sources.rs | Adds unit tests for source selection/helpers. |
| crates/network-scanner/src/tests/task_utils.rs | Adds unit tests for context planning/execution config mapping. |
| crates/network-scanner/Cargo.toml | Adds serde + optional utoipa feature for OpenAPI schema derivations; adjusts platform deps. |
| crates/network-scanner-proto/src/lib.rs | Adds crate-level docs and mounts in-tree tests module. |
| crates/network-scanner-proto/src/netbios.rs | Moves tests out of module body. |
| crates/network-scanner-proto/src/tests/mod.rs | Adds src/tests module root for proto unit tests. |
| crates/network-scanner-proto/src/tests/netbios.rs | Adds extracted NetBIOS protocol parsing tests. |
| crates/network-scanner-net/src/socket.rs | Adds cross-platform socket interface binding helper. |
| crates/network-scanner-net/Cargo.toml | Adds libc dep for unix setsockopt implementation. |
| Cargo.lock | Updates dependency graph for new features/platform changes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
d0ef8ab to
4b15f5b
Compare
e6e838c to
176fcb6
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 34 out of 35 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
93723d4 to
e333faf
Compare
Updated upon request from Paul, mainly to make the request/response shape easier to use on C# side. |
b1b53ee to
4ab4331
Compare
There was a problem hiding this comment.
I need to review this file a bit more carefully
062bb12 to
3324144
Compare
Adds selected network scan sources, explicit target/range planning, interface-aware scan execution, the v1 result format, and gateway API/OpenAPI support while preserving the legacy event format. ARP/NDP discovery and MAC enrichment are intentionally excluded from this review slice and remain available on backup-feat-network-scan-improvement-with-arp-ndp.
Move in-tree `src/tests/` from `network-scanner` and `network-scanner-proto` to `testsuite/tests/network_scanner/`, so the production scanner crates compile no test code into their binaries. The two `task_utils` internals tests need (`ContextConfig`, `TaskExecutionContext` and their constructors) are exposed via a new `test-utils` Cargo feature gated by a small `macro_rules!` helper, and the feature is enabled only by `testsuite`'s dev-dependencies. Drop the duplicated `use` blocks and blanket `#![allow(unused_imports)]` each test file had carried over from a mechanical split; each file now imports only what it actually uses.
Bring `target=`, `range_interface_policy=`, and `response_format=` onto
the same parse-at-validate-time path that `range=` and `probe=` already
use, so each invalid value lands in the structured `{ error, value }`
body with the offending raw value instead of a generic serde rejection
at extraction time.
`NetworkScanQueryError::InvalidTarget` was already declared in the API
surface but had no construct site; this commit wires it up. The
`Option<RangeInterfacePolicy>` / `Option<NetworkScanResponseFormat>`
fields are also widened to `Option<String>` so the network-scanner
enums don't need to leak through `utoipa::IntoParams` (incidentally
removes the implicit dependency on `ToSchema` for them when the gateway
`openapi` feature is enabled).
…emantics The query-param mapping silently dropped `start` and `failure` events when only `probe=ping` was requested, and wired `enable_failure` to TCP-probe failures instead of ping failures. Restore the pre-existing lifecycle defaults so `probe=ping` (with no explicit `report_ping_*` toggles) emits ping start, success, and failure. Treat `enable_failure` as the legacy alias for `report_ping_failure`. TCP-probe failure now requires an explicit `report_tcp_failure=true`; no legacy alias enables it implicitly. Also rewrite a handful of code comments that referenced an external design doc by section number so the comments stand on their own, and remove the now-misleading `ScanEventFilter::enable_failure()` helper (no callers; the name conflated two now-separate concerns).
33e179b to
0bb47dc
Compare
…ing baseline
Three follow-up tweaks against the plan spec after the earlier fix-up
(`fix(net-scan): restore probe=ping default events and enable_failure
semantics`) was reviewed:
1. Drop the `report_ping_status` query parameter. Plan §4 lists it but
the recommended .NET mapping never uses it — the field translates to
`report_ping_success && report_ping_failure` at the call site instead.
It was introduced in this PR and never shipped, so removing it has
no downstream consumer impact.
2. Restore the pre-PR behavior for `probe=ping`:
- The COMMON_PORTS fallback now triggers only when the caller sent no
`probe=` at all. An explicit probe list (even one containing only
`ping`) is honored verbatim. Discriminator changed from
`typed_ports.is_empty()` to `val.probes.is_empty()`.
- `has_ping_probe` no longer auto-enables `report_ping_failure`.
Pre-PR clients that sent only `probe=ping` received start + success
events; failure events required `enable_failure=true`. The earlier
fix-up coupled failure to `has_ping_probe`, which went beyond what
the baseline did.
3. Expand the doc comment on `enable_failure` to call out that the
historical "both ping- and TCP-probe-failure at once" semantics is
split: clients that want the old behavior must send
`enable_failure=true&report_tcp_failure=true` together.
Tests:
- Rename `..._enables_ping_start_success_and_failure_events` →
`..._enables_ping_start_and_success_only`, flip the failure assertion.
- Add `query_mapping_probe_ping_alone_does_not_fallback_to_common_ports`.
- Add `query_mapping_no_probe_falls_back_to_common_ports`.
- Add `query_mapping_probe_ping_with_explicit_port_uses_only_that_port`.
- Add `query_mapping_enable_tcp_probes_false_overrides_explicit_ports`.
- Drop the `report_ping_status` dimension from the boolean-toggle
permutation matrix and simplify the related assertion right-hand sides.
…oints `NetworkScanQueryParams` was being emitted with `in: path` for every query parameter because `IntoParams` defaults to a path location when no explicit `parameter_in` is given. Annotate the struct with `#[into_params(parameter_in = Query)]` so the regenerated schema correctly marks all the new `/jet/net/scan` parameters as query-string parameters. Regenerate `gateway-api.yaml` so it carries: - the new `/jet/net/scan` (GetNetScan) handshake parameters - the new `/jet/net/interfaces` (GetNetInterfaces) endpoint - DTOs: NetworkScanResultEventDto, NetworkScanSourceDto, NetworkScanSourceCapabilitiesDto, HostScanStateDto, ScanResultSourceDto, ScanOriginDto, NetworkScanResultKindDto - the existing `report_ping_status` removal from the previous commit Also bump `subscriber-api.yaml` version stamp to match the workspace. The generated .NET / TS / TS-Angular clients are not regenerated in this commit — that pipeline runs separately.
a3aa448
into
master
Summary
Brings the gateway's
/jet/net/scanAPI closer to feature parity with the local network scanner, so the same scan request shape can drive both.New query parameters
target=<ip>(single host) andrange=<lower>-<upper>. IPv4/IPv6 family validation; rejects ranges over 65 536 addresses with HTTP 400.interface_id=<id>plusrange_interface_policy=intersect_selected_interfaces|allow_cross_interface_range. Missing/down/loopback-only/no-scan-capable sources or out-of-interface ranges return a structured 400 ({ error, interfaceId, reason }or{ error, ranges, interfaceIds }).enable_tcp_probes=falsesuppresses all TCP probes regardless ofprobe=; otherwiseprobe=22|rdp|https|...sets the probe list. An explicit probe list (even justprobe=ping) is honored verbatim — the defaultCOMMON_PORTSfallback only kicks in when the caller sends noprobe=at all.report_ping_start,report_ping_success,report_ping_failure,report_tcp_failure,include_host_results. The previousenable_failureandenable_ping_startflags remain as deprecated aliases (see "Behavior changes" below for theenable_failuresemantics split).max_concurrency,max_ping_concurrency,max_tcp_probe_concurrency.interface_bind_strict=truefails the scan if a per-interface socket bind doesn't take effect (default: warn and fall back).New endpoint
GET /jet/net/interfaces(v2): returns each scan source with a stableid, address,startAddress/endAddress, broadcast address, prefix length, link metadata (MAC, MTU, speed, link type), and per-source capability flags (ipv4,ipv6,subnet,broadcast,zeroConf,tcpProbe,dnsResolve). Theidis whatinterface_id=accepts.The legacy
GET /jet/net/configis retained and now carries RFC 8594Deprecation: trueplusLink: rel="successor-version"headers pointing at/jet/net/interfaces. (NoSunsetdate until product confirms one.)New result format
response_format=network_scan_result_v1opts into a new wire shape:{ "kind": "host", "address": "...", "source": "gateway", "discoverySource": "subnet", "isReachable": true, "hostScanState": "reachable", "responseTimeMs": 7, "interfaceId": "...", "interfaceName": "..." } { "kind": "service", "address": "...", "source": "gateway", "discoverySource": "tcp_probe", "isReachable": true, "port": 3389, "serviceLabel": "RDP", "serviceType": "RDP", "responseTimeMs": 12 }Fields are camelCase with closed lowercase enums (
hostScanState ∈ { queued, probing, reachable, unreachable },discoverySource ∈ { subnet, broadcast, tcp_probe, gateway, zero_conf }).macAddressis in the schema but only populated when neighbor-discovery information is available; it is omitted from the JSON otherwise.Without the parameter, the legacy event format continues to be emitted unchanged.
Internals
IP_UNICAST_IF/IPV6_UNICAST_IF), macOS (IP_BOUND_IF/IPV6_BOUND_IF), and Windows (IP_UNICAST_IF). Other targets fail to compile (compile_error!).TypedReceiver::recvnow treatsRecvError::Laggedas recoverable (warn-and-continue) instead of terminating its consumer task. This fixes a class of dropouts where a wave of timeouts could starve the forwarder and cause subsequent successful events to be lost.ScannerConfigdecomposed into{ ports, timing, limits, targeting }sub-structs.ServiceReachabilityandLinkTypeADTs instead of bare booleans/strings.Behavior changes worth flagging
These are intentional and align the API with the design plan that drove this PR, but they will surprise any client relying on the pre-PR semantics. None of them affect a client that sends no query parameters at all (the default behavior is unchanged).
enable_failure=truenow controls only ping-failure events. Pre-PR it enabled both ping-failure and TCP-probe failure entries in one knob, which generated a lot of noise that callers were filtering out client-side. The two streams are now independently gated — to restore the old "both at once" semantics, sendenable_failure=true&report_tcp_failure=truetogether. New clients should prefer the explicitreport_ping_failure=true/report_tcp_failure=trueflags.probe=pingno longer triggers the COMMON_PORTS fallback. An explicit probe list is taken literally. To also scan the default port list, either omitprobe=entirely or list the ports explicitly.probe=pingno longer auto-emits ping-failure events. Matches pre-PR baseline of start + success only. To receive failure events whenprobe=pingis set, addreport_ping_failure=true(preferred) orenable_failure=true(legacy alias)./jet/net/confignow setsDeprecation: trueplus aLink: ...; rel="successor-version"pointing to/jet/net/interfaces. NoSunsetheader yet, so clients have unbounded migration time — but they should plan to migrate.response_format=network_scan_result_v1is opt-in. Legacy is still the default. Anyone parsing the legacy shape needs no change.report_ping_statusparameter was added and then removed in the course of this PR review and was never shipped to a release. Clients should use the three explicitreport_ping_{start,success,failure}flags directly.Test plan
cargo +nightly fmt --all -- --checkcargo clippy -p devolutions-gateway -p network-scanner --tests -- -D warningscargo test -p devolutions-gateway --lib api::netcargo test -p testsuite --test integration_tests -- network_scanner