Skip to content

Commit 349be6e

Browse files
committed
work-in-progress network API
1 parent 5b8b2ac commit 349be6e

6 files changed

Lines changed: 237 additions & 9 deletions

File tree

picoquic/picoqmux.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#pragma once

picoquic/picoquic.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ typedef enum {
202202
picoquic_state_disconnected
203203
} picoquic_state_enum;
204204

205+
205206
/*
206207
* Transport parameters, as defined by the QUIC transport specification.
207208
* The initial code defined the type as an enum, but the binary representation
@@ -1145,6 +1146,31 @@ int picoquic_subscribe_to_quality_update_per_path(picoquic_cnx_t* cnx, uint64_t
11451146
void picoquic_subscribe_to_quality_update(picoquic_cnx_t* cnx, uint64_t pacing_rate_delta, uint64_t rtt_delta);
11461147
void picoquic_default_quality_update(picoquic_quic_t* quic, uint64_t pacing_rate_delta, uint64_t rtt_delta);
11471148

1149+
/* The socket creation API is used to set the socket creation function
1150+
* in the picoquic context. That function needs to be set by the
1151+
* packet loop when it starts. It has three parameters:
1152+
*
1153+
* - a calling context, specified by the socket loop.
1154+
* - the socket address family, type and protocol.
1155+
* - the local address, in which the socket type MUST be specified. If
1156+
* the IP address and port numbers are specified, the socket will
1157+
* be bound to these addresses.
1158+
* - an optional remote address. If it is specified, the socket will
1159+
* be connected to that address, using an asynchronous version of
1160+
* the connect call.
1161+
*
1162+
* The function returns a (void *) version of the handle of the socket
1163+
* that was created.
1164+
*/
1165+
1166+
typedef void (*picoquic_create_socket_fn)(void* create_socket_ctx,
1167+
int af, int type, int protocol,
1168+
struct sockaddr* addr_local, struct sockaddr* addr_remote, int interface_index);
1169+
1170+
int picoquic_set_socket_fn(picoquic_quic_t* quic, picoquic_create_socket_fn socket_fn,
1171+
void* create_socket_ctx);
1172+
1173+
11481174
/* Connection management API.
11491175
* TODO: many of these API should be deprecated. They were created when we
11501176
* envisaged that applications would directly manipulate which connection

picoquic/picoquic_internal.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,7 @@ typedef struct st_picoquic_quic_t {
654654
picohash_table* table_cnx_by_net;
655655
picohash_table* table_cnx_by_icid;
656656
picohash_table* table_cnx_by_secret;
657+
picohash_table* table_cnx_by_socket_id; /* used for QMux */
657658

658659
picohash_table* table_issued_tickets;
659660
picoquic_issued_ticket_t* table_issued_tickets_first;
@@ -697,6 +698,10 @@ typedef struct st_picoquic_quic_t {
697698
uint64_t rtt_update_delta;
698699
uint64_t pacing_rate_update_delta;
699700

701+
/* Creation of additional socket, if authorized by packet loop. */
702+
picoquic_create_socket_fn create_socket_fn;
703+
void* create_socket_ctx;
704+
700705
/* Logging APIS */
701706
void* F_log;
702707
char* binlog_dir;
@@ -1395,6 +1400,9 @@ typedef struct st_picoquic_cnx_t {
13951400
picoquic_ack_context_t ack_ctx[picoquic_nb_packet_context];
13961401
/* Sequence number of the next observed address frame */
13971402
uint64_t observed_number;
1403+
/* Handling of QMUX socket */
1404+
void* qmux_socket_id;
1405+
picohash_item registered_socket_id_item;
13981406
/* Handling of the QMux QX_PING frame */
13991407
uint64_t qx_acked_last; /* last qx_ping query of the peer acked */
14001408
uint64_t qx_query_last; /* sequence of last qx_ping query from the peer */

picoquic/qmux.c

Lines changed: 166 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ int picoqmux_prepare_packet(picoquic_cnx_t* cnx, uint64_t current_time, uint8_t*
641641
}
642642

643643
/* Prepare the next packets to send in the current buffer */
644-
int picoqmux_prepare_packets(picoquic_cnx_t* cnx, uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, uint64_t* next_wake_time)
644+
int picoqmux_prepare_cnx_packets(picoquic_cnx_t* cnx, uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, uint64_t* next_wake_time)
645645
{
646646
int ret = 0;
647647
*send_length = 0;
@@ -837,7 +837,7 @@ int picoqmux_decode_frames(picoquic_cnx_t* cnx, picoquic_path_t* path_x, const u
837837
return bytes != NULL ? 0 : PICOQUIC_ERROR_DETECTED;
838838
}
839839

840-
int picoqmux_incoming_packet(picoquic_cnx_t* cnx, uint64_t current_time,
840+
int picoqmux_incoming_cnx_packet(picoquic_cnx_t* cnx, uint64_t current_time,
841841
const uint8_t* receive_buffer, size_t receive_length, uint64_t* next_wake_time)
842842
{
843843
int ret = 0;
@@ -852,3 +852,167 @@ int picoqmux_incoming_packet(picoquic_cnx_t* cnx, uint64_t current_time,
852852
}
853853
return ret;
854854
}
855+
856+
/* Connection context:
857+
* The link between a QMUX connection context and a socket_id is
858+
* tracked in the table "table_cnx_by_socket_id". This is a hash table
859+
* in the QUIC context, with the "socket_id" used as the key.
860+
*
861+
* The socket_id in NULL when the connection is created.
862+
* It is added to the context via the call to picoqmux_set_cnx_socket,
863+
* and removed from the context when the connection is deleted, or
864+
* if the socket_id for the connection is reset to a NULL value.
865+
* (TODO: synchronize QUIC context and socket loop.)
866+
*/
867+
868+
static uint64_t picoquic_socket_id_hash(const void* key, const uint8_t* hash_seed)
869+
{
870+
uint64_t h;
871+
uint8_t bytes[sizeof(void*)];
872+
const picoquic_cnx_t* cnx = (const picoquic_cnx_t*)key;
873+
/* Using siphash, because CNX ID and IP address are chosen by third parties*/
874+
h = picohash_siphash(&cnx->qmux_socket_id, (uint32_t)sizeof(void*), hash_seed);
875+
return h;
876+
}
877+
878+
static int picoquic_socket_id_compare(const void* key1, const void* key2)
879+
{
880+
const picoquic_cnx_t* cnx1 = (const picoquic_cnx_t*)key1;
881+
const picoquic_cnx_t* cnx2 = (const picoquic_cnx_t*)key2;
882+
int ret = cnx1->qmux_socket_id == cnx2->qmux_socket_id;
883+
884+
return ret;
885+
}
886+
887+
static picohash_item* picoquic_socket_id_to_item(const void* key)
888+
{
889+
picoquic_cnx_t* cnx = (picoquic_cnx_t*)key;
890+
891+
return &cnx->registered_socket_id_item;
892+
}
893+
894+
int picoquic_register_socket_id(picoquic_quic_t* quic, picoquic_cnx_t* cnx, void* socket_id)
895+
{
896+
int ret = 0;
897+
picoquic_cnx_t dummy_cnx = { 0 };
898+
picohash_item* item;
899+
900+
dummy_cnx.qmux_socket_id = socket_id;
901+
item = picohash_retrieve(quic->table_cnx_by_socket_id, &dummy_cnx);
902+
if (item != NULL) {
903+
ret = -1;
904+
}
905+
else {
906+
cnx->qmux_socket_id = socket_id;
907+
ret = picohash_insert(quic->table_cnx_by_socket_id, cnx);
908+
}
909+
910+
return ret;
911+
}
912+
913+
void picoquic_unregister_socket_id(picoquic_cnx_t* cnx)
914+
{
915+
if (cnx->qmux_socket_id != NULL) {
916+
picohash_item* item = picohash_retrieve(cnx->quic->table_cnx_by_socket_id, cnx);
917+
if (item != NULL) {
918+
picohash_delete_item(cnx->quic->table_cnx_by_socket_id, item, 0);
919+
}
920+
cnx->qmux_socket_id = NULL;
921+
memset(&cnx->registered_socket_id_item, 0, sizeof(cnx->registered_socket_id_item));
922+
}
923+
}
924+
925+
926+
/* Initiation of a QUIC context to make it QMUX ready.
927+
*/
928+
int picoqmux_set_context(picoquic_quic_t* quic)
929+
{
930+
int ret = 0;
931+
932+
if ((quic->table_cnx_by_icid = picohash_create_ex((size_t)quic->max_number_connections,
933+
picoquic_socket_id_hash, picoquic_socket_id_compare, picoquic_socket_id_to_item, quic->hash_seed)) == NULL) {
934+
ret = -1;
935+
}
936+
937+
return ret;
938+
}
939+
940+
int picoqmux_start_sockets(picoquic_quic_t* quic)
941+
{
942+
/* Go through the list of pending qmux connections and start the
943+
* required sockets */
944+
}
945+
946+
int picoqmux_clear_context(picoquic_quic_t* quic)
947+
{
948+
/* Terminate the pending sockets */
949+
/* Terminate the active sockets */
950+
/* Clear the socket_id table */
951+
}
952+
953+
/*
954+
* QMux outgoing connection creation.
955+
* When creating a QMUx connection, the code needs to create an additional
956+
* TCP socket in the "socket loop" context.
957+
*
958+
* We want to preserve the possibility of using different variations of the
959+
* socket loop code. We do that by adding a call from the socket loop
960+
* to the QUIC context to set the "socket create" function and context.
961+
*
962+
* The socket create function allows for creation of new sockets from the
963+
* QUIC context. The main parameter is a socket address to which the
964+
* connection should be connected. (Questions: should there be an option
965+
* to bind the address to a specific local address before setting the
966+
* connect call? Should there be an option to create an extra UDP socket,
967+
* or just TCP?)
968+
*
969+
* This code assumes that the socket loop is created already. We need a special
970+
* logic to manage "pending" connections created before the socket loop
971+
* starts. Probably a queue of pending connection, which will be moved
972+
* to active connections as soon as sockets can be created.
973+
*/
974+
975+
int picoqmux_create_cnx(picoquic_cnx_t* cnx)
976+
{
977+
int ret = 0;
978+
if (cnx->quic->create_socket_fn == NULL) {
979+
/* queue to the qmux pending list */
980+
}
981+
else {
982+
/* create the required socket */
983+
/* set the other qmux parameters */
984+
/* register the socket */
985+
}
986+
return 0;
987+
}
988+
989+
990+
/*
991+
* QMux input call, from the socket loop.
992+
*/
993+
int picoqmux_incoming_cnx_packet(picoquic_quic_t* quic, uint64_t socket_id,
994+
uint64_t current_time, const uint8_t* receive_buffer, size_t receive_length)
995+
{
996+
/* Find the connection based on the socket ID.
997+
* Possibly, create a connection if this is a new socket?
998+
*/
999+
1000+
/* submit the data to the connection context. */
1001+
1002+
/* return an error if this socket should be closed. */
1003+
}
1004+
1005+
/*
1006+
* QMux prepare call, from the socket loop.
1007+
*/
1008+
int picoqmux_prepare_packet(picoquic_quic_t* quic, uint64_t socket_id, uint64_t current_time,
1009+
uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length)
1010+
{
1011+
/* Find the connection based on the socket ID.
1012+
* Possibly, create a connection if this is a new socket?
1013+
*/
1014+
1015+
/* obtain the data from connection context. */
1016+
1017+
/* return an error if this socket should be closed. */
1018+
}

picoquic/quicctx.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2766,6 +2766,16 @@ void picoquic_default_quality_update(picoquic_quic_t* quic, uint64_t pacing_rate
27662766
quic->rtt_update_delta = rtt_delta;
27672767
}
27682768

2769+
2770+
/* Setting the socket creation function */
2771+
2772+
int picoquic_set_socket_fn(picoquic_quic_t* quic, picoquic_create_socket_fn socket_fn, void* create_socket_ctx)
2773+
{
2774+
return 0;
2775+
}
2776+
2777+
/* management of path */
2778+
27692779
int picoquic_refresh_path_connection_id(picoquic_cnx_t* cnx, uint64_t unique_path_id)
27702780
{
27712781
int ret = -1;

picoquictest/qmux_test.c

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@
4343
#include "picoquic_qlog.h"
4444

4545
int picoqmux_init(picoquic_cnx_t* cnx);
46-
int picoqmux_prepare_packet(picoquic_cnx_t* cnx, uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, uint64_t* next_wake_time);
47-
int picoqmux_incoming_packet(picoquic_cnx_t* cnx, uint64_t current_time,
46+
int picoqmux_prepare_cnx_packets(picoquic_cnx_t* cnx, uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, uint64_t* next_wake_time);
47+
int picoqmux_incoming_cnx_packet(picoquic_cnx_t* cnx, uint64_t current_time,
4848
const uint8_t* receive_buffer, size_t receive_length, uint64_t* next_wake_time);
4949
int picoqmux_has_sent_tp(picoquic_cnx_t* cnx);
5050
int picoqmux_has_received_tp(picoquic_cnx_t* cnx);
@@ -85,7 +85,7 @@ int qmux_send_test(void)
8585
}
8686
if (ret == 0) {
8787
/* prepare the packet */
88-
ret = picoqmux_prepare_packet(cnx, 0, buffer, sizeof(buffer), &send_length, &next_wake_time);
88+
ret = picoqmux_prepare_cnx_packets(cnx, 0, buffer, sizeof(buffer), &send_length, &next_wake_time);
8989

9090
if (send_length != sizeof(qmux_test_packet) ||
9191
memcmp(buffer, qmux_test_packet, send_length) != 0) {
@@ -199,7 +199,7 @@ int qmux_send_tp_test(void)
199199

200200
if (ret == 0) {
201201
/* prepare the packet */
202-
ret = picoqmux_prepare_packet(cnx, 0, buffer, sizeof(buffer), &send_length, &next_wake_time);
202+
ret = picoqmux_prepare_cnx_packets(cnx, 0, buffer, sizeof(buffer), &send_length, &next_wake_time);
203203

204204
if (send_length != sizeof(qmux_test_tp_packet) ||
205205
memcmp(buffer, qmux_test_tp_packet, send_length) != 0) {
@@ -241,7 +241,7 @@ int qmux_receive_test_one(int client_mode, int is_tp_received, int is_tp_sent,
241241
picoquic_set_callback(cnx, qmux_test_callback, &qtc);
242242
if (ret == 0) {
243243
/* prepare a packet */
244-
int r_ret = picoqmux_incoming_packet(cnx, 12345,
244+
int r_ret = picoqmux_incoming_cnx_packet(cnx, 12345,
245245
msg, length, &next_wake_time);
246246

247247
if (r_ret == 0) {
@@ -436,7 +436,7 @@ int qmux_send_qx_ping_r_test(void)
436436
picoqmux_update_state_on_tp_sent(cnx);
437437
picoqmux_update_state_on_tp_received(cnx);
438438
/* prepare the packet */
439-
ret = picoqmux_prepare_packet(cnx, 0, buffer, sizeof(buffer), &send_length, &next_wake_time);
439+
ret = picoqmux_prepare_cnx_packets(cnx, 0, buffer, sizeof(buffer), &send_length, &next_wake_time);
440440

441441
if (send_length != sizeof(qmux_qx_ping_r_packet) ||
442442
memcmp(buffer, qmux_qx_ping_r_packet, send_length) != 0) {
@@ -466,7 +466,7 @@ int qmux_send_cnx_close_test(void)
466466
/* Simulate that the local application requests disconnect */
467467
picoquic_close(cnx, 0);
468468
/* prepare the packet */
469-
ret = picoqmux_prepare_packet(cnx, 0, buffer, sizeof(buffer), &send_length, &next_wake_time);
469+
ret = picoqmux_prepare_cnx_packets(cnx, 0, buffer, sizeof(buffer), &send_length, &next_wake_time);
470470

471471
if (send_length != sizeof(qmux_app_close_packet) ||
472472
memcmp(buffer, qmux_app_close_packet, send_length) != 0 ||
@@ -479,3 +479,22 @@ int qmux_send_cnx_close_test(void)
479479

480480
return ret;
481481
}
482+
483+
/*
484+
* Network simulation tests.
485+
* The simulations have to use an interface between the picoquic stack and the
486+
* network. This is logically split between:
487+
* - socket loop functions that wait for new TCP and new TCP data
488+
* or permission to send.
489+
* - Qmux function that accumulate a segment of data per connection.
490+
* We assume that the Qmux function are implemented as a Qmux extension to the
491+
* picoquic_quic_t context. We need functions to:
492+
* - create a Qmux context: extension of picoquic_quic_t with new API.
493+
* - create a Qmux connection: chained to the Qmux context, create a
494+
* tcp socket through an asynchronous call to the socket loop.
495+
* - receive data from a socket.
496+
* - request permission to write on a socket.
497+
* - get polled for writing on the socket if data is granted.
498+
* - ask the socket loop to close a socket.
499+
* - get notified that a socket is closed.
500+
*/

0 commit comments

Comments
 (0)