288 lines
9.7 KiB
Markdown
288 lines
9.7 KiB
Markdown
|
QUIC Connection ID Cache
|
||
|
========================
|
||
|
|
||
|
The connection ID cache is responsible for managing connection IDs, both local
|
||
|
and remote.
|
||
|
|
||
|
Remote Connection IDs
|
||
|
---------------------
|
||
|
|
||
|
For remote connection IDs, we need to be able to:
|
||
|
|
||
|
* add new IDs per connection;
|
||
|
* pick a non-retired ID associated from those available for a connection and
|
||
|
* select a connection ID by sequence number and retire that and all older IDs.
|
||
|
|
||
|
The cache will be implemented as a double ended queue as part of the
|
||
|
QUIC_CONNECTION object. The queue will be sorted by sequence number
|
||
|
and must maintain a count of the number of connection IDs present.
|
||
|
There is no requirement to maintain a global mapping since remote IDs
|
||
|
are only used when sending packets, not receiving them.
|
||
|
|
||
|
In MVP, a many-to-1 matching of Connection IDs per Connection object
|
||
|
is required. Refer third paragraph in [5.1].
|
||
|
|
||
|
When picking a non-retired connection ID for MVP, the youngest available will
|
||
|
be chosen.
|
||
|
|
||
|
Local Connection IDs
|
||
|
--------------------
|
||
|
|
||
|
For local connection IDs, we need to be able to:
|
||
|
|
||
|
* generate a new connection ID and associate it with a connection;
|
||
|
* query if a connection ID is present;
|
||
|
* for a server, map a connection ID to a QUIC_CONNECTION;
|
||
|
* drop all connection IDs associated with a QUIC_CONNECTION;
|
||
|
* select a connection ID by sequence number and retire that and all older IDs
|
||
|
and
|
||
|
* select a connection ID by sequence number and drop that and all older IDs.
|
||
|
|
||
|
All connection IDs issued by our stack must be the same length because
|
||
|
short form packets include the connection ID but no length byte. Random
|
||
|
connection IDs of this length will be allocated. Note that no additional
|
||
|
information will be contained in the connection ID.
|
||
|
|
||
|
There will be one global set of local connection IDs, `QUIC_ROUTE_TABLE`,
|
||
|
which is shared by all connections over all SSL_CTX objects. This is
|
||
|
used to dispatch incoming datagrams to their correct destination and
|
||
|
will be implemented as a dictionary.
|
||
|
|
||
|
### Notes
|
||
|
|
||
|
* For MVP, it would be sufficient to only use a zero length connection ID.
|
||
|
* For MVP, a connection ID to QUIC_CONNECTION mapping need not be implemented.
|
||
|
* Post MVP, funnelling all received packets through a single socket is
|
||
|
likely to be bottleneck.
|
||
|
An alternative would be receiving from <host address, source port> pairs.
|
||
|
* For MVP, the local connection ID cache need only have one element.
|
||
|
I.e. there is no requirement to implement any form of lookup.
|
||
|
|
||
|
Routes
|
||
|
------
|
||
|
|
||
|
A pair of connection IDs identify a route between the two ends of the
|
||
|
communication. This contains the connection IDs of both ends of the
|
||
|
connection and the common sequence number. We need to be able to:
|
||
|
|
||
|
* select a connection ID by sequence number and retire that and all older IDs
|
||
|
and
|
||
|
* select a connection ID by sequence number and drop that and all older IDs.
|
||
|
|
||
|
It is likely that operations on local and remote connection IDs can be
|
||
|
subsumed by the route functionality.
|
||
|
|
||
|
ID Retirement
|
||
|
-------------
|
||
|
|
||
|
Connection IDs are retired by either a [NEW_CONNECTION_ID] or
|
||
|
a [RETIRE_CONNECTION_ID] frame and this is acknowledged by a
|
||
|
RETIRE_CONNECTION_ID or a NEW_CONNECTION_ID frame respectively.
|
||
|
|
||
|
When a retirement frame is received, we can immediately _remove_ the
|
||
|
IDs covered from our cache and then send back an acknowledgement of
|
||
|
the retirement.
|
||
|
|
||
|
If we want to retire a frame, we send a retirement frame and mark the
|
||
|
IDs covered in our cache as _retired_. This means that we cannot send
|
||
|
using any of these IDs but can still receive using them. Once our peer
|
||
|
acknowledges the retirement, we can _remove_ the IDs.
|
||
|
|
||
|
It is possible to receive out of order packets **after** receiving a
|
||
|
retirement notification. It's unclear what to do with these, however
|
||
|
dropping them seems reasonable. The alternative would be to maintain
|
||
|
the route in a _deletable_ state until all packets in flight at the time
|
||
|
of retirement have been acked.
|
||
|
|
||
|
API
|
||
|
---
|
||
|
|
||
|
QUIC connection IDs are defined in #18949 but some extra functions
|
||
|
are available:
|
||
|
|
||
|
```c
|
||
|
/* QUIC connection ID representation. */
|
||
|
#define QUIC_MAX_CONN_ID_LEN 20
|
||
|
|
||
|
typedef struct quic_conn_id_st {
|
||
|
unsigned char id_len;
|
||
|
unsigned char id[QUIC_MAX_CONN_ID_LEN];
|
||
|
#if 0
|
||
|
/* likely required later, although this might not be the ideal location */
|
||
|
unsigned char reset_token[16]; /* stateless reset token is per conn ID */
|
||
|
#endif
|
||
|
} QUIC_CONN_ID;
|
||
|
|
||
|
static ossl_unused ossl_inline int ossl_quic_conn_id_eq(const QUIC_CONN_ID *a,
|
||
|
const QUIC_CONN_ID *b);
|
||
|
|
||
|
/* New functions */
|
||
|
int ossl_quic_conn_id_set(QUIC_CONN_ID *cid, unsigned char *id,
|
||
|
unsigned int id_len);
|
||
|
|
||
|
int ossl_quic_conn_id_generate(QUIC_CONN_ID *cid);
|
||
|
```
|
||
|
|
||
|
### Remote Connection ID APIs
|
||
|
|
||
|
```c
|
||
|
typedef struct quic_remote_conn_id_cache_st QUIC_REMOTE_CONN_ID_CACHE;
|
||
|
|
||
|
/*
|
||
|
* Allocate and free a remote connection ID cache
|
||
|
*/
|
||
|
QUIC_REMOTE_CONN_ID_CACHE *ossl_quic_remote_conn_id_cache_new(
|
||
|
size_t id_limit /* [active_connection_id_limit] */
|
||
|
);
|
||
|
void ossl_quic_remote_conn_id_cache_free(QUIC_REMOTE_CONN_ID_CACHE *cache);
|
||
|
|
||
|
/*
|
||
|
* Add a remote connection ID to the cache
|
||
|
*/
|
||
|
int ossl_quic_remote_conn_id_cache_add(QUIC_REMOTE_CONN_ID_CACHE *cache,
|
||
|
const QUIC_CONNECTION *conn,
|
||
|
const unsigned char *conn_id,
|
||
|
size_t conn_id_len,
|
||
|
uint64_t seq_no);
|
||
|
|
||
|
/*
|
||
|
* Query a remote connection for a connection ID.
|
||
|
* Each connection can have multiple connection IDs associated with different
|
||
|
* routes. This function returns one of these in a non-specified manner.
|
||
|
*/
|
||
|
int ossl_quic_remote_conn_id_cache_get0_conn_id(
|
||
|
const QUIC_REMOTE_CONN_ID_CACHE *cache,
|
||
|
const QUIC_CONNECTION *conn, QUIC_CONN_ID **cid);
|
||
|
|
||
|
/*
|
||
|
* Retire remote connection IDs up to and including the one determined by the
|
||
|
* sequence number.
|
||
|
*/
|
||
|
int ossl_quic_remote_conn_id_cache_retire(
|
||
|
QUIC_REMOTE_CONN_ID_CACHE *cache, uint64_t seq_no);
|
||
|
|
||
|
/*
|
||
|
* Remove remote connection IDs up to and including the one determined by the
|
||
|
* sequence number.
|
||
|
*/
|
||
|
int ossl_quic_remote_conn_id_cache_remove(
|
||
|
QUIC_REMOTE_CONN_ID_CACHE *cache, uint64_t seq_no);
|
||
|
```
|
||
|
|
||
|
### Local Connection ID APIs
|
||
|
|
||
|
```c
|
||
|
typedef struct quic_local_conn_id_cache_st QUIC_LOCAL_CONN_ID_CACHE;
|
||
|
|
||
|
/*
|
||
|
* Allocate and free a local connection ID cache
|
||
|
*/
|
||
|
QUIC_LOCAL_CONN_ID_CACHE *ossl_quic_local_conn_id_cache_new(void);
|
||
|
void ossl_quic_local_conn_id_cache_free(QUIC_LOCAL_CONN_ID_CACHE *cache);
|
||
|
|
||
|
/*
|
||
|
* Generate a new random local connection ID and associate it with a connection.
|
||
|
* For MVP this could just be a zero length ID.
|
||
|
*/
|
||
|
int ossl_quic_local_conn_id_cache_new_conn_id(QUIC_LOCAL_CONN_ID_CACHE *cache,
|
||
|
QUIC_CONNECTION *conn,
|
||
|
QUIC_CONN_ID **cid);
|
||
|
|
||
|
/*
|
||
|
* Remove a local connection and all associated cached IDs
|
||
|
*/
|
||
|
int ossl_quic_local_conn_id_cache_remove_conn(QUIC_LOCAL_CONN_ID_CACHE *cache,
|
||
|
const QUIC_CONNECTION *conn);
|
||
|
|
||
|
/*
|
||
|
* Lookup a local connection by ID.
|
||
|
* Returns the connection or NULL if absent.
|
||
|
*/
|
||
|
QUIC_CONNECTION *ossl_quic_local_conn_id_cache_get0_conn(
|
||
|
const QUIC_LOCAL_CONN_ID_CACHE *cache,
|
||
|
const unsigned char *conn_id, size_t conn_id_len);
|
||
|
|
||
|
/*
|
||
|
* Retire local connection IDs up to and including the one specified by the
|
||
|
* sequence number.
|
||
|
*/
|
||
|
int ossl_quic_local_conn_id_cache_retire(
|
||
|
QUIC_LOCAL_CONN_ID_CACHE *cache, uint64_t from_seq_no);
|
||
|
|
||
|
/*
|
||
|
* Remove local connection IDs up to and including the one specified by the
|
||
|
* sequence number.
|
||
|
*/
|
||
|
int ossl_quic_local_conn_id_cache_remove(
|
||
|
QUIC_LOCAL_CONN_ID_CACHE *cache, uint64_t from_seq_no);
|
||
|
```
|
||
|
|
||
|
### Routes
|
||
|
|
||
|
Additional status and source information is also included.
|
||
|
|
||
|
```c
|
||
|
typedef struct quic_route_st QUIC_ROUTE;
|
||
|
typedef struct quic_route_table QUIC_ROUTE_TABLE;
|
||
|
|
||
|
struct quic_route_st {
|
||
|
QUIC_CONNECTION *conn;
|
||
|
QUIC_CONN_ID local;
|
||
|
QUIC_CONN_ID remote;
|
||
|
uint64_t seq_no; /* Sequence number for both ends */
|
||
|
unsigned int retired : 1; /* Connection ID has been retired */
|
||
|
#if 0
|
||
|
/* Later will require */
|
||
|
BIO_ADDR remote_address;/* remote source address */
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
QUIC_ROUTE_TABLE *ossl_quic_route_table_new(void);
|
||
|
void ossl_quic_route_table_free(QUIC_ROUTE_TABLE *routes);
|
||
|
```
|
||
|
|
||
|
### Add route to route table
|
||
|
|
||
|
```c
|
||
|
int ossl_route_table_add_route(QUIC_ROUTE_TABLE *cache,
|
||
|
QUIC_ROUTE_TABLE *route);
|
||
|
```
|
||
|
|
||
|
### Route query
|
||
|
|
||
|
```c
|
||
|
/*
|
||
|
* Query a route table entry by either local or remote ID
|
||
|
*/
|
||
|
QUIC_ROUTE *ossl_route_table_get0_route_from_local(
|
||
|
const QUIC_ROUTE_TABLE *cache,
|
||
|
const unsigned char *conn_id, size_t conn_id_len);
|
||
|
QUIC_ROUTE *ossl_route_table_get0_route_from_remote(
|
||
|
const QUIC_ROUTE_TABLE *cache,
|
||
|
const unsigned char *conn_id, size_t conn_id_len);
|
||
|
```
|
||
|
|
||
|
### Route retirement
|
||
|
|
||
|
```c
|
||
|
/*
|
||
|
* Retire by sequence number up to and including the one specified.
|
||
|
*/
|
||
|
int ossl_quic_route_table_retire(QUIC_ROUTE_TABLE *routes,
|
||
|
QUIC_CONNECTION *conn,
|
||
|
uint64_t seq_no);
|
||
|
|
||
|
/*
|
||
|
* Delete by sequence number up to and including the one specified.
|
||
|
*/
|
||
|
int ossl_quic_route_table_remove(QUIC_ROUTE_TABLE *routes,
|
||
|
QUIC_CONNECTION *conn,
|
||
|
uint64_t seq_no);
|
||
|
```
|
||
|
|
||
|
[5.1]: (https://datatracker.ietf.org/doc/html/rfc9000#section-5.1)
|
||
|
[active_connection_id_limit]: (https://datatracker.ietf.org/doc/html/rfc9000#section-18.2)
|
||
|
[NEW_CONNECTION_ID]: (https://datatracker.ietf.org/doc/html/rfc9000#section-19.15)
|
||
|
[RETIRE_CONNECTION_ID]: (https://datatracker.ietf.org/doc/html/rfc9000#section-19.16)
|
||
|
[retired]: (https://datatracker.ietf.org/doc/html/rfc9000#section-5.1.2)
|