Windows: both 0 (no ICMP rate limiting — matches real Windows behavior)
Linux: 1000ms / mask 6168 (kernel defaults)
BSD: 250ms / mask 6168 (FreeBSD default is faster than Linux)
Embedded/Cisco: both 0 (most firmware doesn't rate-limit ICMP)
These affect nmap's IE and U1 probe groups which measure ICMP error
response timing to closed UDP ports. Windows responds to all probes
instantly while Linux throttles to ~1/sec.
Tests: 10 new cases (5 per sysctl). Suite: 822 passed.
ip_no_pmtu_disc controls PMTU discovery for UDP/ICMP paths only.
TI=Z originates from ip_select_ident() in the kernel TCP stack setting
IP ID=0 for DF=1 TCP packets — a namespace-scoped sysctl cannot change this.
The previous commit was based on incorrect root-cause analysis.
When ip_no_pmtu_disc=0 the Linux kernel sets DF=1 on TCP packets and uses
IP ID=0 (RFC 6864). nmap's TI=Z fingerprint has no Windows match in its DB,
causing 91% confidence guesses of 'Linux 2.4/2.6 embedded' regardless of
TTL being 128. Setting ip_no_pmtu_disc=1 allows non-zero IP ID generation.
Trade-off: DF bit is not set on outgoing packets (slightly wrong for Windows)
but TI=Z is far more damaging to the spoof than losing DF accuracy.
Add tcp_timestamps, tcp_window_scaling, tcp_sack, tcp_ecn, ip_no_pmtu_disc,
and tcp_fin_timeout to every OS profile in OS_SYSCTLS.
All 6 are network-namespace-scoped and safe to set per-container without
--privileged. They directly influence nmap's OPS, WIN, ECN, and T2-T6
probe groups, making OS family detection significantly more convincing.
Key changes:
- tcp_timestamps=0 for windows/embedded/cisco (strongest Windows discriminator)
- tcp_ecn=2 for linux (ECN offer), 0 for all others
- tcp_sack=0 / tcp_window_scaling=0 for embedded/cisco
- ip_no_pmtu_disc=1 for embedded/cisco (DF bit ICMP behaviour)
- Expose _REQUIRED_SYSCTLS frozenset for completeness assertions
Tests: 88 new test cases across all OS families and composer integration.
Total suite: 812 passed.
net.core.rmem_default is a global (non-namespaced) kernel sysctl.
Docker's OCI runtime rejects it at container start with "permission denied"
unless the container runs --privileged. Drop it from the windows profile;
TTL=128 and tcp_syn_retries=2 are sufficient for nmap TTL-based detection.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each decky base container now receives a set of Linux kernel sysctls
(net.ipv4.ip_default_ttl, net.ipv4.tcp_syn_retries, etc.) tuned to
match the claimed OS family, making nmap OS detection return the
expected OS rather than the Linux host.
- decnet/os_fingerprint.py: OS profile table (linux/windows/bsd/embedded/cisco)
keyed by TTL and TCP tuning knobs
- decnet/archetypes.py: Archetype gains nmap_os field; windows-* → "windows",
printer/iot/industrial → "embedded", rest → "linux"
- decnet/config.py: DeckyConfig gains nmap_os field (default "linux")
- decnet/cli.py: nmap_os resolved from archetype → DeckyConfig in both CLI
and INI build paths
- decnet/composer.py: base container gets sysctls + cap_add: [NET_ADMIN];
service containers inherit via shared network namespace
- tests/test_os_fingerprint.py: 48 new tests covering profiles, compose
injection, archetype coverage, and CLI propagation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>