DEFINE_LIST_OF_IMPL(ch, QUIC_CHANNEL);
static void ch_save_err_state(QUIC_CHANNEL *ch);
-static void ch_rx_pre(QUIC_CHANNEL *ch);
static int ch_rx(QUIC_CHANNEL *ch, int channel_only);
static int ch_tx(QUIC_CHANNEL *ch);
-static void ch_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags);
static int ch_tick_tls(QUIC_CHANNEL *ch, int channel_only);
static void ch_rx_handle_packet(QUIC_CHANNEL *ch, int channel_only);
static OSSL_TIME ch_determine_next_tick_deadline(QUIC_CHANNEL *ch);
static void ch_update_idle(QUIC_CHANNEL *ch);
static void ch_update_ping_deadline(QUIC_CHANNEL *ch);
static void ch_stateless_reset(QUIC_CHANNEL *ch);
-static void ch_raise_net_error(QUIC_CHANNEL *ch);
static void ch_on_terminating_timeout(QUIC_CHANNEL *ch);
static void ch_start_terminating(QUIC_CHANNEL *ch,
const QUIC_TERMINATE_CAUSE *tcause,
goto err;
ch_update_idle(ch);
- ossl_quic_reactor_init(&ch->rtor, ch_tick, ch,
- ch_determine_next_tick_deadline(ch));
ossl_list_ch_insert_tail(&ch->port->channel_list, ch);
ch->on_port_list = 1;
return 1;
QUIC_REACTOR *ossl_quic_channel_get_reactor(QUIC_CHANNEL *ch)
{
- return &ch->rtor;
+ return ossl_quic_port_get0_reactor(ch->port);
}
QUIC_STREAM_MAP *ossl_quic_channel_get_qsm(QUIC_CHANNEL *ch)
* at least everything network I/O related. Best effort - not allowed to fail
* "loudly".
*/
-static void ch_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags)
+void ossl_quic_channel_subtick(QUIC_CHANNEL *ch, QUIC_TICK_RESULT *res,
+ uint32_t flags)
{
OSSL_TIME now, deadline;
- QUIC_CHANNEL *ch = arg;
int channel_only = (flags & QUIC_REACTOR_TICK_FLAG_CHANNEL_ONLY) != 0;
/*
* When we tick the QUIC connection, we do everything we need to do
- * periodically. In order, we:
+ * periodically. Network I/O handling will already have been performed
+ * as necessary by the QUIC port. Thus, in order, we:
*
- * - handle any incoming data from the network;
- * - handle any timer events which are due to fire (ACKM, etc.)
- * - write any data to the network due to be sent, to the extent
- * possible;
+ * - handle any packets the DEMUX has queued up for us;
+ * - handle any timer events which are due to fire (ACKM, etc.);
+ * - generate any packets which need to be sent;
* - determine the time at which we should next be ticked.
*/
}
}
- if (!ch->inhibit_tick) {
+ if (!ch->port->inhibit_tick) {
/* Handle RXKU timeouts. */
ch_rxku_tick(ch);
- /* Handle any incoming data from network. */
- ch_rx_pre(ch);
-
do {
/* Process queued incoming packets. */
ch->did_tls_tick = 0;
* Idle timeout differs from normal protocol violation because we do
* not send a CONN_CLOSE frame; go straight to TERMINATED.
*/
- if (!ch->inhibit_tick)
+ if (!ch->port->inhibit_tick)
ch_on_idle_timeout(ch);
res->net_read_desired = 0;
return;
}
- if (!ch->inhibit_tick) {
+ if (!ch->port->inhibit_tick) {
deadline = ossl_ackm_get_loss_detection_deadline(ch->ackm);
if (!ossl_time_is_zero(deadline)
&& ossl_time_compare(now, deadline) >= 0)
ch_update_ping_deadline(ch);
}
- /* Write any data to the network due to be sent. */
+ /* Queue any data to be sent for transmission. */
ch_tx(ch);
/* Do stream GC. */
res->tick_deadline = ch_determine_next_tick_deadline(ch);
/*
- * Always process network input unless we are now terminated.
- * Although we had not terminated at the beginning of this tick, network
- * errors in ch_rx_pre() or ch_tx() may have caused us to transition to the
- * Terminated state.
+ * Always process network input unless we are now terminated. Although we
+ * had not terminated at the beginning of this tick, network errors in
+ * ch_tx() may have caused us to transition to the Terminated state.
*/
res->net_read_desired = !ossl_quic_channel_is_terminated(ch);
- /* We want to write to the network if we have any in our queue. */
+ /* We want to write to the network if we have any data in our TX queue. */
res->net_write_desired
= (!ossl_quic_channel_is_terminated(ch)
&& ossl_qtx_get_queue_len_datagrams(ch->qtx) > 0);
return 1;
}
-/* Process incoming datagrams, if any. */
-static void ch_rx_pre(QUIC_CHANNEL *ch)
-{
- int ret;
-
- if (!ch->is_server && !ch->have_sent_any_pkt)
- return;
-
- /*
- * Get DEMUX to BIO_recvmmsg from the network and queue incoming datagrams
- * to the appropriate QRX instance.
- */
- ret = ossl_quic_demux_pump(ch->port->demux);
- if (ret == QUIC_DEMUX_PUMP_RES_STATELESS_RESET)
- ch_stateless_reset(ch);
- else if (ret == QUIC_DEMUX_PUMP_RES_PERMANENT_FAIL)
- /*
- * We don't care about transient failure, but permanent failure means we
- * should tear down the connection as though a protocol violation
- * occurred. Skip straight to the Terminating state as there is no point
- * trying to send CONNECTION_CLOSE frames if the network BIO is not
- * operating correctly.
- */
- ch_raise_net_error(ch);
-}
-
/* Check incoming forged packet limit and terminate connection if needed. */
static void ch_rx_check_forged_pkt_limit(QUIC_CHANNEL *ch)
{
case QTX_FLUSH_NET_RES_PERMANENT_FAIL:
default:
/* Permanent underlying network BIO, start terminating. */
- ch_raise_net_error(ch);
+ ossl_quic_port_raise_net_error(ch->port);
break;
}
* =======================================
*/
-/* Determines whether we can support a given poll descriptor. */
-static int validate_poll_descriptor(const BIO_POLL_DESCRIPTOR *d)
-{
- if (d->type == BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD && d->value.fd < 0) {
- ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
- return 0;
- }
-
- return 1;
-}
-
-BIO *ossl_quic_channel_get_net_rbio(QUIC_CHANNEL *ch)
-{
- return ch->net_rbio;
-}
-
-BIO *ossl_quic_channel_get_net_wbio(QUIC_CHANNEL *ch)
-{
- return ch->net_wbio;
-}
-
-static int ch_update_poll_desc(QUIC_CHANNEL *ch, BIO *net_bio, int for_write)
-{
- BIO_POLL_DESCRIPTOR d = {0};
-
- if (net_bio == NULL
- || (!for_write && !BIO_get_rpoll_descriptor(net_bio, &d))
- || (for_write && !BIO_get_wpoll_descriptor(net_bio, &d)))
- /* Non-pollable BIO */
- d.type = BIO_POLL_DESCRIPTOR_TYPE_NONE;
-
- if (!validate_poll_descriptor(&d))
- return 0;
-
- if (for_write)
- ossl_quic_reactor_set_poll_w(&ch->rtor, &d);
- else
- ossl_quic_reactor_set_poll_r(&ch->rtor, &d);
-
- return 1;
-}
-
-int ossl_quic_channel_update_poll_descriptors(QUIC_CHANNEL *ch)
-{
- int ok = 1;
-
- if (!ch_update_poll_desc(ch, ch->net_rbio, /*for_write=*/0))
- ok = 0;
-
- if (!ch_update_poll_desc(ch, ch->net_wbio, /*for_write=*/1))
- ok = 0;
-
- return ok;
-}
-
-/*
- * QUIC_CHANNEL does not ref any BIO it is provided with, nor is any ref
- * transferred to it. The caller (i.e., QUIC_CONNECTION) is responsible for
- * ensuring the BIO lasts until the channel is freed or the BIO is switched out
- * for another BIO by a subsequent successful call to this function.
- */
int ossl_quic_channel_set_net_rbio(QUIC_CHANNEL *ch, BIO *net_rbio)
{
- if (ch->net_rbio == net_rbio)
- return 1;
-
- if (!ch_update_poll_desc(ch, net_rbio, /*for_write=*/0))
- return 0;
-
- ossl_quic_demux_set_bio(ch->port->demux, net_rbio);
- ch->net_rbio = net_rbio;
- return 1;
+ return ossl_quic_port_set_net_rbio(ch->port, net_rbio);
}
int ossl_quic_channel_set_net_wbio(QUIC_CHANNEL *ch, BIO *net_wbio)
{
- if (ch->net_wbio == net_wbio)
- return 1;
-
- if (!ch_update_poll_desc(ch, net_wbio, /*for_write=*/1))
- return 0;
-
- ossl_qtx_set_bio(ch->qtx, net_wbio);
- ch->net_wbio = net_wbio;
- return 1;
+ return ossl_quic_port_set_net_wbio(ch->port, net_wbio);
}
/*
if (!ch_tick_tls(ch, /*channel_only=*/0))
return 0;
- ossl_quic_reactor_tick(&ch->rtor, 0); /* best effort */
+ ossl_quic_reactor_tick(ossl_quic_port_get0_reactor(ch->port), 0); /* best effort */
return 1;
}
OSSL_ERR_STATE_save(ch->err_state);
}
-static void ch_stateless_reset(QUIC_CHANNEL *ch)
+static void ossl_unused ch_stateless_reset(QUIC_CHANNEL *ch)
{
QUIC_TERMINATE_CAUSE tcause = {0};
ch_start_terminating(ch, &tcause, 1);
}
-static void ch_raise_net_error(QUIC_CHANNEL *ch)
+void ossl_quic_channel_raise_net_error(QUIC_CHANNEL *ch)
{
QUIC_TERMINATE_CAUSE tcause = {0};
return 1;
}
-void ossl_quic_channel_set_inhibit_tick(QUIC_CHANNEL *ch, int inhibit)
-{
- ch->inhibit_tick = (inhibit != 0);
-}
-
uint16_t ossl_quic_channel_get_diag_num_rx_ack(QUIC_CHANNEL *ch)
{
return ch->diag_num_rx_ack;
static OSSL_TIME get_time(void *arg);
static void port_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags);
static void port_default_packet_handler(QUIC_URXE *e, void *arg);
+static void port_rx_pre(QUIC_PORT *port);
DEFINE_LIST_OF_IMPL(ch, QUIC_CHANNEL);
int ossl_quic_port_set_net_wbio(QUIC_PORT *port, BIO *net_wbio)
{
+ QUIC_CHANNEL *ch;
+
if (port->net_wbio == net_wbio)
return 1;
if (!port_update_poll_desc(port, net_wbio, /*for_write=*/1))
return 0;
- //ossl_qtx_set_bio(port->qtx, net_wbio);
+ LIST_FOREACH(ch, ch, &port->channel_list)
+ ossl_qtx_set_bio(ch->qtx, net_wbio);
+
port->net_wbio = net_wbio;
return 1;
}
*/
static void port_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags)
{
- /* TODO */
+ QUIC_PORT *port = arg;
+ QUIC_CHANNEL *ch;
+
+ res->net_read_desired = 0;
+ res->net_write_desired = 0;
+ res->tick_deadline = ossl_time_infinite();
+
+ if (!port->inhibit_tick) {
+ /* Handle any incoming data from network. */
+ port_rx_pre(port);
+
+ /* Iterate through all channels and service them. */
+ LIST_FOREACH(ch, ch, &port->channel_list) {
+ QUIC_TICK_RESULT subr = {0};
+
+ ossl_quic_channel_subtick(ch, &subr, flags);
+ ossl_quic_tick_result_merge_into(res, &subr);
+ }
+ }
+}
+
+/* Process incoming datagrams, if any. */
+static void port_rx_pre(QUIC_PORT *port)
+{
+ int ret;
+
+ // TODO !have_sent_any_pkt
+
+ /*
+ * Get DEMUX to BIO_recvmmsg from the network and queue incoming datagrams
+ * to the appropriate QRX instances.
+ */
+ ret = ossl_quic_demux_pump(port->demux);
+ // TODO: handle ret, stateless reset
+
+ if (ret == QUIC_DEMUX_PUMP_RES_PERMANENT_FAIL)
+ /*
+ * We don't care about transient failure, but permanent failure means we
+ * should tear down the port. All connections skip straight to the
+ * Terminated state as there is no point trying to send CONNECTION_CLOSE
+ * frames if the network BIO is not operating correctly.
+ */
+ ossl_quic_port_raise_net_error(port);
}
/*
QUIC_PKT_HDR hdr;
QUIC_CHANNEL *new_ch = NULL;
+ // TODO review this
if (port->tserver_ch == NULL)
goto undesirable;
+ // TODO allow_incoming
+ //if (!ossl_assert(ch->is_server))
+ // goto undesirable;
+
+ //TODO if (ch->state != QUIC_CHANNEL_STATE_IDLE)
+ // goto undesirable;
+
/*
* We have got a packet for an unknown DCID. This might be an attempt to
* open a new connection.
undesirable:
ossl_quic_demux_release_urxe(port->demux, e);
}
+
+void ossl_quic_port_set_inhibit_tick(QUIC_PORT *port, int inhibit)
+{
+ port->inhibit_tick = (inhibit != 0);
+}
+
+void ossl_quic_port_raise_net_error(QUIC_PORT *port)
+{
+ QUIC_CHANNEL *ch;
+
+ // TODO fsm
+
+ LIST_FOREACH(ch, ch, &port->channel_list)
+ ossl_quic_channel_raise_net_error(ch);
+}