GNU/Linux >> Linux Esercitazione >  >> Linux

Problemi di coda TIME_WAIT

Di recente, abbiamo descritto come configurare il server per un carico elevato e la prevenzione degli attacchi DDoS. Oggi parleremo del problema della coda time_wait. Coloro che sviluppano servizi che lavorano attivamente con la rete possono sfruttare le funzionalità del protocollo TCP:il passaggio di molte porte (o tutte libere) allo stato TIME_WAIT. Ci sono molte informazioni superficiali su Internet e molte informazioni non del tutto corrette. Considereremo quali sono queste situazioni e determineremo le possibili vie d'uscita.

Protocollo TCP:connessione chiusa

Quello che segue è un tipico diagramma del ciclo di vita della connessione TCP:

Schema a vita TCP

Non lo considereremo nel suo insieme, ma ci concentreremo sulla parte più importante per noi:chiudere la connessione. Il soggetto che ha avviato la chiusura del collegamento è detto “attivo”, il secondo – è “passivo”. E non importa quale di loro sia stato l'iniziatore della connessione.

Dal lato “passivo”, tutto è semplice. Dopo aver ricevuto il pacchetto FIN, il sistema dovrebbe rispondere ad esso con il pacchetto ACK appropriato ma ha il diritto di continuare a inviare i dati. Dopo aver ricevuto il pacchetto FIN, la connessione sul lato passivo è nello stato CLOSE_WAIT. Quando è pronto, viene inviato un pacchetto FIN di risposta, dopodiché la parte attende un pacchetto ACK ad esso. Al ricevimento dell'ACK alla risposta FIN, la connessione per il lato passivo viene chiusa.

Dal punto di vista del lato “attivo”, tutto è un po' più complicato. Dopo aver inviato il pacchetto FIN, la parte attiva entra in FIN_WAIT_1. Inoltre, sono possibili tre situazioni:

  1. Ricevi ACK sul pacchetto FIN. Questo stato è indicato da FIN_WAIT_2, i dati possono essere consegnati al lato, dopodiché è previsto un pacchetto di risposta FIN, a cui il lato attivo risponde con un ACK e mette la connessione nello stato TIME_WAIT.
  2. Se il lato passivo è pronto per chiudere la sessione, la risposta FIN può essere ricevuta con un ACK simultaneo al pacchetto FIN originale. In questo caso, il lato attivo risponde con un ACK e trasferisce la connessione a TIME_WAIT, bypassando FIN_WAIT_2.
  3. Una situazione è possibile quando le parti hanno avviato contemporaneamente una chiusura. In questo caso entrambi i lati sono “attivi”, su entrambi i lati la connessione va in stato TIME_WAIT.

Come si evince dallo schema e dalla descrizione, la parte attiva invia l'ultimo pacchetto della sessione (ACK al passivo FIN). Poiché non è in grado di scoprire se questo pacchetto è stato ricevuto, lo stato è TIME_WAIT. In questo stato, la connessione dovrebbe essere 2 * MSL (durata massima del pacchetto):tempo di consegna del pacchetto sul lato passivo + tempo di consegna di un eventuale pacchetto di risposta indietro. In pratica, attualmente, il timer TIME_WAIT è impostato su 1 – 2 minuti. Allo scadere di questo timer, la connessione viene considerata chiusa.

TIME_WAIT problemi per la connessione in uscita

Una connessione nel sistema operativo è identificata da quattro parametri:IP locale, porta locale, IP remoto, porta remota. Supponiamo di avere un client che si connette/disconnette attivamente a un servizio remoto. Poiché sia ​​l'IP che la porta remota rimangono invariati, viene assegnata una nuova porta locale per ogni nuova connessione. Se il client era il lato attivo della fine della sessione TCP, questa connessione verrà bloccata per un po' nello stato TIME_WAIT. Se le connessioni vengono stabilite più velocemente della quarantena delle porte, al successivo tentativo di connessione il client riceverà un errore EADDRNOTAVAIL (errno =99).

Anche se le applicazioni accedono a servizi diversi e non si verifica un errore, la coda TIME_WAIT aumenterà, occupando risorse di sistema. Le connessioni nello stato TIME_WAIT possono essere viste tramite netstat, è conveniente guardare le informazioni generalizzate con l'utilità ss (con il tasto -s).

Cosa si può fare:

  • L'intervallo Linux TIME_WAIT non può essere modificato senza ricompilare il kernel. Su Internet si possono trovare riferimenti al parametro net.ipv4.tcp_fin_timeout con la dicitura “in alcuni sistemi ha effetto su TIME_WAIT”. Tuttavia, cosa siano questi sistemi non è chiaro. Secondo la documentazione, il parametro determina il tempo massimo di attesa per il pacchetto FIN di risposta, ovvero limita il tempo impiegato dalla connessione in FIN_WAIT_2, ma non TIME_WAIT.
  • Apri meno connessioni. L'errore si verifica più spesso durante l'interazione di rete all'interno del cluster. In questo caso, utilizzare Keep-Alive sarebbe una decisione saggia.
  • Durante la progettazione di un servizio, potrebbe avere senso spostare TIME_WAIT dall'altra parte, per cui dovremmo astenerci dall'avviare la chiusura delle connessioni TCP, se possibile.
  • Se è difficile ridurre il numero di connessioni, è opportuno avviare il servizio remoto su più porte e accedervi a turno.
  • Il parametro del kernel “net.ipv4.ip_local_port_range” imposta l'intervallo di porte utilizzate per le connessioni in uscita. Maggiore portata:più connessioni disponibili a un servizio remoto.
  • Un modo difficile ed estremamente pericoloso:ridurre il valore del parametro net.ipv4.tcp_max_tw_bucket a un valore inferiore al numero di IP nell'intervallo da ip_local_port_range. Questo parametro imposta la dimensione massima della coda TIME_WAIT e viene utilizzato per la protezione dagli attacchi DOS. Questo "trucco" può essere utilizzato temporaneamente fino a quando non viene sviluppata una soluzione corretta.
  • Abilita il parametro net.ipv4.tcp_tw_reuse. Questo parametro consente l'utilizzo di connessioni nello stato TIME_WAIT per le connessioni in uscita.
  • Abilita parametro net.ipv4.tcp_tw_recycle.
  • Usa la modalità SO_LINGER (impostata tramite setockopt). In questo caso, la sessione TCP non verrà chiusa (scambio di pacchetti FIN) ma scartata. La parte che desidera eseguire un ripristino invia un pacchetto RST. Al ricevimento di questo pacchetto, la connessione si considera terminata. Tuttavia, secondo il protocollo, l'invio di un pacchetto RST dovrebbe essere effettuato solo in caso di errore (ricezione di dati chiaramente non correlati a questa connessione).

TIME_WAIT sui server

Il pericolo principale che la coda TIME_WAIT si espanda sul server è l'esaurimento delle risorse.

Tuttavia, possono verificarsi spiacevoli incidenti quando si lavora con client NAT (quando un gran numero di client server si trova dietro un IP). Nel caso di un piccolo tempo di quarantena della porta sul Firewall, è probabile che il server riceva una richiesta di connessione dalla stessa porta, la cui connessione non è ancora chiusa (situata in TIME_WAIT). In questo caso sono possibili due-tre scenari:

  • Il cliente (improbabile) indovinerà il numero SEQ, il che è altamente improbabile. In questo caso, il comportamento non è definito.
  • Il client invierà il pacchetto con quello errato (dal punto di vista del server, il numero SEQ), al quale il server risponderà con l'ultimo pacchetto ACK, che il client non comprende più. Il client in genere invia RST a questo ACK e attende alcuni secondi prima di un nuovo tentativo di connessione. Se il parametro “net.ipv4.tcp_rfc1337” è disabilitato sul server (off per impostazione predefinita), un nuovo tentativo avrà esito positivo. Tuttavia, principalmente a causa del timeout, si osserverà un calo delle prestazioni.
  • Se, nella situazione descritta a p.2, il parametro net.ipv4.tcp_rfc1337 è abilitato, il server ignorerà il pacchetto RST del client. Tentativi ripetuti di connessione al server dalla stessa porta falliranno. Per il cliente, il servizio non sarà più disponibile.

Cosa si può fare sul lato server.

  1. Prova a spostare l'inizio della chiusura della connessione al client. In tal modo, è necessario impostare timeout ragionevoli.
  2. Fai attenzione con il parametro net.ipv4.tcp_max_tw_bucket. Impostarlo troppo grande renderà il server vulnerabile a un attacco DOS.
  3. Usa SO_LINGER per query ovviamente errate. Se il client si connette e invia "nonsense", è probabile che ci sia un attacco su cui è meglio spendere la quantità minima di risorse.
  4. Abilita net.ipv4.tcp_tw_recycle se sei sicuro che i client non passino tramite NAT. È importante notare che net.ipv4.tcp_tw_reuse non influisce sull'elaborazione delle connessioni in entrata.
  5. In alcuni casi, ha senso non "combattere" la coda, ma distribuirla correttamente. In particolare, le seguenti ricette possono aiutare:
    • Quando si utilizza il bilanciatore L7, tutti i pacchetti provengono dallo stesso IP, che provoca "hit" nella connessione TIME_WAIT, ma in questo caso puoi tranquillamente abilitare tcp_tw_recycle.
    • Quando si utilizza il bilanciatore L3, il server vede gli indirizzi IP di origine. Il bilanciamento IP-HASH, allo stesso tempo, inoltrerà tutte le connessioni per un NAT a un server, aumentando anche la probabilità di una collisione. Round-Robin è più affidabile in questo senso.
    • Evita di utilizzare NAT all'interno della rete ove possibile. Se necessario, è meglio preferire la traduzione 1 in 1.
    • Puoi aumentare il numero di connessioni disponibili ospitando il servizio su più porte. Ad esempio, per un server WEB, il carico può essere bilanciato non su una 80a porta, ma su un pool di porte.
  6. Se il problema è causato da NAT all'interno della rete, è possibile risolvere la situazione riconfigurando la traduzione sul dispositivo di rete:è necessario assicurarsi che il tempo di “quarantena” della porta su NAT sia superiore a TIME_WAIT. Ma in questo caso, aumenta il rischio di esaurire le porte sul traduttore NAT (come opzione, la traduzione non è verso un IP, ma verso il pool).

Parametri del kernel net.ipv4.tcp_tw_reuse e net.ipv4.tcp_tw_recycle

Ci sono due parametri nel kernel Linux che consentono di violare i requisiti del protocollo TCP, liberando le connessioni da TIME_WAIT prima del previsto. Entrambe queste opzioni si basano sull'estensione TCP-timestamps (contrassegnando i pacchetti con relativi timestamp).

net.ipv4.tcp_tw_reuse consente di utilizzare la connessione a TIME_WAIT per una nuova connessione in uscita. In questo caso, il nuovo timestamp della connessione TCP dovrebbe essere un ordine di grandezza maggiore dell'ultimo valore nella sessione precedente. In questo caso il server potrà distinguere il pacchetto “in ritardo” dalla connessione precedente da quella attuale. L'uso di un parametro è sicuro nella maggior parte dei casi. Possono sorgere problemi se è presente un firewall di “tracciamento” lungo un percorso che decide di non perdere il pacchetto nella connessione, che dovrebbe essere in TIME_WAIT.

net.ipv4.tcp_tw_recycle riduce il tempo di connessione nella coda TIME_WAIT al valore RTO (Re-Transmission Time-Out), che viene calcolato in base al Round-Trip-Time (RTT) e allo spread di questo valore. Allo stesso tempo, l'ultimo valore del timestamp TCP viene salvato nel kernel e i pacchetti con un valore inferiore vengono semplicemente scartati. Questa opzione renderà il servizio non disponibile per i client dietro NAT se i timestamp TCP dei client vengono "saltati" durante la traduzione (se NAT li rimuove o li sostituisce con i propri, non ci saranno problemi). Poiché non è possibile prevedere le impostazioni dei dispositivi esterni, questa opzione è fortemente sconsigliata per l'inclusione su server accessibili da Internet. Inoltre, sui server "interni" dove non c'è NAT (o viene utilizzata l'opzione 1-in-1), l'opzione è sicura.


Linux
  1. Ssh – Utilizzo di un canale Ssh già stabilito?

  2. Linux:rilevamento della connessione/disconnessione delle cuffie in Linux?

  3. Errori di connessione RDP:certificato autofirmato scaduto

  4. Test dei servizi di rete con Netcat

  5. Rintracciare le perdite di connessione MySQL

Configura una connessione di rete statica in Linux

Come creare una coda SQS su AWS

Risoluzione dei problemi:errori di connessione al server

Analizzatore di pacchetti:15 esempi di comandi TCPDUMP

Controlla continuamente lo stato della connessione OpenVPN

2 stampanti 1 coda