Passa al contenuto principale

Il TCP Backlog capture è un effetto che impedisce ad un'applicazione server sul modulo Stratus in ascolto sulla porta X di creare con successo una connessione con qualsiasi client che cerchi di connettersi alla porta X. Le connessioni ad altre porte anche dagli stessi client funzionano bene. La soluzione tipica è quella di spegnere e riavviare l'applicazione server. Questo post spiegherà perché la cattura degli arretrati avviene e altre soluzioni meno drastiche.

Il backlog TCP è la coda delle connessioni in attesa, cioè le richieste di connessione che sono state ricevute ma non ancora accettate. Nelle applicazioni compilate su versioni precedenti a OpenVOS 17.1 e nelle applicazioni compilate su OpenVOS 17.1 ma senza le librerie POSIX, le richieste di connessione non vengono inviate come conferma fino a quando non vengono accettate dall'applicazione. Il client invierà nuovamente il pacchetto di richiesta di connessione tra 3 e 10 volte ad intervalli crescenti. I dettagli esatti dipenderanno dal sistema operativo del client e dalla sua configurazione. Ad un certo punto l'applicazione server accetta la richiesta di connessione e lo stack STCP invia una conferma di ricezione. Finché tale riconoscimento viene ricevuto prima che il client si sia arreso, la connessione sarà completata e la comunicazione potrà iniziare.

Lo scenario di cattura degli arretrati richiede diverse condizioni

  1. C'è un guasto temporaneo della rete tra il server e il client (ma non tra client e server). Ciò consente di ricevere le richieste di connessione del client da parte del server, ma impedisce al client di ricevere le conferme di connessione del server.
    oppure

    L'applicazione server ritarda la chiamata della funzione di accettazione per qualche motivo.

  2. Il cliente continua ad inviare richieste di connessione, cioè quando la richiesta corrente si interrompe e il cliente ne invia una nuova. Il valore di timeout del client deve essere più breve del valore di timeout del server.
  3. Un firewall stateful o qualche altro dispositivo da qualche parte tra il client e il server esegue il times out delle richieste di connessione del client e scarta le conferme di connessione del server senza inviare una risposta al server. Questo valore di timeout deve essere più breve del valore di timeout del server.

Purtroppo questa combinazione di condizioni non è così improbabile come potrebbe sembrare.

Di solito inizia con un'interruzione della rete che impedisce ai pacchetti dal server di raggiungere il client, ma permette ai pacchetti dal client di raggiungere il server. Quando il pacchetto di richiesta di connessione del client raggiunge il server, viene messo nella coda di attesa. Quando l'applicazione del server effettua una chiamata al codice di accettazione, il codice estrae la richiesta di connessione dalla coda in arretrato e invia una conferma di connessione. Poiché il server non riceve un riconoscimento per il suo pacchetto, poiché la rete è interrotta, ritrasmette il riconoscimento della connessione. Anche lo stack TCP del client ritrasmette la richiesta di connessione perché non ha ottenuto un riconoscimento. Dopo un po' lo stack TCP del client si arrende e l'applicazione client invia una nuova richiesta. Il server continua a rispondere alla prima richiesta. Dopo circa un minuto e mezzo (per impostazione predefinita) il server si spegne ed estrae la richiesta successiva dalla coda arretrata. Il client nel frattempo si è risentito della seconda richiesta e potrebbe essere sulla terza. Anche queste richieste sono nella coda di attesa. E' possibile che qualche altro client possa ottenere una richiesta di connessione inserita nella coda backlog ma per completarla con successo il client avrebbe bisogno di un timeout maggiore del timeout del server moltiplicato per la posizione della richiesta di connessione nella coda backlog. Una volta che l'interruzione della rete è stata corretta, il firewall a stato perpetua l'interruzione facendo cadere silenziosamente i riconoscimenti di connessione per cui non ha più uno stato perché mantiene lo stato solo per un tempo più breve del timeout del server.

Come lo riconoscete?

Prima l'arretrato è pieno o almeno lo riempie. È possibile visualizzare la coda degli arretrati per una connessione con questo comando.

analyze_system -request_line (stringa match backlog -o syncnt (byte 3Bx) dump_st +
cbq -full -lport 7654 -faddr 0x) -quit 
OpenVOS Release 17.0.2au, analyse_system Release 17.0.2au 
Il processo attuale è 154, passo 91CC7100, Noah_Davids.CAC 
Sincronizzazione 6 
arretrato 5 
pronto 11:41:19

Il valore lport è il numero di porta locale, 7654 in questo caso. Il valore di indirizzo estero (faddr) di 0 limita l'uscita alla sola presa LISTENING. Se il valore backlog più 1 è uguale al valore di sincronizzazione, il backlog è pieno. Se il valore di sincronizzazione è maggiore di 0 e salendo la coda backlog si riempie. La dimensione della coda backlog viene impostata dall'applicazione quando chiama la funzione di ascolto.

In secondo luogo è necessario guardare una traccia del protocollo di rete. La traccia mostrerà che l'ultima richiesta di connessione non viene riconosciuta mentre ci sono conferme di connessione per una precedente richiesta di connessione e tali conferme non ricevono alcun tipo di risposta.

La traccia in figura 1 lo mostra. Ogni connessione è etichettata con un indice di flusso e anche con un codice colore. La prima richiesta di connessione da parte del cliente è nel frame 1 al tempo 0. Il riconoscimento viene inviato nel frame 2 e poi ritrasmesso nel frame 3. Il frame 4 è il client che ritrasmette la richiesta di connessione. Poiché STCP sta già inviando i riscontri di connessione, il frame 4 attiva solo un riscontro TCP nel frame 5. I frame 6 e 7 sono conferme di connessione ritrasmesse. Il frame 8 è una richiesta di connessione ritrasmessa dal cliente. Il frame 9 è la conferma TCP attivata dalla richiesta di connessione ritrasmessa e il frame 10 è un'altra conferma di connessione ritrasmessa. Il frame 11 è un'altra richiesta di connessione del client, ma per una nuova seconda connessione. Si noti che non vi è alcuna risposta e il frame 12 mostra una richiesta di connessione ritrasmessa. Il frame 13 mostra una conferma di connessione, ma è ancora per la prima connessione. Il frame 14 è una ritrasmissione della seconda richiesta di connessione. Il frame 14 è un'altra ritrasmissione della seconda richiesta di connessione. Il fotogramma 15 mostra una nuova, terza richiesta di connessione del cliente e i fotogrammi 16 e 17 sono ritrasmissioni. Il frame 18 è un'altra ritrasmissione della conferma di connessione alla prima connessione. Il frame 19 è un'altra richiesta di connessione con i frame 20 e 21 le ritrasmissioni e il frame 22 è la quarta nuova richiesta di connessione. Infine nel frame 23 STCP rinuncia alla prima richiesta di connessione e invia un reset seguito nel frame 24 con una conferma di connessione alla seconda richiesta di connessione con una ritrasmissione nel frame 25. A questo punto credo che l'idea sia chiara.

STCP ha preso dal fotogramma 0 al fotogramma 23 al time out, per un totale di 106,9 secondi. Mentre il client non invia un reset al time out, il tempo tra l'inizio della seconda e la terza richiesta di connessione (dal frame 11 al frame 15) è di 29,152 secondi e tra la terza e la quarta (frame 15 e 19) è di 25,8 secondi; molto più veloce di STCP.

Cosa potete fare per evitare questo?

La soluzione migliore è l'aggiornamento ad OpenVOS 17.1 e la ricompilazione dell'applicazione con le librerie POSIX. In questo modo si eviterà che il problema si verifichi, poiché STCP invierà immediatamente un riconoscimento di connessione, senza aspettare che l'applicazione chiami accetta. Potrebbe tuttavia cambiare altri comportamenti dell'applicazione e si consiglia di effettuare un attento test dell'applicazione. Se l'aggiornamento e la ricompilazione non sono possibili è possibile

  1. Riconfigurare il dispositivo di rete a stato che sta facendo cadere i riscontri di connessione per rispondere con un reset. Quando STCP riceve il reset, passerà alla richiesta di connessione successiva nella coda di attesa. Il risultato è che le richieste di connessione interrotte dal dispositivo di stato saranno rapidamente eliminate dalla coda. Ogni ulteriore conferma di connessione sarà gestita dal client che invierà il proprio reset o lo riconoscerà.
  2. Impedire al client di effettuare richieste di connessione fino a quando la coda del server non viene svuotata.
  3. Chiudere il socket di ascolto dell'applicazione server e riaprirlo. In genere, l'applicazione non è progettata per fare questo, quindi è necessario arrestare e riavviare l'applicazione server.
  4. Configurare il valore syn_rcvd_abort a qualcosa che sia più breve del valore di timeout del cliente. Il valore viene cambiato con la richiesta analyze_system set_stcp_param
    analyze_system -request_line 'set_stcp_param syn_rcvd_abort 15' -quit
    OpenVOS Release 17.0.2au, analyse_system Release 17.0.2au 
    Il processo attuale è 151, passo 91530AC0, Noah_Davids.CAC 
    Modifica del timeout tcp SYN_RCVD (syn_rcvd_abort) da off a 15 
    pronto 11:07:44

    Nell'esempio sopra riportato il timeout è impostato su 15 secondi. I valori validi sono compresi tra 1 e 180 con un valore di 0 che significa utilizzare il valore predefinito (circa 100 secondi).

    In questo modo si eviterà che un singolo cliente crei un grande accumulo nella coda degli arretrati. Quanto breve dovrebbe essere il valore dipenderà dal fatto che molti client potrebbero accedere al server contemporaneamente. Il problema di questo approccio è che riguarda tutti i client e tutte le porte del server, non si può avere un valore per un set di porte e un altro per un altro set di porte. Inoltre, un valore troppo basso potrebbe causare il fallimento di alcune connessioni che passano su collegamenti ad alta latenza quando potrebbero avere successo se il timer fosse più lungo. Scegliere un valore ottimale è più arte che scienza

© 2020 Stratus Tecnologie.