Files
echolot/docs/feature-catalog-and-feasibility.md
T
mrambossekandClaude Opus 5 e3840f54fe Initial commit: capability prober + design docs
Monorepo root for Echolot. Contains the no-root capability prober
(Kotlin/Compose, app.echo_lot.prober) and the four design docs that act
as the contract for the production app and the Go server.

LICENSE is deliberately absent — still undecided, see docs/build-status.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 08:54:28 +02:00

149 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Echolot (Android Network Debug App) — Feature Catalog & Feasibility (v0.1 draft)
Status: brainstorm/design input, 2026-07-29. Premises: F/OSS, no root in v1, Wi-Fi + cellular + USB ethernet, own server infrastructure available, audience = network engineers, detailed archived/exportable measurements with a summary score.
Decisions so far: name = **Echolot** (domain echo-lot.app, scheme echolot://, suggested appId at.rambossek.echolot); **Shizuku support in v1** (not v1.x); **native Kotlin + Jetpack Compose** (no Flutter — all value is in platform-level APIs; Flutter would be a Dart skin over a full Kotlin app and complicates F-Droid reproducible builds); **peer mode with BLE out-of-band control channel** is a headline feature.
## 1. What no-root Android allows and forbids
The whole design hangs on this. Verified against Linux/Android capability rules; re-check details against the current Android release before implementation.
**Allowed without root:**
- UDP/TCP sockets on ephemeral ports, all the usual `setsockopt`s: `IP_TTL`, `IP_RECVERR` (+ `MSG_ERRQUEUE`), `IP_MTU_DISCOVER` (DF bit / probe mode), `IP_TOS`/DSCP, ECN bits via `IP_TOS`, `SO_TIMESTAMP(NS)`.
- ICMP echo: `socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)` and the ICMPv6 equivalent work for any UID on Android (`ping_group_range` is opened system-wide). Echo only — the kernel filters other ICMP types.
- Traceroute: UDP (or ICMP-echo) probes with increasing TTL + `IP_RECVERR`; time-exceeded senders are delivered on the error queue. No raw socket needed.
- Multicast send/receive with `CHANGE_WIFI_MULTICAST_STATE` + `MulticastLock` → mDNS, SSDP, LLMNR.
- Platform info: `ConnectivityManager`/`LinkProperties` (addresses, prefixes, routes, gateways, DNS servers, MTU, NAT64 prefix, private-DNS state, captive-portal state incl. RFC 8908 API), `WifiManager`/`WifiInfo` (RSSI, link speed, channel/band, Wi-Fi standard, security type; SSID/BSSID require fine-location permission), `TelephonyManager` (cell type, signal, APN-ish info), deprecated-but-working `DhcpInfo`.
- Multi-network: `requestNetwork` for `TRANSPORT_CELLULAR`, `TRANSPORT_WIFI`, `TRANSPORT_ETHERNET` concurrently; `Network.bindSocket()` per probe. USB ethernet adapters supported by the kernel appear as ethernet networks.
- `VpnService`: capture/inspect the device's own traffic without root (PCAPdroid model).
**Shizuku tier (v1, no root — ADB-shell privileges via wireless-ADB pairing):**
The `shell` user's SELinux domain is far less restricted than an app's. Verified capabilities to build on (each needs per-release verification; build as version-tagged parsers with graceful degradation, and record in every report which tier produced which evidence):
- `ip neigh` → ARP/ND table: gateway MAC monitoring, ARP-spoof heuristics (moves from root-only to Shizuku tier).
- `ip -6 route` / `ip addr` with lifetimes → RA-derived routes (`proto ra`, expiry), address valid/preferred lifetimes.
- `ip monitor` → live netlink stream of addr/route/neigh changes (watch DHCP renewals and RA churn in real time).
- `svc wifi disable|enable`, `cmd wifi` → trigger reconnect/re-lease programmatically, observe via `ip monitor` + dumpsys.
- `dumpsys network_stack` / `dumpsys wifi` → IpClient logs: DHCP OFFER/ACK details, lease params, provisioning events.
- `logcat` → network-stack log lines.
**Not possible without root (→ future root-only module):**
- Raw/packet sockets: own DHCP packets, ARP, RA sniffing, promiscuous/monitor capture, ESP/GRE probes.
- Binding ports < 1024 (this alone kills a real DHCP client — replies go to UDP 68).
- `/proc/net/*` (blocked since Android 10), netlink `RTM_GETLINK` (blocked since 11) → no ARP/neighbor table, no MAC addresses (own interface reports `02:00:00:00:00:00`).
- Wi-Fi monitor mode, beacon-level analysis.
**Platform gotchas to design around:** Wi-Fi scan throttling (≈4 scans/2 min foreground); fine-location permission (+ location services ON) required for SSID/BSSID/scan results; per-SSID MAC randomization; Doze/background limits → long tests need a foreground service, monitor mode uses WorkManager with coarse intervals; `NetworkInterface` enumeration is partially restricted for modern target SDKs → treat `LinkProperties` per `Network` as the source of truth.
## 2. The original wishlist, item by item
| Item | Feasibility (no root) | Approach |
|---|---|---|
| v4/v6 config readout | Full | `LinkProperties` per network: addresses, prefixes, routes, DNS, MTU, NAT64 prefix. Analyze: address scope/type, privacy extensions in use, prefix sanity, gateway in subnet, DNS reachability. |
| Own DHCP requests + option analysis | **Blocked** | Needs port 68 or raw socket (server replies always go to UDP 68, never the requester's ephemeral port — so send-only broadcast is useless). VpnService does NOT help: TUN is a virtual L3 interface; link-scoped traffic (DHCP/ARP/ND/RA) is handled by IpClient directly on wlan0 and never routed through the VPN, and writing into the TUN only feeds your own stack. v1 fallback: read `DhcpInfo` (server, lease) + diff `LinkProperties` across lease renewals; flag server-outside-subnet, weird lease times, gateway/DNS flapping. Middle tier (v1.x): Shizuku/ADB-shell → parse `dumpsys network_stack` / `dumpsys wifi` IpClient logs (real observed DHCP OFFER/ACK details, lease params, RA provisioning events) — unstable format, needs per-release parser + graceful degradation; verify with a prototype spike. Full crafted-packet prober → root module. |
| IPv6 RA inspection | **Blocked** (raw ICMPv6) | Indirect: multiple v6 default routes with different link-local next-hops, prefixes from unrelated aggregates, default-route churn → "multiple RA sources suspected". M/O flags and lifetimes not readable. |
| Multiple DHCP servers / RAs | Partial | Above heuristics only; reliable detection needs sniffing (root module). Peer mode (see 3.7) can help: a second device may hold a lease from the other server — diff their views. |
| Packet loss with patterns | Full | UDP trains to own server, sequence numbers + timestamps both directions; report per-direction loss, burstiness (Gilbert-Elliott fit), reordering, duplication, jitter distribution — not just a percentage. |
| Multiple IP networks on one segment (VLAN leakage) | Good | mDNS/SSDP/LLMNR inventory: announcements revealing addresses in foreign prefixes; own subnet vs observed peers. Plus DHCP-flap heuristics. |
| NAT analysis | Full | RFC 5780 STUN (needs 2 IPs on server): mapping + filtering behavior, hairpinning, port preservation/allocation pattern. Mapping lifetime measurement (idle-timeout binary search, UDP and TCP separately). CGNAT: traceroute into 100.64.0.0/10, STUN-mapped vs interface addr. Inbound: server connects back to mapped port. |
| MTU measurement | Full (with server) | Both directions with DF via `IP_PMTUDISC_PROBE` + binary search; MSS clamping (server reports SYN MSS seen); PMTU blackhole detection (large-DF timeout while small passes); v6 fragmentation behavior; DNS large-response/TCP-fallback test. |
| IDS/MITM detection | Good (with server) | Echo-comparison principle: server returns exactly what it received — HTTP request bytes, TLS ClientHello (JA4), DSCP/ECN, remaining TTL, source port. App diffs against what it sent. Catches transparent proxies, header injection/stripping, TLS interception (also: cert-chain pinning against out-of-band-fetched chain), DPI resets (SNI-based RST vs timeout), DSCP bleaching. ARP-spoof detection: not possible app-only, but feasible in the Shizuku tier via `ip neigh` polling (gateway MAC change detection). |
| DNS analysis + canary zone | Full | Own resolver implementation (UDP/TCP/DoT/DoH). User-configurable **override resolvers**: full dns.* battery runs once per resolver, in parallel, incl. TTL-integrity (clamping/freezing/serve-stale vs spec-defined reference records) and answer-integrity (RDATA/flags/order/ECS vs ground truth); `dns.compare` diffs all resolvers. Canary zone with unique per-run labels → authoritative server logs actual querying resolver, source AS, EDNS/0x20/DNSSEC behavior, minimization. Client side: provider vs own-server resolver diff, interception test (DNS query to a known non-DNS IP:53 — any answer = transparent redirect), NXDOMAIN wildcarding, DNSSEC validation (known-bad zone must SERVFAIL), rebind protection (does resolver strip answers pointing to RFC1918?), AAAA filtering, DNS64 synthesis correctness, latency/cache behavior. |
## 3. Additional test catalog (suggestions)
### 3.1 IPv6 & dual-stack
- Broken-v6 detection: v6 configured but blackholed (very common). Compare v4 vs v6 for every test; measure real Happy-Eyeballs behavior and fallback delay.
- NAT64/DNS64/464XLAT/CLAT detection and correctness.
- v6 privacy/temporary address usage; source-address selection sanity.
### 3.2 Path & routing
- Paris-style traceroute (fixed flow tuple) v4+v6, UDP and ICMP modes; ECMP path variance by varying flow label/ports.
- Remaining-TTL analysis at server → hop-count asymmetry, transparent-proxy hint (TTL reset).
- Egress identity: public IP, reverse DNS, ASN/geo (server-side lookup), consistency across protocols.
### 3.3 Transport & middleboxes
- ECN negotiation and survival; DSCP bleaching/remarking (server echo).
- Outbound port/protocol sweep: 25, 465, 587, 110/143/993, 22, 53-TCP/UDP, 123, 445, 3389, 1194, 500/4500, 51820, 8080/8443, QUIC 443/UDP. Distinguish RST vs timeout vs proxy-intercepted.
- "Is UDP usable at all" (hotel/guest networks); QUIC vs TCP-443 performance delta.
- Rate-limit/shaping detection: ramp test, token-bucket signature; per-port throttling comparison.
- IPv4 fragmentation and v6 fragment-header survival (server sends fragmented; client reports).
### 3.4 Performance
- Throughput (multi-stream TCP + paced UDP), both directions.
- Bufferbloat: latency under load, RPM-style / Waveform-style grade.
- Idle→active latency step on cellular (RRC state transitions).
- Gateway RTT vs first-external-hop RTT vs server RTT → segment isolation ("is it my Wi-Fi, my router, or my ISP").
### 3.5 Wi-Fi layer (within API limits)
- RSSI/link-speed/channel/band/standard logging over time; roaming log (BSSID transitions, gap duration) in a walk-around mode.
- Same-SSID environment scan: BSSIDs, bands, security types; security-downgrade and evil-twin heuristics (same SSID, different security).
- Channel congestion snapshot from scan results (co-channel BSS count).
### 3.6 Gateway & LAN services
- UPnP-IGD / NAT-PMP / PCP probe: discover, query external IP, attempt (and immediately release) a mapping — capability report + security finding.
- mDNS/SSDP/LLMNR device inventory; duplicate names, foreign-subnet announcements.
- Captive-portal analysis: RFC 8908/8910 API data + multi-URL probe matrix (HTTP/HTTPS/DNS behavior while captive).
- NTP: offset/latency to pool + own server; is 123/UDP intercepted?
### 3.7 Peer mode (two or more devices running the app)
Covers what no server can see: AP client isolation, LAN loss/jitter/throughput, multicast/IGMP delivery, cross-VLAN reachability, band-steering comparison (one device per band), roaming behavior diffing, and "do we even see the same network" (diff both devices' LinkProperties and leases — helps with the multi-DHCP question).
Design: **control plane over BLE GATT** (out-of-band — coordinating over the medium under test is self-defeating; when Wi-Fi is broken is when coordination matters most). Small CBOR messages: orchestration, result summaries. Data plane over the network under test; bulk result exchange syncs later over the network or the server. Third rendezvous path via the server when both devices have cellular. Clock sync for one-way delay: offset handshake over the LAN when usable (BLE jitter too high for precise sync — coarse coordination only). >2 devices: star topology, one coordinator. Permissions: BLUETOOTH_SCAN/CONNECT/ADVERTISE (Android 12+) with `neverForLocation`.
### 3.8 Modes & UX features
- Continuous monitor mode: lightweight periodic checks, outage/change timeline ("what broke at 03:12").
- Diff view: compare any two archived measurements (before/after, Wi-Fi vs cellular vs USB-eth, site A vs site B).
- Simultaneous multi-path run: same battery over Wi-Fi + cellular + ethernet at once via per-network socket binding.
- Scheduled runs via WorkManager (battery-honest).
### 3.9 Later (v1.5/v2)
- VpnService capture module: own-traffic pcap, per-app flows, DNS-leak analysis. Sees only routed L3 app traffic — no DHCP/ARP/ND/RA. Conflicts with real VPNs → separate opt-in.
- Root module: real DHCP prober (all options, multiple-offer capture), RA sniffer (flags, lifetimes, RDNSS), ARP/ND table + spoof detection, monitor-mode Wi-Fi where hardware allows, ESP/GRE reachability.
- Anonymized export (strip/pseudonymize addresses, SSIDs, hostnames consistently within a report).
## 4. Server-side design sketch
One self-contained daemon (Go or Rust; open-source alongside the app), deployable on multiple VMs/locations. Needs at least one host with **two public IPv4 addresses** (RFC 5780 STUN) and native v6.
Components: control API (TLS, versioned, session tokens; sessions correlate all probes of one run); UDP echo/pattern responder with receive-timestamps and observed-header report (TTL, DSCP, ECN, source port, size); TCP endpoints (MSS/window report, connect-back tester); STUN (RFC 5780); authoritative DNS for the canary zone with query logging exposed through the control API; recursive resolver for comparison; HTTPS echo endpoint (returns exact request bytes + ClientHello JA4 + observed TLS params) and reference cert-chain endpoint; large/fragmented-packet senders; NTP responder optional.
Abuse controls from day one (it is an F/OSS-published traffic reflector): session-token gating, per-IP rate limits, no amplification (responses ≤ requests unless session-authenticated), connect-back only to the session's source address.
### 4.1 Self-hosting, enrollment & profiles (decided direction)
Anyone can deploy the server in their own infrastructure. Configuration of the app happens via a **bootstrap QR / paste-string** — same payload, two transports:
```
echolot://enroll?v=1&u=<control-URL>&p=pin-sha256:<SPKI-hash>&t=<enrollment-token>
```
- QR carries only: control endpoint, server SPKI pin, one-time enrollment token. App verifies TLS against the **pin, not CAs** (self-signed homelab certs work), presents token, downloads the full profile: probe targets per location, STUN addresses, canary zone, long-lived credential, `capabilities` list.
- Trust anchor is transferred out-of-band (camera↔screen) → the reference channel is immune to CA-level/on-path interference — exactly the property the MITM/tampering tests need. Enrollment doubles as trust bootstrap.
- Profile is refreshed from the control server on each run → server-side updates without re-scanning; multi-location = one control server handing out all targets, or multiple independent profiles aggregated in the app.
- Capability negotiation via the profile's `capabilities` list (`stun-5780`, `canary-dns`, `connect-back`, `fragmented-send`, …) — third parties will run mismatched server versions forever; degrade gracefully per capability, not per version number.
- App side: CameraX + **ZXing** for scanning (NOT ML Kit — proprietary, would disqualify from F-Droid main repo). Custom URI scheme registered so any scanner app opens the enrollment.
- Server side: same single daemon serves a small admin web UI (localhost or separate admin port, admin password set at install): health/self-test, enrollment-token management (expiry, scopes enroll vs run-tests, per-token rate limits), QR rendered client-side in JS. Reinforces Go single-static-binary + `embed` for the web UI; cross-compile for ARM (VPSes, RPis).
## 5. App architecture notes
- **Decided: Kotlin + coroutines, Jetpack Compose UI. No Flutter** — all differentiating functionality is platform-level (Os sockets, Network callbacks, VpnService, NsdManager, Shizuku Binder, foreground services); Flutter would mean platform channels around a full Kotlin app plus F-Droid build friction.
- Module layout: `core-probe` (measurement engine, no UI deps → JUnit/CLI-testable, desktop-portable), `core-shizuku` (privileged tier, version-tagged parsers), `core-server` (protocol client), `core-peer` (BLE control plane + peer tests), `feature-*` per test family, thin `app` (Compose).
- Sockets via `android.system.Os` for ~95%; plan a small C-over-JNI shim (a few hundred lines, deliberately not Rust — lower contributor bar) for the corners with patchy `Os` coverage: `MSG_ERRQUEUE` reads, cmsg parsing, `SO_TIMESTAMPING`.
- Storage: Room; measurement = schema-versioned JSON (or CBOR) containing raw evidence (every packet summary, every reply, timestamps) + derived findings + per-category verdicts. Export via `ACTION_SEND`/SAF: full JSON + rendered human-readable report (HTML/PDF).
- Scoring: per-category traffic lights that expand into findings, each with severity, plain-language explanation, and raw evidence links. Engineers distrust opaque scores — always show the reasoning chain.
- Permissions: INTERNET, ACCESS_NETWORK_STATE, ACCESS_WIFI_STATE, ACCESS_FINE_LOCATION (Wi-Fi identifiers/scans), CHANGE_WIFI_MULTICAST_STATE, POST_NOTIFICATIONS, FOREGROUND_SERVICE(+type). Explain the location permission prominently — it's the one users will question.
## 6. Open decisions
(Decided: app framework = Kotlin/Compose; Shizuku tier in v1; peer mode with BLE control plane.)
1. Server language/stack: leaning Go (single static binary, embedded admin UI, ARM cross-compile) — confirm vs Rust or composing existing components (e.g. coturn for STUN).
2. minSdk: 26 keeps old spare phones usable; 29+ simplifies (cleaner APIs, but scan throttling and /proc/net loss apply anyway). Suggest 2628 floor, decide per API audit.
3. License: GPLv3 vs Apache-2.0 (server and app can differ).
4. Measurement format: JSON vs CBOR; whether to define the schema first (recommended — it drives both app and server).
5. Canary-zone domain + how much server-side logging to retain (privacy statement needed even for a debug tool).