Späť na blog

tcpdump vidí SYN, ale služba timeoutuje: pasca listen backlogu

Toto je typ incidentu, ktorý ťa prinúti pochybovať o nástrojoch. Klienti timeoutovali. tcpdump na node ukazoval SYN pakety prichádzať na správny interface aj port. Niekedy sme videli aj odchádzajúci SYN-ACK. Ale service logy boli ticho a aplikácia sa správala, akoby sa nikto ani nepokúšal pripojiť.

Príčina nebola firewall, DNS ani “náhodná sieť”. Kernel robil presne to, čo má robiť, keď proces nestíha accept()ovať nové spojenia: prestane ich prijímať. A podľa jedného sysctl-u to z pohľadu klienta vyzerá buď ako nevysvetliteľný timeout (tichý drop), alebo ako rýchly RST.

Prostredie: Linux nody, Kubernetes, vysoký connection churn, CPU throttling / vyťažený event loop / pomalý accept loop

Vzor symptómov

Na tomto probléme je zradné, že každý signál vyzerá sám o sebe “OK”:

  • Klienti: občasné timeouty pri connecte (alebo hneď na prvom requeste).
  • tcpdump: SYN pakety prichádzajú na node/pod IP na správny port.
  • Aplikácia: žiadne access logy, žiadne request logy, niekedy ani žiadna aktivita accept().

Veľmi ľahko potom začneš riešiť nesprávne veci (conntrack, CNI, MTU, health-checky), lebo “pakety sem predsa chodia”.

Skrytý mechanizmus: nie jedna fronta, ale dve

Keď zavoláš listen(fd, backlog), Linux efektívne spravuje dve súvisiace fronty:

  1. SYN fronta (half-open spojenia): prišiel SYN, odišiel SYN-ACK, handshake ešte nie je komplet.
  2. Accept fronta (spojenia pripravené na accept()): handshake je hotový, kernel čaká, kým si proces zavolá accept() a začne čítať.

Ak sa accept fronta zaplní (CPU starvation, málo workerov, blokovanie v accept loope, náročné TLS handshaky v single-thread runtime), kernel musí rozhodnúť, čo spraví s ďalším pokusom o spojenie.

V praxi to znamená:

  • tcpdump vidí SYN, lebo capture je skôr, než aplikácia uvidí socket.
  • tcpdump môže vidieť aj SYN-ACK, lebo kernel dokáže odpovedať aj keď proces “nestíha”.
  • Aplikácia však nič nevidí, lebo nové spojenia sa už nedostanú do accept fronty.

Ako to dokázať (produkčne)

1) Skontroluj frontu na listen socket-e

Na node (alebo v pod netns) si pozri listener:

ss -ltnp 'sport = :443'

Pre listen socket je Recv-Q dobrá praktická aproximácia “koľko hotových spojení čaká na accept()”. Ak počas incidentu naráža na strop, máš tlak na accept frontu.

2) Pozri kernel countery, ktoré neklamú

Linux na to má explicitné štatistiky:

# TcpExt sú dva riadky: najprv názvy, potom hodnoty
grep -A1 '^TcpExt:' /proc/net/netstat | tail -n2

Hľadaj:

  • ListenOverflows — accept fronta overflowla (kernel už nevedel enqueue-núť ďalšie spojenie).
  • ListenDrops — SYN-y na listen sockety boli dropnuté.
  • SyncookiesSent / SyncookiesRecv — SYN cookies aktivita (signál tlaku na SYN frontu).

Ak ti počas incidentu rastie ListenOverflows, je to “smoking gun”.

3) Sysctly, ktoré rozhodujú “timeout vs reset”

sysctl net.core.somaxconn
sysctl net.ipv4.tcp_max_syn_backlog
sysctl net.ipv4.tcp_syncookies
sysctl net.ipv4.tcp_abort_on_overflow

Kľúčový detail:

  • net.ipv4.tcp_abort_on_overflow=0 (častý default): overflow sa často prejaví ako timeout.
  • net.ipv4.tcp_abort_on_overflow=1: overflow sa častejšie prejaví ako rýchly RST (ľahšie sa to debugguje).

Repro lab: ako naschvál spraviť “tcpdump klame”

Tento failure mode vieš reprodukovať na hocijakom Linuxe.

Krok 1: Server, ktorý acceptuje veľmi pomaly (malý backlog)

python3 - <<'PY'
import socket, time
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("0.0.0.0", 8080))
s.listen(1)  # naschvál malé
print("listening on :8080")
while True:
    conn, addr = s.accept()
    time.sleep(5)  # simulácia starvation / zaseknutého accept loopu
    conn.close()
PY

Krok 2: Zaplň ho spojeniami, ktoré ostanú otvorené

# Vyžaduje bash /dev/tcp
for i in $(seq 1 2000); do (exec 3<>/dev/tcp/127.0.0.1/8080; sleep 60) & done
wait

Krok 3: Sleduj plnenie backlogu a overflows

ss -ltn 'sport = :8080'
grep -A1 '^TcpExt:' /proc/net/netstat | tail -n2

Krok 4 (voliteľné): Jeden sysctl zmení symptóm

# 0 -> viac “timeoutov”
sysctl -w net.ipv4.tcp_abort_on_overflow=0

# 1 -> viac “connection reset by peer”
sysctl -w net.ipv4.tcp_abort_on_overflow=1

Rovnaká príčina, iný “pocit” na klientskej strane.

Prečo je to v Kubernetes častejšie

V Kubernetes je jednoduchšie neúmyselne vytvoriť “accept starvation”:

  • CPU limity/throttling: proces je runnable, ale nedostane CPU včas.
  • Single-thread accept loop: jedna pomalá cesta blokuje prijímanie nových spojení.
  • TLS handshake na hot path: veľa práce ešte pred tým, než sa niečo zaloguje.
  • Burst traffic po rolloute: reconnect stormy zaplnia accept frontu.

Preto je “v dev-e to išlo” typická súčasť príbehu.

Fixy, ktoré reálne pomáhajú

Rieš to v tomto poradí (tuning sysctl ti nezachráni server, ktorý je jednoducho preťažený):

  1. Urob accept rýchly a nudný: accept loop musí vždy bežať; ťažkú prácu odovzdaj workerom.
  2. Nastav správnu concurrency: dosť workerov/threads aby si odčerpával accept frontu aj pri špičke.
  3. Bezpečne zväčši backlog: nastav rozumné listen(backlog) a zvýš net.core.somaxconn na nodoch, aby to nebolo capnuté.
  4. Nadimenzuj SYN frontu: ak vidíš SYN cookies, pozri net.ipv4.tcp_max_syn_backlog.
  5. Preferuj rýchly fail: zváž tcp_abort_on_overflow=1, aby klienti dostali okamžitý signál.

Monitoring checklist

  • Alert na rast ListenOverflows (trvalý rate je red flag).
  • Sleduj ListenDrops a SyncookiesSent počas incident okien.
  • Koreluj overflows s CPU throttlingom a reconnect špičkami.
  • Pridaj canary, ktorý meria connect latenciu, nie len HTTP latenciu.
  • Over backlog nastavenia po upgrade node image (defaulty sa menia).

Ak ti tento incident niečo pripomína, je to rovnaká lekcia ako pri iných “tcpdump vidí, ale aplikácia nie”: kernel vie zahodiť paket/spojenie skôr, než sa to dostane na miesto, ktoré bežne debuggujeme.

Súvisiace články

Citujte tento článok

Ak na článok odkazujete, pridajte pôvodnú URL a uveďte autora.

Michal Drozd. "tcpdump vidí SYN, ale služba timeoutuje: pasca listen backlogu". https://www.michal-drozd.com/sk/blog/tcpdump-syn-timeout-backlog-pasca/ (Publikované 3. januára 2026).