Passa al contenuto principale

Il fenomeno del "backlog capture" nel protocollo TCP è un effetto che impedisce a un'applicazione server in esecuzione sul Stratus , in ascolto sulla porta X, di stabilire correttamente una connessione con qualsiasi client che tenti di connettersi a tale porta. Le connessioni ad altre porte, anche da parte degli stessi client, funzionano correttamente. La soluzione tipica consiste nell'arrestare e riavviare l'applicazione server. Questo articolo spiegherà perché si verifica il backlog capture e illustrerà altre soluzioni meno drastiche.

Il backlog TCP è la coda delle connessioni in sospeso, ovvero delle richieste di connessione ricevute ma non ancora accettate. Nelle applicazioni compilate su versioni precedenti a OpenVOS 17.1 e in quelle compilate su OpenVOS 17.1 ma prive delle librerie POSIX, le richieste di connessione non ricevono un riconoscimento finché non vengono accettate dall'applicazione. Il client invierà nuovamente il pacchetto di richiesta di connessione da 3 a 10 volte, a intervalli crescenti. I dettagli esatti dipenderanno dal sistema operativo del client e da come è configurato. A un certo punto l'applicazione server accetta la richiesta di connessione e lo stack STCP invia un riconoscimento. Finché tale riconoscimento viene ricevuto prima che il client si sia arreso, la connessione verrà completata e la comunicazione potrà iniziare.

Lo scenario di acquisizione degli ordini arretrati richiede diverse condizioni

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

    Per qualche motivo, l'applicazione server ritarda l'esecuzione della funzione accept.

  2. Il client continua a inviare richieste di connessione; in altre parole, quando la richiesta corrente scade, il client ne invia una nuova. Il tempo di timeout del client deve essere inferiore a quello del server.
  3. Un firewall stateful o un altro dispositivo situato tra il client e il server interrompe le richieste di connessione del client e scarta le conferme di connessione del server senza inviare una risposta al server. Il valore di timeout deve essere inferiore a quello del server.

Purtroppo questa combinazione di circostanze non è così improbabile come potrebbe sembrare a prima vista.

In genere, tutto ha inizio con un'interruzione della rete che impedisce ai pacchetti provenienti dal server di raggiungere il client, ma consente ai pacchetti provenienti dal client di raggiungere il server. Quando il pacchetto di richiesta di connessione del client raggiunge il server, viene inserito nella coda di backlog. Quando l'applicazione server chiama il codice `accept`, quest'ultimo preleva la richiesta di connessione dalla coda di backlog e invia una conferma di connessione. Poiché il server non riceve un riconoscimento per il proprio pacchetto, a causa dell'interruzione della rete, ritrasmette il riconoscimento di connessione. Anche lo stack TCP del client ritrasmetterà la richiesta di connessione poiché non ha ricevuto 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 va in timeout ed estrae la richiesta successiva dalla coda di backlog. Nel frattempo il client ha inviato nuovamente la seconda richiesta e potrebbe essere alla terza. Anche queste richieste si trovano nella coda di backlog. È possibile che un altro client riesca a inserire una richiesta di connessione nella coda di backlog, ma affinché questa venga completata con successo il client avrebbe bisogno di un timeout superiore al timeout del server moltiplicato per la posizione della richiesta di connessione nella coda di backlog. Una volta risolto l'interruzione di rete, il firewall stateful perpetua l'interruzione scartando silenziosamente i riconoscimenti di connessione per i quali non ha più uno stato, poiché mantiene lo stato solo per un tempo inferiore al timeout del server.

Come si fa a riconoscerlo?

Innanzitutto, la coda di lavoro arretrato è piena o, quantomeno, si sta riempiendo. È possibile visualizzare la coda di lavoro arretrato di una connessione con questo comando.

analyze_system -request_line (string match backlog -or syncnt (byte 3Bx) dump_st +
cbq -full -lport 7654 -faddr 0x) -quit 
OpenVOS versione 17.0.2au, analyze_system versione 17.0.2au 
Il processo corrente è 154, ptep 91CC7100, Noah_Davids.CAC 
syncnt                        6 
backlog                       5 
ready  11:41:19

Il valore lport è il numero di porta locale, in questo caso 7654. Il valore dell'indirizzo esterno (faddr), pari a 0, limita l'uscita al solo socket in ascolto. Se il valore di backlog più 1 è uguale al valore di syncnt, la coda di backlog è piena. Se il valore di syncnt è maggiore di 0 e in aumento, la coda di backlog si sta riempiendo. La dimensione della coda di backlog viene impostata dall'applicazione quando chiama la funzione listen.

In secondo luogo, è necessario esaminare una traccia del protocollo di rete. La traccia mostrerà che l'ultima richiesta di connessione non riceve alcun riconoscimento, mentre sono presenti conferme di connessione relative a una richiesta precedente e tali conferme non ricevono alcuna risposta.

La traccia nella figura 1 illustra questo processo. Ogni connessione è contrassegnata da un indice di flusso e identificata da un codice colore. La prima richiesta di connessione proveniente dal client si trova nel frame 1 al tempo 0. Il riconoscimento viene inviato nel frame 2 e successivamente ritrasmesso nel frame 3. Il frame 4 mostra il client che ritrasmette la richiesta di connessione. Poiché STCP sta già inviando i riconoscimenti di connessione, il frame 4 genera solo un riconoscimento TCP nel frame 5. I frame 6 e 7 sono conferme di connessione ritrasmesse. Il frame 8 è una richiesta di connessione ritrasmessa dal client. 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 dal client, ma per una nuova seconda connessione. Si noti che non c'è risposta e il frame 12 mostra una richiesta di connessione ritrasmessa. Il frame 13 mostra un riconoscimento 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 frame 15 mostra una nuova, terza richiesta di connessione da parte del client e i frame 16 e 17 sono ritrasmissioni. Il frame 18 è un'altra ritrasmissione del riconoscimento di connessione per la prima connessione. Il frame 19 è un'altra richiesta di connessione, con i frame 20 e 21 che ne sono le ritrasmissioni, mentre 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 da un riconoscimento di connessione alla seconda richiesta di connessione, con una ritrasmissione nel frame 25. A questo punto penso che abbiate capito il concetto.

Il timeout di STCP è avvenuto tra il frame 0 e il frame 23, per un totale di 106,9 secondi. Sebbene il client non invii un reset al termine del timeout, il tempo che intercorre tra l'inizio della seconda e della 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 rispetto a STCP.

Cosa si può fare per evitarlo?

La soluzione migliore consiste nell'effettuare l'aggiornamento a OpenVOS 17.1 e ricompilare l'applicazione utilizzando le librerie POSIX. In questo modo si eviterà che il problema si verifichi, poiché STCP invierà immediatamente una conferma di connessione, senza attendere che l'applicazione chiami la funzione `accept`. Tuttavia, ciò potrebbe modificare altri comportamenti dell'applicazione, pertanto è necessario eseguire test accurati. Se l'aggiornamento e la ricompilazione non sono possibili, è possibile

  1. Riconfigurare il dispositivo di rete con gestione dello stato che sta ignorando le conferme di connessione in modo che risponda con un comando di reset. Quando STCP riceve il comando di reset, passerà alla richiesta di connessione successiva nella coda di backlog. Di conseguenza, le richieste di connessione per le quali il dispositivo con gestione dello stato ha generato un timeout verranno rapidamente eliminate dalla coda. Eventuali ulteriori conferme di connessione saranno gestite dal client, che potrà inviare un proprio comando di reset oppure confermarle.
  2. Impedire al client di inviare richieste di connessione finché la coda di backlog del server non viene svuotata.
  3. Chiudere il socket di ascolto dell'applicazione server e riaprirlo. Di solito l'applicazione non è progettata per eseguire questa operazione, quindi è necessario arrestare e riavviare l'applicazione server.
  4. Impostare il valore di syn_rcvd_abort su un valore inferiore al timeout del client. Il valore viene modificato tramite la richiesta analyze_system set_stcp_param
    analyze_system -request_line 'set_stcp_param syn_rcvd_abort 15' -quit
    OpenVOS versione 17.0.2au, analyze_system versione 17.0.2au 
    Il processo corrente è 151, ptep 91530AC0, Noah_Davids.CAC 
    Modifica del timeout SYN_RCVD di TCP (syn_rcvd_abort) da off a 15 
    pronto  11:07:44

    Nell'esempio sopra riportato, il timeout è impostato su 15 secondi. I valori ammessi vanno da 1 a 180; il valore 0 indica di utilizzare l'impostazione predefinita (circa 100 secondi).

    Ciò impedirà che un singolo client causi un accumulo eccessivo nella coda di elaborazione. La durata del timer dipenderà dal numero di client che potrebbero accedere al server contemporaneamente. Il problema di questo approccio è che si applica a tutti i client e a tutte le porte del server; non è possibile impostare un valore diverso per un gruppo di porte rispetto a un altro. Inoltre, un valore troppo basso potrebbe causare l'interruzione di alcune connessioni che passano attraverso collegamenti ad alta latenza, mentre queste potrebbero andare a buon fine se il timer fosse più lungo. La scelta di un valore ottimale è più un'arte che una scienza