feat: add soft scan mode (slow ICMP) to avoid switch/AP rate-limiting
Reduces ICMP concurrency from 100 to 10 workers when soft_scan=true, spreading out probes to avoid rate-limiting on managed switches and APs. The option is hidden in the UI when TCP check is active (redundant). Update README (en/fr/es), docs/backend.md with the new scan modes table and a troubleshooting entry for ICMP rate-limiting. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+12
-1
@@ -429,13 +429,24 @@ npm run dev # Servidor dev Vite en :5173, proxifica /api/ hacia :8000
|
||||
|
||||
**Causa** — Algunos equipos de red (especialmente UniFi Security Gateway, Dream Machine y equipos similares) activan el proxy-ARP y responden a los pings ICMP para **todas** las IPs de la subred, suplantando la IP de origen de la respuesta. La verificación de IP de origen integrada en el escáner no puede filtrar estos falsos positivos.
|
||||
|
||||
**Solución** — Activar la opción **"Verificación TCP (anti proxy-ARP)"** en la pantalla de configuración del escaneo. Esta opción sondea cada host en los puertos TCP 22, 80, 443, 8080 y 8443 tras el ping ICMP:
|
||||
**Solución** — Activar la opción **"Escaneo TCP (anti proxy-ARP)"** en la pantalla de configuración del escaneo. Esta opción usa TCP en lugar de ICMP para detectar hosts activos (puertos 22, 80, 443, 8080, 8443):
|
||||
|
||||
- Un **host real** responde con RST (puerto cerrado) o acepta la conexión → considerado activo.
|
||||
- Una **IP fantasma**: el gateway descarta silenciosamente el SYN sin responder → timeout → eliminada.
|
||||
|
||||
> **Nota**: un equipo cuyo firewall descarte silenciosamente (*DROP*, sin RST) **todos** los puertos sondeados no será descubierto automáticamente y deberá añadirse manualmente.
|
||||
|
||||
### Faltan equipos en el escaneo (rate-limiting ICMP)
|
||||
|
||||
**Síntoma** — Algunos hosts (APs Wi-Fi, switches, dispositivos IoT) responden a un ping directo pero no aparecen en un escaneo completo de la subred.
|
||||
|
||||
**Causa** — Cuando 100 workers ICMP concurrentes inundan un `/24`, algunos equipos o switches gestionados limitan las respuestas ICMP. El equipo ignora la sonda durante el escaneo aunque un ping simple funcione correctamente.
|
||||
|
||||
**Solución** — Dos opciones:
|
||||
|
||||
- Activar **"Escaneo suave (ICMP lento)"**: reduce la concurrencia de 100 a 10 workers. El escaneo tarda más pero las sondas ICMP se distribuyen en el tiempo, evitando el rate-limiting. Ideal para redes sin proxy-ARP.
|
||||
- Activar **"Escaneo TCP (anti proxy-ARP)"**: evita ICMP por completo. Las sondas TCP no están sujetas al mismo rate-limiting. Ideal cuando hay tanto proxy-ARP como rate-limiting.
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Arquitectura
|
||||
|
||||
+12
-1
@@ -429,13 +429,24 @@ npm run dev # Serveur dev Vite sur :5173, proxifie /api/ vers :8000
|
||||
|
||||
**Cause** — Certains équipements réseau (notamment UniFi Security Gateway, Dream Machine et équipements similaires) activent le proxy-ARP et répondent aux pings ICMP pour **toutes** les IP du sous-réseau, en usurpant l'IP source de la réponse. La vérification de l'IP source intégrée au scan ne peut donc pas filtrer ces faux positifs.
|
||||
|
||||
**Solution** — Cocher l'option **"Vérification TCP (anti proxy-ARP)"** dans l'écran de configuration du scan. Cette option sonde chaque hôte sur les ports TCP 22, 80, 443, 8080 et 8443 après le ping ICMP :
|
||||
**Solution** — Cocher l'option **"Scan TCP (anti proxy-ARP)"** dans l'écran de configuration du scan. Cette option utilise TCP au lieu de ICMP pour détecter les hôtes actifs (ports 22, 80, 443, 8080, 8443) :
|
||||
|
||||
- Un **vrai hôte** répond avec RST (port fermé) ou accepte la connexion → considéré vivant.
|
||||
- Une **IP fantôme** : le gateway droppe silencieusement le SYN sans répondre → timeout → éliminée.
|
||||
|
||||
> **Note** : un équipement dont le pare-feu bloque silencieusement (*DROP*, sans RST) **tous** les ports sondés ne sera pas découvert automatiquement et devra être ajouté manuellement.
|
||||
|
||||
### Des équipements manquent dans le scan (rate-limiting ICMP)
|
||||
|
||||
**Symptôme** — Quelques hôtes (AP Wi-Fi, switchs, appareils IoT) répondent à un ping direct mais n'apparaissent pas lors d'un scan complet du sous-réseau.
|
||||
|
||||
**Cause** — Quand 100 workers ICMP concurrents inondent un `/24`, certains équipements ou switchs managés bridant les réponses ICMP. L'équipement ignore la sonde pendant le scan alors qu'un ping simple fonctionne.
|
||||
|
||||
**Solution** — Deux options :
|
||||
|
||||
- Activer **"Scan doux (ICMP lent)"** : réduit la concurrence de 100 à 10 workers. Le scan prend plus de temps mais les sondes ICMP sont étalées, évitant le rate-limiting. Idéal pour les réseaux sans proxy-ARP.
|
||||
- Activer **"Scan TCP (anti proxy-ARP)"** : contourne ICMP entièrement. Les sondes TCP ne sont pas soumises au même rate-limiting. Idéal si proxy-ARP et rate-limiting sont tous deux présents.
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
@@ -429,13 +429,24 @@ npm run dev # Vite dev server on :5173, proxies /api/ to :8000
|
||||
|
||||
**Cause** — Some network equipment (notably UniFi Security Gateway, Dream Machine, and similar devices) enables proxy-ARP and responds to ICMP pings for **every** IP in the subnet, spoofing the source IP of the reply. The built-in source-IP check in the scanner cannot filter these false positives.
|
||||
|
||||
**Fix** — Enable the **"TCP check (anti proxy-ARP)"** option in the scan configuration screen. This option probes each host on TCP ports 22, 80, 443, 8080, and 8443 after the ICMP ping:
|
||||
**Fix** — Enable the **"TCP check (anti proxy-ARP)"** option in the scan configuration screen. This option uses TCP instead of ICMP to detect live hosts (ports 22, 80, 443, 8080, 8443):
|
||||
|
||||
- A **real host** replies with RST (port closed) or accepts the connection → marked alive.
|
||||
- A **ghost IP**: the gateway silently drops the SYN without replying → timeout → discarded.
|
||||
|
||||
> **Note**: a device whose firewall silently drops (*DROP*, without RST) **all** probed ports will not be discovered automatically and must be added manually.
|
||||
|
||||
### Some devices are missing from the scan (ICMP rate-limiting)
|
||||
|
||||
**Symptom** — A few hosts (APs, switches, IoT devices) respond to a direct ping but are not found during a full subnet scan.
|
||||
|
||||
**Cause** — When 100 concurrent ICMP workers flood a `/24`, some devices or managed switches rate-limit ICMP responses. The device drops the probe during the scan even though a single ping works fine.
|
||||
|
||||
**Fix** — Two options:
|
||||
|
||||
- Enable **"Soft scan (slow ICMP)"**: reduces concurrency from 100 to 10 workers. The scan takes longer but ICMP probes are spread out, avoiding rate-limiting. Best for subnets without proxy-ARP.
|
||||
- Enable **"TCP check (anti proxy-ARP)"**: bypasses ICMP entirely. TCP probes are not subject to the same rate-limiting. Best when both proxy-ARP and rate-limiting are present.
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
@@ -29,6 +29,7 @@ class ScanRequest(BaseModel):
|
||||
dns_server: str = ""
|
||||
targets: list[ScanTarget]
|
||||
tcp_check: bool = False
|
||||
soft_scan: bool = False
|
||||
|
||||
@field_validator("dns_server")
|
||||
@classmethod
|
||||
@@ -189,7 +190,11 @@ def scan(req: ScanRequest):
|
||||
t0 = time.time()
|
||||
results: list[DiscoveredHost] = []
|
||||
|
||||
with ThreadPoolExecutor(max_workers=100) as pool:
|
||||
# Soft scan reduces ICMP concurrency to avoid rate-limiting on switches/APs.
|
||||
# Has no effect in tcp_check mode (TCP probes are not rate-limited the same way).
|
||||
workers = 10 if (req.soft_scan and not req.tcp_check) else 100
|
||||
|
||||
with ThreadPoolExecutor(max_workers=workers) as pool:
|
||||
futures = [pool.submit(_scan_one, *args, tcp_check=req.tcp_check) for args in tasks]
|
||||
for f in as_completed(futures):
|
||||
host = f.result()
|
||||
|
||||
+12
-2
@@ -125,7 +125,9 @@ Ping sweep + DNS PTR reverse lookup for one or more CIDR ranges.
|
||||
"dns_server": "192.168.1.1",
|
||||
"targets": [
|
||||
{ "vlan_id": 1, "cidr": "192.168.1.0/24" }
|
||||
]
|
||||
],
|
||||
"tcp_check": false,
|
||||
"soft_scan": false
|
||||
}
|
||||
|
||||
// Response
|
||||
@@ -136,7 +138,15 @@ Ping sweep + DNS PTR reverse lookup for one or more CIDR ranges.
|
||||
}
|
||||
```
|
||||
|
||||
Maximum 1024 hosts per target (rejects /21 and wider). 100 concurrent workers. DNS queries use `dnspython` with a 1s timeout.
|
||||
Maximum 1024 hosts per target (rejects /21 and wider). DNS queries use `dnspython` with a 1s timeout.
|
||||
|
||||
**Scan modes** (mutually exclusive options):
|
||||
|
||||
| `tcp_check` | `soft_scan` | Behaviour |
|
||||
|:-----------:|:-----------:|-----------|
|
||||
| `false` | `false` | ICMP ping, 100 concurrent workers (default) |
|
||||
| `false` | `true` | ICMP ping, 10 concurrent workers — avoids rate-limiting by switches/APs |
|
||||
| `true` | _(ignored)_ | TCP-only (ports 22, 80, 443, 8080, 8443), 100 workers — eliminates proxy-ARP false positives |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -63,6 +63,14 @@
|
||||
<div class="input-hint">{{ t('tcpCheckHint') }}</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!tcpCheck" class="field field-toggle">
|
||||
<label class="toggle-row">
|
||||
<input type="checkbox" v-model="softScan" class="toggle-checkbox" />
|
||||
<span class="toggle-label">{{ t('softScanLabel') }}</span>
|
||||
</label>
|
||||
<div class="input-hint">{{ t('softScanHint') }}</div>
|
||||
</div>
|
||||
|
||||
<div v-if="configError" class="error-box">{{ configError }}</div>
|
||||
</div>
|
||||
|
||||
@@ -186,6 +194,7 @@ const emit = defineEmits(['close', 'refresh'])
|
||||
const step = ref('config')
|
||||
const dnsServer = ref('')
|
||||
const tcpCheck = ref(false)
|
||||
const softScan = ref(false)
|
||||
const selectedVlanIds = ref([])
|
||||
const results = ref([])
|
||||
const selectedIps = ref([])
|
||||
@@ -272,6 +281,7 @@ async function startScan() {
|
||||
dns_server: dnsServer.value.trim(),
|
||||
targets,
|
||||
tcp_check: tcpCheck.value,
|
||||
soft_scan: softScan.value,
|
||||
})
|
||||
results.value = resp.data.hosts
|
||||
scanMeta.value = { total_scanned: resp.data.total_scanned, duration_s: resp.data.duration_s }
|
||||
|
||||
@@ -130,6 +130,8 @@ const LANGS = {
|
||||
scanNote: 'Chaque hôte est pingé puis interrogé en DNS.',
|
||||
tcpCheckLabel: 'Scan TCP (anti proxy-ARP)',
|
||||
tcpCheckHint: 'Utilise TCP au lieu de ICMP pour détecter les hôtes actifs (ports 22, 80, 443, 8080, 8443). Élimine les faux positifs proxy-ARP (UniFi…) et détecte les équipements dont le ping ICMP est bridé sous charge. Peut rater les équipements qui bloquent silencieusement (DROP) tous ces ports.',
|
||||
softScanLabel: 'Scan doux (ICMP lent)',
|
||||
softScanHint: 'Réduit la concurrence ICMP de 100 à 10 workers pour éviter le rate-limiting des switchs et AP. Le scan prend plus de temps mais manque moins d'équipements.',
|
||||
hostsFound: 'hôte(s) découvert(s)',
|
||||
addressesScanned: 'adresses scannées',
|
||||
newHosts: 'nouveaux',
|
||||
@@ -289,6 +291,8 @@ const LANGS = {
|
||||
scanNote: 'Each host is pinged then queried via DNS.',
|
||||
tcpCheckLabel: 'TCP scan (anti proxy-ARP)',
|
||||
tcpCheckHint: 'Uses TCP instead of ICMP to detect live hosts (ports 22, 80, 443, 8080, 8443). Eliminates proxy-ARP false positives (UniFi…) and detects hosts whose ICMP is rate-limited under load. May miss devices that silently block (DROP) all probed ports.',
|
||||
softScanLabel: 'Soft scan (slow ICMP)',
|
||||
softScanHint: 'Reduces ICMP concurrency from 100 to 10 workers to avoid rate-limiting by switches and APs. The scan takes longer but misses fewer devices.',
|
||||
hostsFound: 'host(s) found',
|
||||
addressesScanned: 'addresses scanned',
|
||||
newHosts: 'new',
|
||||
@@ -447,6 +451,8 @@ const LANGS = {
|
||||
scanNote: 'Cada host es pingado y luego consultado en DNS.',
|
||||
tcpCheckLabel: 'Escaneo TCP (anti proxy-ARP)',
|
||||
tcpCheckHint: 'Usa TCP en lugar de ICMP para detectar hosts activos (puertos 22, 80, 443, 8080, 8443). Elimina falsos positivos de proxy-ARP (UniFi…) y detecta hosts con ICMP limitado bajo carga. Puede omitir equipos que bloqueen silenciosamente (DROP) todos los puertos sondeados.',
|
||||
softScanLabel: 'Escaneo suave (ICMP lento)',
|
||||
softScanHint: 'Reduce la concurrencia ICMP de 100 a 10 workers para evitar el rate-limiting de switches y APs. El escaneo tarda más pero detecta más equipos.',
|
||||
hostsFound: 'host(s) descubierto(s)',
|
||||
addressesScanned: 'direcciones escaneadas',
|
||||
newHosts: 'nuevos',
|
||||
|
||||
Reference in New Issue
Block a user