/* Copyright (C) 2022 CZ.NIC, z.s.p.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /*! * \file * * \brief QUIC connection management. * * \addtogroup xdp * @{ */ #pragma once #include #include #include #include #define MAX_STREAMS_PER_CONN 10 struct ngtcp2_cid; // declaration taken from wherever in ngtcp2 struct knot_quic_creds; struct knot_sweep_stats; // those are equivalent to contrib/ucw/lists.h , just must not be included. typedef struct knot_xquic_ucw_node { struct knot_xquic_ucw_node *next, *prev; } knot_xquic_ucw_node_t; typedef struct knot_xquic_ucw_list { knot_xquic_ucw_node_t head, tail; } knot_xquic_ucw_list_t; typedef struct { void *get_conn; void *user_data; } nc_conn_ref_placeholder_t; typedef struct { knot_xquic_ucw_node_t node; size_t len; uint8_t buf[]; } knot_xquic_obuf_t; typedef struct { struct iovec inbuf; struct iovec *inbuf_fin; knot_xquic_ucw_list_t outbufs; size_t obufs_size; knot_xquic_obuf_t *unsent_obuf; size_t first_offset; size_t unsent_offset; } knot_xquic_stream_t; typedef struct knot_xquic_conn { knot_xquic_ucw_node_t timeout; // MUST be first field of the struct uint64_t last_ts; nc_conn_ref_placeholder_t conn_ref; // placeholder for internal struct ngtcp2_crypto_conn_ref struct ngtcp2_conn *conn; struct gnutls_session_int *tls_session; knot_xquic_stream_t *streams; int16_t streams_count; // number of allocated streams structures int16_t stream_inprocess; // index of first stream that has complete incomming data to be processed (aka inbuf_fin) bool handshake_done; bool session_taken; // TODO ... ? int64_t streams_first; // stream_id/4 of first allocated stream size_t ibufs_size; size_t obufs_size; struct knot_xquic_table *xquic_table; struct knot_xquic_conn *next; } knot_xquic_conn_t; typedef struct knot_xquic_cid { uint8_t cid_placeholder[32]; knot_xquic_conn_t *conn; struct knot_xquic_cid *next; } knot_xquic_cid_t; typedef struct knot_xquic_table { size_t size; size_t usage; size_t pointers; size_t max_conns; size_t ibufs_max; size_t obufs_max; size_t ibufs_size; size_t obufs_size; size_t udp_payload_limit; // for simplicity not distinguishing IPv4/6 void (*log_cb)(const char *); uint64_t hash_secret[4]; struct knot_quic_creds *creds; knot_xquic_ucw_list_t timeout; knot_xquic_cid_t *conns[]; } knot_xquic_table_t; /*! * \brief Allocate QUIC connections hash table. * * \param max_conns Maximum nuber of connections. * \param max_ibufs Maximum size of buffers for fragmented incomming DNS msgs. * \param max_obufs Maximum size of buffers for un-ACKed outgoing data. * \param udp_pl Maximum UDP payload size (both IPv4 and 6). * \param creds QUIC crypto context.. * * \return Allocated table, or NULL. */ knot_xquic_table_t *knot_xquic_table_new(size_t max_conns, size_t max_ibufs, size_t max_obufs, size_t udp_payload, struct knot_quic_creds *creds); /*! * \brief Free QUIC table including its contents. * * \param table Table to be freed. */ void knot_xquic_table_free(knot_xquic_table_t *table); /*! * \brief Close timed out connections and some oldest ones if table full. * * \param table QUIC table to be cleaned up. * \param stats Out: sweep statistics. * * \return KNOT_E* */ int knot_xquic_table_sweep(knot_xquic_table_t *table, struct knot_sweep_stats *stats); /*! * \brief Add new connection/CID link to table. * * \param xconn QUIC connection linked. * \param cid New CID to be added. * \param table QUIC table to be modified. * * \return Pointer on the CID reference in table, or NULL. */ knot_xquic_cid_t **xquic_table_insert(knot_xquic_conn_t *xconn, const struct ngtcp2_cid *cid, knot_xquic_table_t *table); /*! * \brief Add new connection to the table, allocating conn struct. * * \param conn Ngtcp2 conn struct. * \param cid CID to be linked (usually oscid for server). * \param table QUIC table to be modified. * * \return Allocated (and linked) Knot conn struct, or NULL. */ knot_xquic_conn_t *xquic_table_add(struct ngtcp2_conn *conn, const struct ngtcp2_cid *cid, knot_xquic_table_t *table); /*! * \brief Lookup connection/CID link in table. * * \param cid CID to be searched for. * \param table QUIC table. * * \return Pointer on the CID reference in table, or NULL. */ knot_xquic_cid_t **xquic_table_lookup2(const struct ngtcp2_cid *cid, knot_xquic_table_t *table); /*! * \brief Lookup QUIC connection in table. * * \param cid CID to be searched for. * \param table QUIC table. * * \return Connection that the CID belongs to, or NULL. */ knot_xquic_conn_t *xquic_table_lookup(const struct ngtcp2_cid *cid, knot_xquic_table_t *table); /*! * \brief Put the connection on the end of timeout queue. */ void xquic_conn_mark_used(knot_xquic_conn_t *conn, knot_xquic_table_t *table, uint64_t now); /*! * \brief Remove connection/CID link from table. * * \param pcid CID to be removed. * \param table QUIC table. */ void xquic_table_rem2(knot_xquic_cid_t **pcid, knot_xquic_table_t *table); /*! * \brief Remove specified stream from QUIC connection, freeing all buffers. * * \param xconn QUIC connection to remove from. * \param stream_id Stream QUIC ID. */ void xquic_stream_free(knot_xquic_conn_t *xconn, int64_t stream_id); /*! * \brief Remove and deinitialize connection completely. * * \param conn Connection to be removed. * \param table Table to remove from. */ void knot_xquic_table_rem(knot_xquic_conn_t *conn, knot_xquic_table_t *table); /*! * \brief Fetch or initialize a QUIC stream. * * \param xconn QUIC connection. * \param stream_id Stream QUIC ID. * \param create Trigger stream creation if not exists. * * \return Stream or NULL. */ knot_xquic_stream_t *knot_xquic_conn_get_stream(knot_xquic_conn_t *xconn, int64_t stream_id, bool create); /*! * \brief Process incomming stream data to stream structure. * * \param xconn QUIC connection that has received data. * \param stream_id Stream QUIC ID of the incomming data. * \param data Incomming payload data. * \param len Incomming payload data length. * \param fin FIN flag set for incomming data. * * \return KNOT_E* */ int knot_xquic_stream_recv_data(knot_xquic_conn_t *xconn, int64_t stream_id, const uint8_t *data, size_t len, bool fin); /*! * \brief Get next stream which has pending incomming data to be processed. * * \param xconn QUIC connection. * \param stream_id Out: strem QUIC ID of the returned stream. * * \return Stream with incomming data. */ knot_xquic_stream_t *knot_xquic_stream_get_process(knot_xquic_conn_t *xconn, int64_t *stream_id); /*! * \brief Add outgiong data to the stream for sending. * * \param xconn QUIC connection that shall send data. * \param stream_id Stream ID for outgoing data. * \param data Data payload. * \param len Data payload length. * * \return NULL if error, or pinter at the data in outgiong buffer. */ uint8_t *knot_xquic_stream_add_data(knot_xquic_conn_t *xconn, int64_t stream_id, uint8_t *data, size_t len); /*! * \brief Mark outgiong data as acknowledged after ACK received. * * \param xconn QUIC connection that received ACK. * \param stream_id Stream ID of ACKed data. * \param end_acked Offset of ACKed data + ACKed length. * \param keep_stream Don't free the stream even when ACKed all outgoing data. */ void knot_xquic_stream_ack_data(knot_xquic_conn_t *xconn, int64_t stream_id, size_t end_acked, bool keep_stream); /*! * \brief Mark outgoing data as sent. * * \param xconn QUIC connection that sent data. * \param stream_id Stream ID of sent data. * \param amount_sent Length of sent data. */ void knot_xquic_stream_mark_sent(knot_xquic_conn_t *xconn, int64_t stream_id, size_t amount_sent); /*! * \brief Free rest of resources of closed conns. * * \param conns Array with recently used conns (possibly NULLs). * \param n_conns Size of the array. */ void knot_xquic_cleanup(knot_xquic_conn_t *conns[], size_t n_conns); /*! * \brief Toggle sending Retry packet as a reaction to Initial packet of new connection. * * \param table Connection table. * * \return True if instead of continuing handshake, Retry packet shall be sent * to verify counterpart's address. */ bool xquic_require_retry(knot_xquic_table_t *table); /*! @} */