mirror of
https://github.com/Horizontal42/netsleuth.git
synced 2026-11-11 01:02:10 +03:00
The only feature in this batch adding a real dependency, approved up front and landed the same way tcptrace/ndt7 already do: an optional extra (uv sync --extra quic), imported lazily inside measure_quic() so a missing install degrades to a clear skip warning instead of a crash. Verified the actually-installed aioquic 1.3.0 API directly rather than assuming. connect() takes local_port but no local_host/source-address parameter at all, so --quic structurally cannot honor --interface - _quic_section() says so explicitly rather than silently measuring the OS-default path under a report claiming a forced adapter. Its context manager doesn't yield until the handshake finishes, so unlike the TLS RTT probe's two-connection subtraction trick, handshake time here is timed directly. HandshakeCompleted's fields matched what was expected; dropped a planned quic_version field since aioquic has no clean public API for it rather than reach into a private attribute. quic_verdict(quic_ok, tcp_ok) is the actual payoff: QUIC failing while plain TCP to the same host succeeds means the network specifically drops or throttles UDP/443, silently degrading Chrome/YouTube's HTTP/3 preference in a way no TCP-based diagnostic would ever catch. Both failing is ordinary unreachability already covered elsewhere, so it stays silent rather than duplicating that finding. Opt-in --quic, excluded from --full - an optionally-installed extra must never become a silent requirement. Added the quic extra to the release workflow's wheel-install verification. Live-tested against real cloudflare.com/google.com: real handshakes, ALPN h3 negotiated, correct end-to-end timing. This completes the 16-feature extension batch from plans/zesty-zooming-wilkinson.md (gitignored, local planning doc).
199 lines
7.2 KiB
YAML
199 lines
7.2 KiB
YAML
timeouts:
|
|
http_seconds: 8.0
|
|
module_seconds: 30.0
|
|
speedtest_seconds: 90.0
|
|
dns_seconds: 4.0
|
|
subprocess_seconds: 60.0
|
|
|
|
probing:
|
|
reference_hosts:
|
|
- { label: "cloudflare-dns", host: "1.1.1.1" }
|
|
- { label: "google-dns", host: "8.8.8.8" }
|
|
- { label: "quad9-dns", host: "9.9.9.9" }
|
|
# Only probed when the host is dual-stack (auto), always (on), or never (off).
|
|
# Latency to these is measured alongside the IPv4 reference hosts, labeled
|
|
# "<label>-v6", so a report can compare the two stacks side by side.
|
|
reference_hosts_v6:
|
|
- { label: "cloudflare-dns", host: "2606:4700:4700::1111" }
|
|
- { label: "google-dns", host: "2001:4860:4860::8888" }
|
|
- { label: "quad9-dns", host: "2620:fe::fe" }
|
|
ipv6: "auto"
|
|
service_hosts:
|
|
- { label: "cloudflare", host: "cloudflare.com" }
|
|
- { label: "google", host: "google.com" }
|
|
- { label: "github", host: "github.com" }
|
|
ping_count: 20
|
|
quick_ping_count: 5
|
|
ping_interval_seconds: 0.25
|
|
ping_timeout_seconds: 2.0
|
|
mtr_cycles: 10
|
|
quick_mtr_cycles: 1
|
|
max_hops: 30
|
|
trace_concurrency: 2
|
|
|
|
speedtest:
|
|
enabled_tiers: ["ookla_bin", "cloudflare", "fastcom"]
|
|
download_sizes_bytes: [1000000, 10000000, 25000000]
|
|
upload_sizes_bytes: [1000000, 5000000]
|
|
cloudflare_base_url: "https://speed.cloudflare.com"
|
|
fastcom_api_url: "https://api.fast.com/netflix/speedtest/v2"
|
|
ndt7_locate_url: "https://locate.measurementlab.net/v2/nearest/ndt/ndt7"
|
|
bufferbloat_probe_interval_seconds: 0.2
|
|
|
|
providers:
|
|
cf_trace_url: "https://www.cloudflare.com/cdn-cgi/trace"
|
|
ip_api_url: "http://ip-api.com/json/"
|
|
freeipapi_url: "https://freeipapi.com/api/json/"
|
|
ipinfo_url: "https://ipinfo.io/"
|
|
ipwhois_url: "https://ipwho.is/"
|
|
ripestat_base_url: "https://stat.ripe.net/data"
|
|
asrank_url: "https://api.asrank.caida.org/v2/graphql"
|
|
peeringdb_base_url: "https://www.peeringdb.com/api"
|
|
internetdb_url: "https://internetdb.shodan.io/"
|
|
abuseipdb_url: "https://api.abuseipdb.com/api/v2/check"
|
|
cymru_origin_zone: "origin.asn.cymru.com"
|
|
cymru_origin6_zone: "origin6.asn.cymru.com"
|
|
cymru_asn_zone: "asn.cymru.com"
|
|
ripestat_max_rows: 200
|
|
ripestat_timeframe_days: 14
|
|
peeringdb_cache_hours: 24
|
|
firehol_refresh_hours: 24
|
|
firehol_netsets:
|
|
- "https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level1.netset"
|
|
- "https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level2.netset"
|
|
- "https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_abusers_30d.netset"
|
|
|
|
dnsbl:
|
|
zones:
|
|
- "zen.spamhaus.org"
|
|
- "bl.spamcop.net"
|
|
- "b.barracudacentral.org"
|
|
- "dnsbl.dronebl.org"
|
|
|
|
thresholds:
|
|
latency_ms: { good: 40.0, warn: 100.0 }
|
|
jitter_ms: { good: 5.0, warn: 20.0 }
|
|
loss_pct: { good: 0.0, warn: 2.0 }
|
|
bufferbloat_ms: { a: 5.0, b: 30.0, c: 60.0, d: 200.0, e: 400.0 }
|
|
vpn_confidence: { likely: 0.40, confirmed: 0.75 }
|
|
tls_handshake_ms: { good: 100.0, warn: 300.0 }
|
|
ttfb_ms: { good: 200.0, warn: 800.0 }
|
|
dns_resolve_ms: { good: 30.0, warn: 120.0 }
|
|
prefix_spread_ms: { good: 20.0, warn: 80.0 }
|
|
tls_cpu_bound_ratio: 2.0
|
|
first_hop_ms: { good: 5.0, warn: 25.0 }
|
|
quic_handshake_ms: { good: 150.0, warn: 400.0 }
|
|
|
|
tls:
|
|
targets:
|
|
- { label: "cloudflare", host: "cloudflare.com" }
|
|
- { label: "google", host: "google.com" }
|
|
port: 443
|
|
concurrency: 4
|
|
ttfb_path: "/"
|
|
verify: true
|
|
# host -> expected SHA-256 of the full DER certificate (openssl x509 -outform DER
|
|
# | sha256sum). Colons/case are normalized. Capped at 32 entries in code.
|
|
pinned_fingerprints: {}
|
|
|
|
# Opt-in via --prefix-bench. Pings the first host of a handful of your own AS's
|
|
# announced prefixes to find the lowest-latency PoP. max_prefixes is capped at 256
|
|
# in code: this benchmarks a sample of your own network, not a mass scan of it.
|
|
prefix_bench:
|
|
max_prefixes: 32
|
|
ping_count: 3
|
|
concurrency: 8
|
|
host_offset: 1
|
|
family: 4
|
|
|
|
# Opt-in via --my-server <host>. Single-host self-diagnostic for provider port
|
|
# blocking/TCP RST injection. ports/concurrency are capped in code so this cannot
|
|
# become a range scanner: never point it at infrastructure you don't own.
|
|
dpi_check:
|
|
ports: [80, 443, 8443, 2083, 2096, 53]
|
|
connect_timeout_seconds: 3.0
|
|
delay_between_ports_seconds: 0.25
|
|
concurrency: 2
|
|
|
|
dns_advanced:
|
|
control_names: ["cloudflare.com", "example.com"]
|
|
doh_endpoints:
|
|
- { name: "cloudflare", url: "https://cloudflare-dns.com/dns-query" }
|
|
- { name: "google", url: "https://dns.google/resolve" }
|
|
- { name: "quad9", url: "https://dns.quad9.net:5053/dns-query" }
|
|
bogus_resolver_ip: "192.0.2.1"
|
|
bogus_probe_name: "cloudflare.com"
|
|
|
|
path_diversity:
|
|
targets:
|
|
- { label: "cloudflare", host: "cloudflare.com" }
|
|
- { label: "discord", host: "discord.com" }
|
|
max_targets: 3
|
|
|
|
# Runs first, before any other probe. Checks a handful of known generate_204-style
|
|
# endpoints for a redirect/tampered response, the classic signature of a public-wifi
|
|
# login wall. check_urls is capped at 4 in code.
|
|
# Opt-in via --quic (needs the optional `quic` extra: uv sync --extra quic).
|
|
# Measures QUIC/HTTP3 handshake time and checks whether UDP/443 is reachable at
|
|
# all -- a network that silently drops or throttles QUIC degrades Chrome/YouTube
|
|
# without ever showing up in an ordinary TCP-based diagnostic.
|
|
quic:
|
|
targets:
|
|
- { label: "cloudflare", host: "cloudflare.com" }
|
|
- { label: "google", host: "google.com" }
|
|
port: 443
|
|
concurrency: 2
|
|
timeout_seconds: 5.0
|
|
|
|
# Opt-in via --pmtud. Binary-searches for the largest DF-set packet that gets
|
|
# through to each target, to catch a VPN/PPPoE-style PMTUD blackhole. targets is
|
|
# capped at 2 in code: this is a path-MTU sweep, not a scan.
|
|
pmtud:
|
|
targets:
|
|
- { label: "cloudflare", host: "cloudflare.com" }
|
|
- { label: "google", host: "google.com" }
|
|
min_bytes: 576
|
|
max_bytes: 1500
|
|
probe_timeout_seconds: 2.0
|
|
|
|
# Opt-in via --ecmp. Traces the first few reference hosts multiple times to spot
|
|
# per-TTL load balancing (different next hops across runs). runs/max_targets are
|
|
# capped in code: this diffs a handful of runs, not a path-sampling campaign.
|
|
ecmp:
|
|
runs: 3
|
|
max_targets: 2
|
|
|
|
captive_portal:
|
|
enabled: true
|
|
check_urls:
|
|
- "http://cp.cloudflare.com/generate_204"
|
|
- "http://connectivitycheck.gstatic.com/generate_204"
|
|
- "http://clients3.google.com/generate_204"
|
|
expected_status: 204
|
|
|
|
output:
|
|
logs_dir: "./logs"
|
|
cache_dir: "./.cache"
|
|
emoji: true
|
|
# Which report artifacts a run writes by default: md, ru-md, json, prom, csv, or all.
|
|
# Overridden per run by --format/--ru/--json.
|
|
formats: ["md"]
|
|
|
|
# auto_diff prints a compact vs-previous-run summary after every run that writes JSON
|
|
# for the same network (silent if there is no prior JSON report to compare against).
|
|
# trend_default_reports is the N used by --trend when no explicit count is given,
|
|
# capped at 200 in code (reading 200 multi-MB JSON reports is the real cost).
|
|
history:
|
|
auto_diff: true
|
|
trend_default_reports: 10
|
|
|
|
watch:
|
|
interval_seconds: 60
|
|
speedtest_every_n_cycles: 10
|
|
dashboard_refresh_hz: 4
|
|
# Optional: POST a JSON alert on a verdict transition during --watch. Overridable
|
|
# per run with --webhook. min_interval_seconds is floored at 30 in code so a
|
|
# flapping link cannot fire the webhook every tick.
|
|
webhook_url: null
|
|
webhook_min_interval_seconds: 300
|
|
webhook_on: ["warn", "crit"]
|