Passa al contenuto principale

5. Connessioni (Connections)

5. Connessioni

Una connessione QUIC è uno stato condiviso tra un client e un server.

Ogni connessione inizia con una fase di handshake, durante la quale i due endpoint stabiliscono chiavi condivise e negoziano il protocollo applicativo utilizzando il protocollo di handshake crittografico [QUIC-TLS]. L'handshake (sezione 7) conferma che entrambi gli endpoint sono disposti a comunicare (sezione 8.1) e stabilisce i parametri per la connessione (sezione 7.4).

Il protocollo applicativo può utilizzare la connessione durante la fase di handshake, ma con alcune limitazioni. 0-RTT consente al client di inviare dati applicativi prima di ricevere una risposta dal server. Tuttavia, 0-RTT non fornisce protezione contro gli attacchi replay; vedere la sezione 9.2 di [QUIC-TLS]. Il server può anche inviare dati applicativi al client prima di ricevere i messaggi di handshake crittografico finali che gli consentono di confermare l'identità e l'attività del client. Queste capacità consentono ai protocolli applicativi di fornire opzioni che scambiano alcune garanzie di sicurezza per una riduzione della latenza.

L'uso degli ID di connessione (sezione 5.1) consente a una connessione di migrare a un nuovo percorso di rete, sia come scelta diretta di un endpoint che quando forzata da cambiamenti di middlebox. La sezione 9 descrive le mitigazioni per i problemi di sicurezza e privacy relativi alla migrazione.

Per le connessioni che non sono più necessarie o desiderate, client e server hanno diversi modi per terminare la connessione, come descritto nella sezione 10.

5.1 [ID di connessione (Connection ID)

Ogni connessione possiede un insieme di identificatori di connessione o ID di connessione, ciascuno dei quali può identificare la connessione. Gli ID di connessione sono selezionati indipendentemente dagli endpoint; ogni endpoint seleziona l'ID di connessione che il suo peer utilizza.

La funzione principale di un ID di connessione è garantire che i cambiamenti di indirizzamento ai livelli di protocollo inferiori (UDP, IP) non causino la consegna di pacchetti di una connessione QUIC all'endpoint sbagliato. Ogni endpoint seleziona gli ID di connessione utilizzando un metodo specifico dell'implementazione (possibilmente specifico del deployment) che consentirà ai pacchetti con quell'ID di connessione di essere instradati verso l'endpoint e identificati dall'endpoint alla ricezione.

Vengono utilizzati più ID di connessione in modo che gli endpoint possano inviare pacchetti che non possono essere identificati come appartenenti alla stessa connessione da un osservatore senza la cooperazione dell'endpoint; vedere la sezione 9.5.

Gli ID di connessione non devono contenere informazioni che un osservatore esterno (cioè un osservatore senza cooperazione con l'emittente) potrebbe utilizzare per correlarli con altri ID di connessione della stessa connessione. Come esempio semplice, questo significa che lo stesso ID di connessione non deve essere emesso più volte sulla stessa connessione.

I pacchetti con intestazione lunga includono i campi ID di connessione sorgente (Source Connection ID) e ID di connessione di destinazione (Destination Connection ID). Questi campi sono utilizzati per stabilire gli ID di connessione per le nuove connessioni; vedere la sezione 7.2 per i dettagli.

I pacchetti con intestazione corta (sezione 17.3) includono solo l'ID di connessione di destinazione e omettono la lunghezza esplicita. La lunghezza del campo ID di connessione di destinazione dovrebbe essere nota all'endpoint. Gli endpoint che utilizzano un bilanciatore di carico che instrada in base all'ID di connessione possono concordare con il bilanciatore di carico su una lunghezza fissa per gli ID di connessione o su uno schema di codifica. Una parte fissa può codificare una lunghezza esplicita, il che consente alla lunghezza dell'intero ID di connessione di variare pur essendo utilizzata dal bilanciatore di carico.

I pacchetti di negoziazione versione (sezione 17.2.1) echeggiano l'ID di connessione selezionato dal client, sia per garantire il corretto instradamento al client che per dimostrare che il pacchetto è una risposta a un pacchetto inviato dal client.

Un ID di connessione di lunghezza zero può essere utilizzato quando l'ID di connessione non è necessario per instradare verso l'endpoint corretto. Tuttavia, quando si utilizza un ID di connessione di lunghezza zero, il multiplexing delle connessioni sullo stesso indirizzo IP locale e porta porterà a fallimenti in presenza di migrazione di connessione peer, rebinding NAT e riutilizzo della porta client. Un endpoint non deve utilizzare lo stesso indirizzo IP e porta per più connessioni concorrenti con un ID di connessione di lunghezza zero a meno che non sia certo che queste funzionalità del protocollo non vengano utilizzate.

Quando un endpoint utilizza un ID di connessione di lunghezza non zero, deve garantire che il suo peer abbia ID di connessione sufficienti tra cui scegliere per i pacchetti inviati all'endpoint. Questi ID di connessione sono forniti dall'endpoint utilizzando frame NEW_CONNECTION_ID (sezione 19.15).

5.1.1 Emissione degli ID di connessione (Issuing Connection IDs)

Ogni ID di connessione ha un numero di sequenza associato per aiutare a rilevare quando i frame NEW_CONNECTION_ID o RETIRE_CONNECTION_ID fanno riferimento allo stesso valore. L'ID di connessione iniziale emesso da un endpoint viene inviato nel campo ID di connessione sorgente dell'intestazione del pacchetto lungo (sezione 17.2) durante l'handshake. Il numero di sequenza dell'ID di connessione iniziale è 0. Se viene inviato il parametro di trasporto preferred_address, l'ID di connessione fornito ha un numero di sequenza di 1.

Gli ID di connessione aggiuntivi sono comunicati al peer utilizzando frame NEW_CONNECTION_ID (sezione 19.15). Il numero di sequenza su ogni ID di connessione appena emesso deve aumentare di 1. L'ID di connessione che il client seleziona per il primo campo ID di connessione di destinazione che invia e qualsiasi ID di connessione fornito da un pacchetto Retry non vengono assegnati numeri di sequenza.

Quando un endpoint emette un ID di connessione, deve accettare pacchetti che portano questo ID di connessione durante la connessione o fino a quando il suo peer invalida l'ID di connessione tramite un frame RETIRE_CONNECTION_ID (sezione 19.16). Gli ID di connessione emessi e non ritirati sono considerati attivi; qualsiasi ID di connessione attivo è valido per la connessione corrente in qualsiasi momento e in qualsiasi tipo di pacchetto. Questo include l'ID di connessione emesso dal server tramite il parametro di trasporto preferred_address.

Un endpoint dovrebbe garantire che il suo peer abbia un numero sufficiente di ID di connessione disponibili e non utilizzati. Gli endpoint pubblicizzano il numero di ID di connessione attivi che sono disposti a mantenere utilizzando il parametro di trasporto active_connection_id_limit. Un endpoint non deve fornire più ID di connessione del limite del peer. Un endpoint può inviare ID di connessione che superano temporaneamente il limite del peer se il frame NEW_CONNECTION_ID richiede anche il ritiro di qualsiasi parte in eccesso includendo un valore sufficientemente grande nel campo Retire Prior To.

Un frame NEW_CONNECTION_ID può causare l'aggiunta di alcuni ID di connessione attivi e il ritiro di altri ID di connessione attivi da parte dell'endpoint in base al valore del campo Retire Prior To. Dopo l'elaborazione di un frame NEW_CONNECTION_ID e l'aggiunta e il ritiro di ID di connessione attivi, se il numero di ID di connessione attivi supera il valore pubblicizzato nel parametro di trasporto active_connection_id_limit, l'endpoint deve chiudere la connessione con un errore di tipo CONNECTION_ID_LIMIT_ERROR.

Quando il peer ritira un ID di connessione, l'endpoint dovrebbe fornire un nuovo ID di connessione. Se l'endpoint ha fornito meno ID di connessione dell'active_connection_id_limit del peer, può fornire un nuovo ID di connessione quando riceve un pacchetto con un ID di connessione precedentemente non utilizzato. Un endpoint può limitare il numero totale di ID di connessione emessi per connessione per evitare il rischio di esaurire gli ID di connessione; vedere la sezione 10.3.2. Un endpoint può anche limitare l'emissione di ID di connessione per ridurre la quantità di stato per percorso che mantiene, come lo stato di validazione del percorso, poiché il suo peer potrebbe interagire con esso tramite tanti percorsi quanti sono gli ID di connessione emessi.

Un endpoint che avvia la migrazione e richiede un ID di connessione di lunghezza non zero dovrebbe garantire che il pool di ID di connessione disponibili per il peer consenta al peer di utilizzare un nuovo ID di connessione durante la migrazione, poiché il peer non sarà in grado di rispondere se il pool è esaurito.

Un endpoint che seleziona un ID di connessione di lunghezza zero durante l'handshake non può emettere nuovi ID di connessione. Un campo ID di connessione di destinazione di lunghezza zero viene utilizzato in tutti i pacchetti inviati a tale endpoint tramite qualsiasi percorso di rete.

5.1.2 Consumo e ritiro degli ID di connessione (Consuming and Retiring Connection IDs)

Un endpoint può cambiare l'ID di connessione che utilizza per il suo peer in un altro ID di connessione disponibile in qualsiasi momento durante la connessione. Un endpoint consuma ID di connessione in risposta a un peer che migra; vedere la sezione 9.5 per ulteriori dettagli.

Un endpoint mantiene un insieme di ID di connessione ricevuti dal suo peer, qualsiasi dei quali può utilizzare durante l'invio di pacchetti. Quando un endpoint desidera rimuovere un ID di connessione dall'uso, invia un frame RETIRE_CONNECTION_ID al suo peer. L'invio di un frame RETIRE_CONNECTION_ID indica che l'ID di connessione non sarà più utilizzato e richiede al peer di sostituirlo con un nuovo ID di connessione con un frame NEW_CONNECTION_ID.

Come descritto nella sezione 9.5, gli endpoint limitano l'uso di un ID di connessione ai pacchetti inviati da un singolo indirizzo locale a un singolo indirizzo di destinazione. Un endpoint dovrebbe ritirare un ID di connessione quando non utilizza più attivamente l'indirizzo locale o di destinazione a cui l'ID di connessione era utilizzato.

Un endpoint potrebbe aver bisogno di cessare di accettare ID di connessione precedentemente emessi in alcune circostanze. Tale endpoint può far sì che il suo peer ritiri ID di connessione inviando un frame NEW_CONNECTION_ID con un campo Retire Prior To aumentato. L'endpoint dovrebbe continuare ad accettare ID di connessione precedentemente emessi fino a quando non vengono ritirati dal peer. Se l'endpoint non può più elaborare gli ID di connessione indicati, può chiudere la connessione.

Dopo aver ricevuto un campo Retire Prior To aumentato, il peer deve cessare di utilizzare gli ID di connessione corrispondenti e ritirarli utilizzando frame RETIRE_CONNECTION_ID prima di aggiungere gli ID di connessione appena forniti all'insieme degli ID di connessione attivi. Questo ordinamento consente a un endpoint di sostituire tutti gli ID di connessione attivi senza la possibilità che il peer non abbia un ID di connessione disponibile e senza superare il limite impostato dal peer nel parametro di trasporto active_connection_id_limit; vedere la sezione 18.2. Il mancato arresto dell'utilizzo degli ID di connessione quando richiesto può portare a un fallimento della connessione, poiché l'endpoint emittente potrebbe non essere in grado di continuare a utilizzare l'ID di connessione nella connessione attiva.

Un endpoint dovrebbe limitare il numero di ID di connessione che ha ritirato localmente ma per i quali i frame RETIRE_CONNECTION_ID non sono ancora stati confermati. Un endpoint dovrebbe consentire l'invio e il tracciamento di almeno il doppio del valore del parametro di trasporto active_connection_id_limit in numero di frame RETIRE_CONNECTION_ID. Un endpoint non deve dimenticare un ID di connessione senza ritirarlo, sebbene possa scegliere di trattare gli ID di connessione che necessitano di essere ritirati oltre questo limite come un errore di connessione di tipo CONNECTION_ID_LIMIT_ERROR.

Un endpoint non dovrebbe emettere aggiornamenti del campo Retire Prior To prima di ricevere frame RETIRE_CONNECTION_ID che ritirano tutti gli ID di connessione indicati dal valore Retire Prior To precedente.

5.2 [Corrispondenza dei pacchetti alle connessioni (Matching Packets to Connections)

I pacchetti in arrivo vengono classificati alla ricezione. I pacchetti possono essere associati a una connessione esistente o, per un server, possono creare una nuova connessione.

Un endpoint tenta di associare un pacchetto a una connessione esistente. Se il pacchetto ha un ID di connessione di destinazione di lunghezza non zero corrispondente a una connessione esistente, QUIC elabora quel pacchetto di conseguenza. Si noti che più ID di connessione possono essere associati a una connessione; vedere la sezione 5.1.

Se l'ID di connessione di destinazione è di lunghezza zero e le informazioni di indirizzamento nel pacchetto corrispondono alle informazioni di indirizzamento che l'endpoint utilizza per identificare una connessione con un ID di connessione di lunghezza zero, QUIC elabora il pacchetto come parte di quella connessione. Un endpoint può utilizzare solo l'IP e la porta di destinazione o sia gli indirizzi di origine che di destinazione per l'identificazione, sebbene ciò renda la connessione fragile come descritto nella sezione 5.1.

Un endpoint può inviare un reset senza stato (sezione 10.3) per qualsiasi pacchetto che non può essere attribuito a una connessione esistente. Un reset senza stato consente al peer di identificare più rapidamente quando una connessione diventa inutilizzabile.

I pacchetti che corrispondono a una connessione esistente vengono scartati se sono incompatibili con lo stato di quella connessione. Ad esempio, i pacchetti vengono scartati se il pacchetto indica una versione di protocollo diversa da quella della connessione o se la rimozione della protezione del pacchetto non riesce una volta che le chiavi previste sono disponibili.

I pacchetti non validi privi di forte protezione dell'integrità (come Initial, Retry o Version Negotiation) possono essere scartati. Se l'endpoint genera un errore di connessione durante l'elaborazione del contenuto di questi pacchetti prima di scoprire un errore, o recupera completamente tutte le modifiche apportate durante tale elaborazione, l'errore di connessione deve essere generato.

5.2.1 Gestione dei pacchetti client (Client Packet Handling)

I pacchetti validi inviati al client includono sempre un ID di connessione di destinazione corrispondente a un valore selezionato dal client. I client che selezionano di ricevere un ID di connessione di lunghezza zero possono utilizzare l'indirizzo locale e la porta per identificare una connessione. I pacchetti che non corrispondono a una connessione esistente, in base all'ID di connessione di destinazione o (se questo valore è di lunghezza zero) all'indirizzo IP locale e alla porta, vengono scartati.

A causa del riordinamento o della perdita di pacchetti, i client possono ricevere pacchetti di connessione cifrati con chiavi che non hanno ancora calcolato. I client possono scartare questi pacchetti o possono metterli in buffer nella speranza che pacchetti successivi consentano loro di calcolare le chiavi.

Se il client riceve un pacchetto utilizzando una versione diversa da quella inizialmente selezionata, deve scartare quel pacchetto.

5.2.2 Gestione dei pacchetti server (Server Packet Handling)

Se il server riceve un pacchetto che indica una versione non supportata e se il pacchetto è sufficientemente grande da avviare una nuova connessione per qualsiasi versione supportata, il server dovrebbe inviare un pacchetto di negoziazione versione come descritto nella sezione 6.1. Il server può limitare il numero di pacchetti a cui risponde con un pacchetto di negoziazione versione. Il server deve scartare i pacchetti più piccoli che specificano una versione non supportata.

Il primo pacchetto di una versione non supportata può utilizzare semantiche e codifiche diverse per qualsiasi campo specifico della versione. In particolare, versioni diverse possono utilizzare chiavi di protezione del pacchetto diverse. Un server che non supporta una versione particolare è improbabile che sia in grado di decifrare il payload del pacchetto o interpretare correttamente il risultato. I server dovrebbero rispondere con un pacchetto di negoziazione versione, a condizione che il datagramma sia sufficientemente grande.

I pacchetti con una versione supportata o senza campo versione vengono abbinati a una connessione utilizzando l'ID di connessione o, per i pacchetti con un ID di connessione di lunghezza zero, l'indirizzo locale e la porta. Questi pacchetti vengono elaborati utilizzando la connessione selezionata; altrimenti, il server continua come descritto di seguito.

Se il pacchetto è un pacchetto Initial completamente conforme, il server continua l'handshake (sezione 7). Questo impegna il server alla versione selezionata dal client.

Se il server rifiuta di accettare una nuova connessione, dovrebbe inviare un pacchetto Initial contenente un frame CONNECTION_CLOSE con il codice di errore CONNECTION_REFUSED.

Se il pacchetto è un pacchetto 0-RTT, il server può mettere in buffer un numero limitato di questi pacchetti nell'attesa di un pacchetto Initial in ritardo. I client non possono inviare pacchetti Handshake prima di ricevere una risposta dal server, quindi il server dovrebbe ignorare qualsiasi pacchetto di questo tipo.

Il server deve scartare i pacchetti in arrivo in tutti gli altri casi.

5.2.3 Considerazioni per i bilanciatori di carico semplici (Considerations for Simple Load Balancers)

I deployment di server possono bilanciare il carico tra i server utilizzando solo gli indirizzi IP e le porte di origine e destinazione. I cambiamenti nell'indirizzo IP o nella porta del client possono far sì che i pacchetti vengano inoltrati al server sbagliato. Tali deployment di server possono utilizzare uno dei seguenti metodi per mantenere la continuità della connessione quando l'indirizzo del client cambia.

  • I server possono utilizzare un meccanismo fuori banda per inoltrare i pacchetti al server corretto in base all'ID di connessione.

  • Se i server possono utilizzare un indirizzo IP o una porta server dedicata (diversa dall'indirizzo o dalla porta a cui il client si è inizialmente connesso), possono utilizzare il parametro di trasporto preferred_address per richiedere al client di spostare la connessione a quell'indirizzo dedicato. Si noti che i client possono scegliere di non utilizzare l'indirizzo preferito.

I server nei deployment che non implementano una soluzione per mantenere la continuità della connessione quando l'indirizzo del client cambia dovrebbero utilizzare il parametro di trasporto disable_active_migration per indicare che la migrazione non è supportata. Il parametro di trasporto disable_active_migration non proibisce la migrazione della connessione del client dopo aver agito sul parametro di trasporto preferred_address.

I deployment di server che utilizzano questa forma semplice di bilanciamento del carico devono evitare di creare un oracolo di reset senza stato; vedere la sezione 21.11.

5.3 [Operazioni sulle connessioni (Operations on Connections)

Questo documento non definisce un'API per QUIC; invece, definisce un insieme di funzioni di una connessione QUIC su cui i protocolli applicativi possono fare affidamento. I protocolli applicativi possono presumere che l'implementazione di QUIC fornisca un'interfaccia che include le operazioni descritte in questa sezione. Le implementazioni progettate per un protocollo applicativo specifico possono fornire solo le operazioni utilizzate da quel protocollo.

Quando si implementa il ruolo client, un protocollo applicativo può:

  • Aprire una connessione, che avvia lo scambio descritto nella sezione 7;
  • Abilitare Early Data (se disponibile);
  • Essere informato che Early Data è stato accettato o rifiutato dal server.

Quando si implementa il ruolo server, un protocollo applicativo può:

  • Ascoltare le connessioni in arrivo, che prepara lo scambio descritto nella sezione 7;
  • Se Early Data è supportato, incorporare dati controllati dall'applicazione nel ticket di ripresa TLS inviato al client;
  • Se Early Data è supportato, recuperare i dati controllati dall'applicazione dal ticket di ripresa del client e accettare o rifiutare Early Data in base a tali informazioni.

In entrambi i ruoli, un protocollo applicativo può:

  • Configurare un valore minimo per il numero iniziale di flussi consentiti di ciascun tipo, come comunicato nei parametri di trasporto (sezione 7.4);
  • Controllare l'allocazione delle risorse del buffer di ricezione impostando limiti di controllo di flusso per i flussi e la connessione;
  • Identificare se l'handshake è stato completato con successo o è ancora in corso;
  • Prevenire la chiusura silenziosa della connessione generando frame PING (sezione 19.2) o richiedendo al trasporto di inviare frame aggiuntivi prima della scadenza del timeout di inattività (sezione 10.1);
  • Chiudere immediatamente (sezione 10.2) la connessione.

Capitolo precedente: 4. Controllo di flusso (Flow Control)
Capitolo successivo: 6. Negoziazione versione (Version Negotiation)