Release 2.5.0

- [API] lsquic_engine_connect() can now be passed QUIC version to use.
- [OPTIMIZATION] Queue opportunistic ACKs if there is data to be sent.
- [BUGFIX] Don't evict streams from priority iterator if there is
  only one queue.
- [OPTIMIZATION, BUGFIX] Several other optimizations and bug fixes.
- Use ls-qpack v0.10.7.
This commit is contained in:
Dmitri Tikhonov 2019-10-31 12:21:14 -04:00
parent 34e9ac5f5d
commit a0e1aeeee0
34 changed files with 327 additions and 108 deletions

View file

@ -1,3 +1,12 @@
2019-10-31
- 2.5.0
- [API] lsquic_engine_connect() can now be passed QUIC version to use.
- [OPTIMIZATION] Queue opportunistic ACKs if there is data to be sent.
- [BUGFIX] Don't evict streams from priority iterator if there is
only one queue.
- [OPTIMIZATION, BUGFIX] Several other optimizations and bug fixes.
- Use ls-qpack v0.10.7.
2019-10-24
- 2.4.10
- [BUGFIX] IETF QUIC server: fix uninitialized variable use.

View file

@ -22,7 +22,9 @@ MESSAGE(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
OPTION(LSQUIC_FIU "Use Fault Injection in Userspace (FIU)" OFF)
SET(MY_CMAKE_FLAGS "-DLSQUIC_DEBUG_NEXT_ADV_TICK=1")
IF (NOT "$ENV{EXTRA_CFLAGS}" MATCHES "-DLSQUIC_DEBUG_NEXT_ADV_TICK")
SET(MY_CMAKE_FLAGS "-DLSQUIC_DEBUG_NEXT_ADV_TICK=1")
ENDIF()
IF (NOT MSVC)

View file

@ -24,8 +24,8 @@ extern "C" {
#endif
#define LSQUIC_MAJOR_VERSION 2
#define LSQUIC_MINOR_VERSION 4
#define LSQUIC_PATCH_VERSION 10
#define LSQUIC_MINOR_VERSION 5
#define LSQUIC_PATCH_VERSION 0
/**
* Engine flags:
@ -1026,11 +1026,16 @@ lsquic_engine_new (unsigned lsquic_engine_flags,
/**
* Create a client connection to peer identified by `peer_ctx'.
*
* To let the engine specify QUIC version, use N_LSQVER. If zero-rtt info
* is supplied, version is picked from there instead.
*
* If `max_packet_size' is set to zero, it is inferred based on `peer_sa':
* 1350 for IPv6 and 1370 for IPv4.
*/
lsquic_conn_t *
lsquic_engine_connect (lsquic_engine_t *, const struct sockaddr *local_sa,
lsquic_engine_connect (lsquic_engine_t *, enum lsquic_version,
const struct sockaddr *local_sa,
const struct sockaddr *peer_sa,
void *peer_ctx, lsquic_conn_ctx_t *conn_ctx,
const char *hostname, unsigned short max_packet_size,

@ -1 +1 @@
Subproject commit 1786126dc6e104fdfa7b7c45611b2d7c870a0e55
Subproject commit 9ace654b3aac2cf16f76fbcf52d3170278f60141

View file

@ -52,7 +52,6 @@ enum alarm_id_bit {
ALBIT_IDLE = 1 << AL_IDLE,
ALBIT_RET_CIDS = 1 << AL_RET_CIDS,
ALBIT_CID_THROT = 1 << AL_CID_THROT,
ALBIT_PATH_CHAL = 1 << AL_PATH_CHAL,
ALBIT_PATH_CHAL_0 = 1 << AL_PATH_CHAL_0,
ALBIT_PATH_CHAL_1 = 1 << AL_PATH_CHAL_1,
ALBIT_SESS_TICKET = 1 << AL_SESS_TICKET,

View file

@ -30,5 +30,5 @@ lsquic_zero_rtt_version (const unsigned char *buf, size_t bufsz)
return lsquic_tag2ver(tag);
}
else
return -1;
return N_LSQVER;
}

View file

@ -1462,7 +1462,8 @@ add_conn_to_hash (struct lsquic_engine *engine, struct lsquic_conn *conn,
lsquic_conn_t *
lsquic_engine_connect (lsquic_engine_t *engine, const struct sockaddr *local_sa,
lsquic_engine_connect (lsquic_engine_t *engine, enum lsquic_version version,
const struct sockaddr *local_sa,
const struct sockaddr *peer_sa,
void *peer_ctx, lsquic_conn_ctx_t *conn_ctx,
const char *hostname, unsigned short max_packet_size,
@ -1470,7 +1471,7 @@ lsquic_engine_connect (lsquic_engine_t *engine, const struct sockaddr *local_sa,
const unsigned char *token, size_t token_sz)
{
lsquic_conn_t *conn;
unsigned flags;
unsigned flags, versions;
int is_ipv4;
ENGINE_IN(engine);
@ -1492,13 +1493,31 @@ lsquic_engine_connect (lsquic_engine_t *engine, const struct sockaddr *local_sa,
return NULL;
flags = engine->flags & (ENG_SERVER|ENG_HTTP);
is_ipv4 = peer_sa->sa_family == AF_INET;
if (engine->pub.enp_settings.es_versions & LSQUIC_IETF_VERSIONS)
conn = lsquic_ietf_full_conn_client_new(&engine->pub,
if (zero_rtt && zero_rtt_len)
{
version = lsquic_zero_rtt_version(zero_rtt, zero_rtt_len);
if (version >= N_LSQVER)
{
LSQ_INFO("zero-rtt version is bad, won't use");
zero_rtt = NULL;
zero_rtt_len = 0;
}
}
if (version >= N_LSQVER)
{
if (version > N_LSQVER)
LSQ_WARN("invalid version specified, engine will pick");
versions = engine->pub.enp_settings.es_versions;
}
else
versions = 1u << version;
if (versions & LSQUIC_IETF_VERSIONS)
conn = lsquic_ietf_full_conn_client_new(&engine->pub, versions,
flags, hostname, max_packet_size,
is_ipv4, zero_rtt, zero_rtt_len, token, token_sz);
else
conn = lsquic_gquic_full_conn_client_new(&engine->pub, flags,
hostname, max_packet_size, is_ipv4,
versions, hostname, max_packet_size, is_ipv4,
zero_rtt, zero_rtt_len);
if (!conn)
goto err;
@ -1558,13 +1577,12 @@ refflags2str (enum lsquic_conn_flags flags, char s[6])
static void
engine_incref_conn (lsquic_conn_t *conn, enum lsquic_conn_flags flag)
{
const lsquic_cid_t *cid;
char str[2][7];
assert(flag & CONN_REF_FLAGS);
assert(!(conn->cn_flags & flag));
conn->cn_flags |= flag;
cid = lsquic_conn_log_cid(conn);
LSQ_DEBUGC("incref conn %"CID_FMT", '%s' -> '%s'", CID_BITS(cid),
LSQ_DEBUGC("incref conn %"CID_FMT", '%s' -> '%s'",
CID_BITS(lsquic_conn_log_cid(conn)),
(refflags2str(conn->cn_flags & ~flag, str[0]), str[0]),
(refflags2str(conn->cn_flags, str[1]), str[1]));
}
@ -1574,7 +1592,6 @@ static lsquic_conn_t *
engine_decref_conn (lsquic_engine_t *engine, lsquic_conn_t *conn,
enum lsquic_conn_flags flags)
{
const lsquic_cid_t *cid;
char str[2][7];
lsquic_time_t now;
assert(flags & CONN_REF_FLAGS);
@ -1584,8 +1601,8 @@ engine_decref_conn (lsquic_engine_t *engine, lsquic_conn_t *conn,
assert(0 == (conn->cn_flags & LSCONN_HASHED));
#endif
conn->cn_flags &= ~flags;
cid = lsquic_conn_log_cid(conn);
LSQ_DEBUGC("decref conn %"CID_FMT", '%s' -> '%s'", CID_BITS(cid),
LSQ_DEBUGC("decref conn %"CID_FMT", '%s' -> '%s'",
CID_BITS(lsquic_conn_log_cid(conn)),
(refflags2str(conn->cn_flags | flags, str[0]), str[0]),
(refflags2str(conn->cn_flags, str[1]), str[1]));
if (0 == (conn->cn_flags & CONN_REF_FLAGS))
@ -2128,7 +2145,6 @@ send_packets_out (struct lsquic_engine *engine,
struct conns_tailq *ticked_conns,
struct conns_stailq *closed_conns)
{
const lsquic_cid_t *cid;
unsigned n, w, n_sent, n_batches_sent;
lsquic_packet_out_t *packet_out;
struct lsquic_packet_out **packet;
@ -2148,14 +2164,13 @@ send_packets_out (struct lsquic_engine *engine,
while ((conn = coi_next(&conns_iter)))
{
cid = lsquic_conn_log_cid(conn);
packet_out = conn->cn_if->ci_next_packet_to_send(conn, 0);
if (!packet_out) {
/* Evanescent connection always has a packet to send: */
assert(!(conn->cn_flags & LSCONN_EVANESCENT));
LSQ_DEBUGC("batched all outgoing packets for %s conn %"CID_FMT,
(conn->cn_flags & LSCONN_MINI ? "mini" :
"full"), CID_BITS(cid));
(conn->cn_flags & LSCONN_MINI ? "mini" : "full"),
CID_BITS(lsquic_conn_log_cid(conn)));
coi_deactivate(&conns_iter, conn);
continue;
}
@ -2174,7 +2189,7 @@ send_packets_out (struct lsquic_engine *engine,
/* This is pretty bad: close connection immediately */
conn->cn_if->ci_packet_not_sent(conn, packet_out);
LSQ_INFOC("conn %"CID_FMT" has unsendable packets",
CID_BITS(cid));
CID_BITS(lsquic_conn_log_cid(conn)));
if (!(conn->cn_flags & LSCONN_EVANESCENT))
{
if (!(conn->cn_flags & LSCONN_CLOSING))
@ -2207,7 +2222,7 @@ send_packets_out (struct lsquic_engine *engine,
}
}
LSQ_DEBUGC("batched packet %"PRIu64" for connection %"CID_FMT,
packet_out->po_packno, CID_BITS(cid));
packet_out->po_packno, CID_BITS(lsquic_conn_log_cid(conn)));
if (packet_out->po_flags & PO_ENCRYPTED)
{
iov->iov_base = packet_out->po_enc_data;
@ -2597,7 +2612,6 @@ lsquic_engine_earliest_adv_tick (lsquic_engine_t *engine, int *diff)
lsquic_time_t now, next_time;
#if LSQUIC_DEBUG_NEXT_ADV_TICK
const struct lsquic_conn *conn;
const lsquic_cid_t *cid;
const enum lsq_log_level L = LSQ_LOG_DEBUG; /* Easy toggle */
#endif
@ -2608,11 +2622,11 @@ lsquic_engine_earliest_adv_tick (lsquic_engine_t *engine, int *diff)
{
#if LSQUIC_DEBUG_NEXT_ADV_TICK
conn = lsquic_mh_peek(&engine->conns_out);
cid = lsquic_conn_log_cid(conn);
LSQ_LOGC(L, "next advisory tick is now: went past deadline last time "
"and have %u outgoing connection%.*s (%"CID_FMT" first)",
lsquic_mh_count(&engine->conns_out),
lsquic_mh_count(&engine->conns_out) != 1, "s", CID_BITS(cid));
lsquic_mh_count(&engine->conns_out) != 1, "s",
CID_BITS(lsquic_conn_log_cid(conn)));
#endif
*diff = 0;
return 1;
@ -2631,11 +2645,11 @@ lsquic_engine_earliest_adv_tick (lsquic_engine_t *engine, int *diff)
{
#if LSQUIC_DEBUG_NEXT_ADV_TICK
conn = lsquic_mh_peek(&engine->conns_tickable);
cid = lsquic_conn_log_cid(conn);
LSQ_LOGC(L, "next advisory tick is now: have %u tickable "
"connection%.*s (%"CID_FMT" first)",
lsquic_mh_count(&engine->conns_tickable),
lsquic_mh_count(&engine->conns_tickable) != 1, "s", CID_BITS(cid));
lsquic_mh_count(&engine->conns_tickable) != 1, "s",
CID_BITS(lsquic_conn_log_cid(conn)));
#endif
*diff = 0;
return 1;
@ -2668,12 +2682,9 @@ lsquic_engine_earliest_adv_tick (lsquic_engine_t *engine, int *diff)
*diff = (int) ((int64_t) next_time - (int64_t) now);
#if LSQUIC_DEBUG_NEXT_ADV_TICK
if (next_attq)
{
cid = lsquic_conn_log_cid(next_attq->ae_conn);
LSQ_LOGC(L, "next advisory tick is %d usec away: conn %"CID_FMT
": %s", *diff, CID_BITS(cid),
": %s", *diff, CID_BITS(lsquic_conn_log_cid(next_attq->ae_conn)),
lsquic_attq_why2str(next_attq->ae_why));
}
else
LSQ_LOG(L, "next advisory tick is %d usec away: resume sending", *diff);
#endif

View file

@ -619,8 +619,6 @@ decode_and_pass_payload (struct lsquic_frame_reader *fr)
LSQ_INFO("%s: stream error %u", __func__, err);
if (hset)
fr->fr_hsi_if->hsi_discard_header_set(hset);
if (uh)
free(uh);
if (buf)
lsquic_mm_put_16k(fr->fr_mm, buf);
fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, fr_get_stream_id(fr), err);

View file

@ -219,6 +219,7 @@ struct full_conn
struct short_ack_info fc_saved_ack_info;
lsquic_time_t fc_saved_ack_received;
struct network_path fc_path;
unsigned fc_orig_versions; /* Client only */
};
static const struct ver_neg server_ver_neg;
@ -672,7 +673,7 @@ new_conn_common (lsquic_cid_t cid, struct lsquic_engine_public *enpub,
struct lsquic_conn *
lsquic_gquic_full_conn_client_new (struct lsquic_engine_public *enpub,
unsigned flags,
unsigned versions, unsigned flags,
const char *hostname, unsigned short max_packet_size,
int is_ipv4,
const unsigned char *zero_rtt, size_t zero_rtt_len)
@ -682,12 +683,13 @@ lsquic_gquic_full_conn_client_new (struct lsquic_engine_public *enpub,
lsquic_cid_t cid;
const struct enc_session_funcs_gquic *esf_g;
version = highest_bit_set(enpub->enp_settings.es_versions);
versions &= (~LSQUIC_IETF_VERSIONS & LSQUIC_SUPPORTED_VERSIONS);
assert(versions);
version = highest_bit_set(versions);
if (zero_rtt)
{
zero_rtt_version = lsquic_zero_rtt_version(zero_rtt, zero_rtt_len);
if (zero_rtt_version < N_LSQVER &&
((1 << zero_rtt_version) & enpub->enp_settings.es_versions))
if (zero_rtt_version < N_LSQVER && ((1 << zero_rtt_version) & versions))
version = zero_rtt_version;
}
esf_g = select_esf_gquic_by_ver(version);
@ -725,7 +727,8 @@ lsquic_gquic_full_conn_client_new (struct lsquic_engine_public *enpub,
conn->fc_stream_ifs[STREAM_IF_HSK]
.stream_if = &lsquic_client_hsk_stream_if;
conn->fc_stream_ifs[STREAM_IF_HSK].stream_if_ctx = &conn->fc_hsk_ctx.client;
init_ver_neg(conn, conn->fc_settings->es_versions, &version);
conn->fc_orig_versions = versions;
init_ver_neg(conn, versions, &version);
if (conn->fc_settings->es_handshake_to)
lsquic_alarmset_set(&conn->fc_alset, AL_HANDSHAKE,
lsquic_time_now() + conn->fc_settings->es_handshake_to);
@ -2510,6 +2513,7 @@ idle_alarm_expired (enum alarm_id al_id, void *ctx, lsquic_time_t expiry,
{
struct full_conn *conn = ctx;
LSQ_DEBUG("connection timed out");
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "connection timed out");
conn->fc_flags |= FC_TIMED_OUT;
}
@ -4379,8 +4383,8 @@ lsquic_gquic_full_conn_srej (struct lsquic_conn *lconn)
lconn->cn_esf.g->esf_reset_cid(lconn->cn_enc_session, &cce->cce_cid);
/* Reset version negotiation */
version = highest_bit_set(conn->fc_settings->es_versions);
init_ver_neg(conn, conn->fc_settings->es_versions, &version);
version = highest_bit_set(conn->fc_orig_versions);
init_ver_neg(conn, conn->fc_orig_versions, &version);
/* Reset receive history */
lsquic_rechist_cleanup(&conn->fc_rechist);

View file

@ -7,6 +7,7 @@ struct lsquic_engine_public;
struct lsquic_conn *
lsquic_gquic_full_conn_client_new (struct lsquic_engine_public *,
unsigned versions,
unsigned flags /* Only FC_SERVER and FC_HTTP */,
const char *hostname, unsigned short max_packet_size,
int is_ipv4,
@ -14,6 +15,7 @@ lsquic_gquic_full_conn_client_new (struct lsquic_engine_public *,
struct lsquic_conn *
lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *,
unsigned versions,
unsigned flags /* Only FC_SERVER and FC_HTTP */,
const char *hostname, unsigned short max_packet_size, int is_ipv4,
const unsigned char *zero_rtt, size_t,

View file

@ -68,7 +68,7 @@
#include "lsquic_headers.h"
#define LSQUIC_LOGGER_MODULE LSQLM_CONN
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(&conn->ifc_conn)
#define LSQUIC_LOG_CONN_ID ietf_full_conn_ci_get_log_cid(&conn->ifc_conn)
#include "lsquic_logger.h"
#define MAX_RETR_PACKETS_SINCE_LAST_ACK 2
@ -421,6 +421,9 @@ ignore_hsk (struct ietf_full_conn *);
static unsigned
ietf_full_conn_ci_n_avail_streams (const struct lsquic_conn *);
static const lsquic_cid_t *
ietf_full_conn_ci_get_log_cid (const struct lsquic_conn *);
static unsigned
highest_bit_set (unsigned sz)
@ -480,6 +483,7 @@ idle_alarm_expired (enum alarm_id al_id, void *ctx, lsquic_time_t expiry,
{
struct ietf_full_conn *const conn = (struct ietf_full_conn *) ctx;
LSQ_DEBUG("connection timed out");
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "connection timed out");
conn->ifc_flags |= IFC_TIMED_OUT;
}
@ -937,7 +941,7 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
struct lsquic_conn *
lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
unsigned flags,
unsigned versions, unsigned flags,
const char *hostname, unsigned short max_packet_size, int is_ipv4,
const unsigned char *zero_rtt, size_t zero_rtt_sz,
const unsigned char *token, size_t token_sz)
@ -946,7 +950,6 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
struct ietf_full_conn *conn;
enum lsquic_version ver, zero_rtt_version;
lsquic_time_t now;
unsigned versions;
conn = calloc(1, sizeof(*conn));
if (!conn)
@ -963,8 +966,8 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
return NULL;
}
versions = enpub->enp_settings.es_versions & LSQUIC_IETF_VERSIONS;
assert(versions);
versions &= LSQUIC_IETF_VERSIONS;
ver = highest_bit_set(versions);
if (zero_rtt)
{
@ -1070,8 +1073,8 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
lsquic_malo_create(sizeof(struct lsquic_packet_out));
if (!conn->ifc_pub.packet_out_malo)
{
free(conn);
lsquic_stream_destroy(conn->ifc_u.cli.crypto_streams[ENC_LEV_CLEAR]);
free(conn);
return NULL;
}
conn->ifc_flags |= IFC_PROC_CRYPTO;
@ -2420,6 +2423,7 @@ ietf_full_conn_ci_destroy (struct lsquic_conn *lconn)
lsquic_hash_destroy(conn->ifc_pub.u.ietf.promises);
}
lsquic_hash_destroy(conn->ifc_pub.all_streams);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "full connection destroyed");
free(conn->ifc_errmsg);
free(conn);
}
@ -3133,6 +3137,7 @@ ietf_full_conn_ci_push_stream (struct lsquic_conn *lconn, void *hset,
if (!uh)
{
LSQ_WARN("stream push: cannot allocate uh");
free(promise);
lsquic_mm_put_4k(conn->ifc_pub.mm, header_block_buf);
if (own_hset)
conn->ifc_enpub->enp_hsi_if->hsi_discard_header_set(hset);
@ -5391,6 +5396,27 @@ try_queueing_ack (struct ietf_full_conn *conn, enum packnum_space pns,
}
static int
maybe_queue_opp_ack (struct ietf_full_conn *conn)
{
if (/* If there is at least one ackable packet */
conn->ifc_n_slack_akbl[PNS_APP] > 0
/* ...and there are things to write */
&& (!TAILQ_EMPTY(&conn->ifc_pub.write_streams) || conn->ifc_send_flags)
/* ...and writing is possible */
&& write_is_possible(conn))
{
lsquic_alarmset_unset(&conn->ifc_alset, AL_ACK_APP);
lsquic_send_ctl_sanity_check(&conn->ifc_send_ctl);
conn->ifc_flags |= IFC_ACK_QUED_APP;
LSQ_DEBUG("%s ACK queued opportunistically", lsquic_pns2str[PNS_APP]);
return 1;
}
else
return 0;
}
static int
process_retry_packet (struct ietf_full_conn *conn,
struct lsquic_packet_in *packet_in)
@ -5750,8 +5776,7 @@ verneg_ok (const struct ietf_full_conn *conn)
{
enum lsquic_version ver;
ver = highest_bit_set(conn->ifc_enpub->enp_settings.es_versions
& LSQUIC_IETF_VERSIONS);
ver = highest_bit_set(conn->ifc_u.cli.ifcli_ver_neg.vn_supp);
return (1 << ver) & LSQUIC_IETF_DRAFT_VERSIONS;
}
@ -6005,7 +6030,8 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
have_delayed_packets =
lsquic_send_ctl_maybe_squeeze_sched(&conn->ifc_send_ctl);
if (should_generate_ack(conn, IFC_ACK_QUEUED))
if (should_generate_ack(conn, IFC_ACK_QUEUED) ||
(!have_delayed_packets && maybe_queue_opp_ack(conn)))
{
if (have_delayed_packets)
lsquic_send_ctl_reset_packnos(&conn->ifc_send_ctl);
@ -6483,8 +6509,7 @@ ietf_full_conn_ci_record_addrs (struct lsquic_conn *lconn, void *peer_ctx,
if (first_unvalidated || first_other)
{
victim = first_unvalidated ? first_unvalidated : first_other;
record_to_path(&first_unvalidated->cop_path, peer_ctx, local_sa,
peer_sa);
record_to_path(&victim->cop_path, peer_ctx, local_sa, peer_sa);
return victim - conn->ifc_paths;
}

View file

@ -2892,6 +2892,7 @@ gquic_decrypt_packet (enc_session_t *enc_session_p,
return DECPI_NOMEM;
}
assert(packet_in->pi_data);
header_len = packet_in->pi_header_sz;
data_len = packet_in->pi_data_sz - packet_in->pi_header_sz;
enc_level = lsquic_enc_session_decrypt(enc_session_p,

View file

@ -261,10 +261,10 @@ lsquic_logger_log3 (enum lsq_log_level log_level,
va_list ap;
va_start(ap, fmt);
lb = vsnprintf(buf + len, max - len, fmt, ap);
va_end(ap);
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
va_end(ap);
lb = snprintf(buf + len, max - len, "\n");
if (FORMAT_PROBLEM(lb, len, max))
goto end;
@ -303,10 +303,10 @@ lsquic_logger_log2 (enum lsq_log_level log_level,
va_list ap;
va_start(ap, fmt);
lb = vsnprintf(buf + len, max - len, fmt, ap);
va_end(ap);
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
va_end(ap);
lb = snprintf(buf + len, max - len, "\n");
if (FORMAT_PROBLEM(lb, len, max))
goto end;
@ -343,10 +343,10 @@ lsquic_logger_log1 (enum lsq_log_level log_level,
va_list ap;
va_start(ap, fmt);
lb = vsnprintf(buf + len, max - len, fmt, ap);
va_end(ap);
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
va_end(ap);
lb = snprintf(buf + len, max - len, "\n");
if (FORMAT_PROBLEM(lb, len, max))
goto end;

View file

@ -58,10 +58,12 @@ lsquic_gquic_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz,
const lsquic_cid_t *cid, unsigned versions);
int
lsquic_Q046_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz,
const lsquic_cid_t *scid, const lsquic_cid_t *dcid, unsigned versions);
const lsquic_cid_t *scid, const lsquic_cid_t *dcid, unsigned versions,
uint8_t);
int
lsquic_ietf_v1_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz,
const lsquic_cid_t *scid, const lsquic_cid_t *dcid, unsigned versions);
const lsquic_cid_t *scid, const lsquic_cid_t *dcid, unsigned versions,
uint8_t);
int
lsquic_iquic_gen_retry_pkt (unsigned char *buf, size_t bufsz,
const struct lsquic_engine_public *, const lsquic_cid_t *scid,

View file

@ -7,7 +7,6 @@
#include <inttypes.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#ifndef WIN32
@ -1835,7 +1834,8 @@ popcount (unsigned v)
int
lsquic_ietf_v1_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz,
const lsquic_cid_t *scid, const lsquic_cid_t *dcid, unsigned versions)
const lsquic_cid_t *scid, const lsquic_cid_t *dcid, unsigned versions,
uint8_t rand)
{
size_t need;
int r;
@ -1846,7 +1846,7 @@ lsquic_ietf_v1_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz,
if (need > bufsz)
return -1;
*buf++ = 0x80 | 0x40 | rand();
*buf++ = 0x80 | 0x40 | (rand & 0xF);
memset(buf, 0, 4);
buf += 4;

View file

@ -6,7 +6,6 @@
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include <sys/types.h>
@ -549,7 +548,8 @@ popcount (unsigned v)
int
lsquic_Q046_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz,
const lsquic_cid_t *scid, const lsquic_cid_t *dcid, unsigned versions)
const lsquic_cid_t *scid, const lsquic_cid_t *dcid, unsigned versions,
uint8_t rand)
{
unsigned slen, dlen;
size_t need;
@ -561,7 +561,7 @@ lsquic_Q046_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz,
if (need > bufsz)
return -1;
*buf++ = 0x80 | 0x40 | rand();
*buf++ = 0x80 | 0x40 | (rand & 0xF);
memset(buf, 0, 4);
buf += 4;

View file

@ -103,9 +103,19 @@ struct pr_queue
unsigned char prq_pubres_g_buf[GQUIC_RESET_SZ];
unsigned char prq_verneg_g_buf[1 + GQUIC_CID_LEN
+ N_LSQVER * 4];
/* We generate random nybbles in batches */
#define NYBBLE_COUNT_BITS 4
#define NYBBLE_COUNT (1 << NYBBLE_COUNT_BITS)
#define NYBBLE_MASK (NYBBLE_COUNT - 1)
unsigned prq_rand_nybble_off;
uint8_t prq_rand_nybble_buf[NYBBLE_COUNT * 2];
};
static uint8_t
get_rand_byte (struct pr_queue *);
static int
comp_reqs (const void *s1, const void *s2, size_t n)
{
@ -196,6 +206,7 @@ prq_create (unsigned max_elems, unsigned max_conns,
prq->prq_verneg_g_sz = verneg_g_sz;
prq->prq_pubres_g_sz = (unsigned) prst_g_sz;
prq->prq_enpub = enpub;
prq->prq_rand_nybble_off = 0;
LSQ_INFO("initialized queue of size %d", max_elems);
@ -264,7 +275,7 @@ prq_new_req (struct pr_queue *prq, enum packet_req_type type,
lsquic_ver_tag_t ver_tag;
enum lsquic_version version;
enum pr_flags flags;
unsigned max, size;
unsigned max, size, rand;
if (packet_in->pi_flags & PI_GQUIC)
flags = PR_GQUIC;
@ -292,7 +303,10 @@ prq_new_req (struct pr_queue *prq, enum packet_req_type type,
/* Use a random stateless reset size */
max = MIN(IQUIC_MAX_SRST_SIZE, packet_in->pi_data_sz - 1u);
if (max > IQUIC_MIN_SRST_SIZE)
size = IQUIC_MIN_SRST_SIZE + rand() % (max - IQUIC_MIN_SRST_SIZE);
{
rand = get_rand_byte(prq);
size = IQUIC_MIN_SRST_SIZE + rand % (max - IQUIC_MIN_SRST_SIZE);
}
else
size = IQUIC_MIN_SRST_SIZE;
LSQ_DEBUGC("selected %u-byte reset size for CID %"CID_FMT
@ -398,6 +412,31 @@ get_evconn (struct pr_queue *prq)
}
static uint8_t
get_rand_nybble (struct pr_queue *prq)
{
uint8_t byte;
if (prq->prq_rand_nybble_off == 0)
RAND_bytes(prq->prq_rand_nybble_buf, sizeof(prq->prq_rand_nybble_buf));
byte = prq->prq_rand_nybble_buf[prq->prq_rand_nybble_off / 2];
if (prq->prq_rand_nybble_off & 1)
byte >>= 4;
else
byte &= 0xF;
prq->prq_rand_nybble_off = (prq->prq_rand_nybble_off + 1) & NYBBLE_MASK;
return byte;
}
static uint8_t
get_rand_byte (struct pr_queue *prq)
{
return (get_rand_nybble(prq) << 4) | get_rand_nybble(prq);
}
struct lsquic_conn *
prq_next_conn (struct pr_queue *prq)
{
@ -407,7 +446,7 @@ prq_next_conn (struct pr_queue *prq)
struct packet_req *req;
struct lsquic_packet_out *packet_out;
int (*gen_verneg) (unsigned char *, size_t, const lsquic_cid_t *,
const lsquic_cid_t *, unsigned);
const lsquic_cid_t *, unsigned, uint8_t);
int len;
lconn = TAILQ_FIRST(&prq->prq_returned_conns);
@ -451,7 +490,8 @@ prq_next_conn (struct pr_queue *prq)
gen_verneg = lsquic_ietf_v1_gen_ver_nego_pkt;
len = gen_verneg(packet_out->po_data, max_bufsz(prq),
/* Flip SCID/DCID here: */ &req->pr_dcid, &req->pr_scid,
prq->prq_enpub->enp_settings.es_versions);
prq->prq_enpub->enp_settings.es_versions,
get_rand_nybble(prq));
if (len > 0)
packet_out->po_data_sz = len;
else

View file

@ -410,6 +410,7 @@ qdh_supply_hset_to_stream (struct qpack_dec_hdl *qdh,
}
lsqpack_dec_destroy_header_list(qlist);
qlist = NULL;
st = hset_if->hsi_process_header(hset, 0, 0, 0, 0, 0);
if (st != LSQUIC_HDR_OK)
goto err;
@ -431,7 +432,8 @@ qdh_supply_hset_to_stream (struct qpack_dec_hdl *qdh,
return 0;
err:
lsqpack_dec_destroy_header_list(qlist);
if (qlist)
lsqpack_dec_destroy_header_list(qlist);
hset_if->hsi_discard_header_set(hset);
free(uh);
return -1;

View file

@ -114,7 +114,7 @@ lsquic_qeh_settings (struct qpack_enc_hdl *qeh, unsigned max_table_size,
}
enc_opts = LSQPACK_ENC_OPT_STAGE_2
| server ? LSQPACK_ENC_OPT_SERVER : 0;
| (server ? LSQPACK_ENC_OPT_SERVER : 0);
qeh->qeh_tsu_sz = sizeof(qeh->qeh_tsu_buf);
if (0 != lsqpack_enc_init(&qeh->qeh_encoder, (void *) qeh->qeh_conn,
max_table_size, dyn_table_size, max_risked_streams, enc_opts,

View file

@ -99,7 +99,8 @@ lsquic_qlog_packet_rx (const lsquic_cid_t* cid,
const unsigned char *packet_in_data,
size_t packet_in_size)
{
int i, cur = 0, first = 0, ret = 0;
int i, first, ret;
unsigned cur;
size_t raw_bytes_written;
char data[QLOG_PACKET_RAW_SZ];
char frame_list[QLOG_FRAME_LIST_MAX + 1];
@ -107,11 +108,12 @@ lsquic_qlog_packet_rx (const lsquic_cid_t* cid,
if (!packet_in || !packet_in_data)
return;
frame_list[cur] = '\0';
if (packet_in->pi_frame_types)
{
cur = sprintf(frame_list, "%s", QLOG_FRAME_LIST_PREFIX);
for (i = 0; i < N_QUIC_FRAMES; i++)
memcpy(frame_list, QLOG_FRAME_LIST_PREFIX,
sizeof(QLOG_FRAME_LIST_PREFIX));
cur = sizeof(QLOG_FRAME_LIST_PREFIX) - 1;
for (i = 0, first = 0; i < N_QUIC_FRAMES; i++)
if (packet_in->pi_frame_types & (1 << i))
{
ret = snprintf(frame_list + cur,
@ -124,13 +126,16 @@ lsquic_qlog_packet_rx (const lsquic_cid_t* cid,
QLOG_FRAME_DICT_PREFIX),
QUIC_FRAME_NAME(i),
QLOG_FRAME_DICT_SUFFIX);
if ((unsigned)ret > QLOG_FRAME_LIST_MAX - cur)
if (ret < 0 || (unsigned)ret > QLOG_FRAME_LIST_MAX - cur)
break;
cur += ret;
}
if ((unsigned)cur <= QLOG_FRAME_LIST_MAX)
sprintf(frame_list + cur, "%s", QLOG_FRAME_LIST_SUFFIX);
if (cur + sizeof(QLOG_FRAME_LIST_SUFFIX) <= QLOG_FRAME_LIST_MAX)
memcpy(frame_list + cur, QLOG_FRAME_LIST_SUFFIX,
sizeof(QLOG_FRAME_LIST_SUFFIX));
}
else
frame_list[0] = '\0';
raw_bytes_written = lsquic_hex_encode(packet_in_data, packet_in_size,
data, QLOG_PACKET_RAW_SZ);

View file

@ -2402,7 +2402,7 @@ split_buffered_packet (lsquic_send_ctl_t *ctl,
new_packet_out = send_ctl_allocate_packet(ctl, bits, 0,
lsquic_packet_out_pns(packet_out), packet_out->po_path);
if (!packet_out)
if (!new_packet_out)
return -1;
if (0 == lsquic_packet_out_split_in_two(&ctl->sc_enpub->enp_mm, packet_out,

View file

@ -46,6 +46,7 @@ add_stream_to_spi (struct stream_prio_iter *iter, lsquic_stream_t *stream)
}
TAILQ_INSERT_TAIL(&iter->spi_streams[ stream->sm_priority ],
stream, next_prio_stream);
++iter->spi_n_added;
}
@ -70,6 +71,7 @@ lsquic_spi_init (struct stream_prio_iter *iter, struct lsquic_stream *first,
iter->spi_cur_prio = 0;
iter->spi_prev_stream = NULL;
iter->spi_next_stream = NULL;
iter->spi_n_added = 0;
stream = first;
count = 0;
@ -302,12 +304,53 @@ have_non_critical_streams (const struct stream_prio_iter *iter)
}
#if __GNUC__
# define popcount __builtin_popcountll
#else
static int
popcount (unsigned long long v)
{
int count, i;
for (i = 0, count = 0; i < sizeof(v) * 8; ++i)
if (v & (1 << i))
++count;
return count;
}
#endif
static int
spi_has_more_than_one_queue (const struct stream_prio_iter *iter)
{
unsigned i;
int count;
if (iter->spi_n_added < 2)
return 0;
count = 0;
for (i = 0; i < sizeof(iter->spi_set) / sizeof(iter->spi_set[0]); ++i)
{
count += popcount(iter->spi_set[i]);
if (count > 1)
return 1;
}
return 0;
}
static void
spi_drop_high_or_non_high (struct stream_prio_iter *iter, int drop_high)
{
uint64_t new_set[ sizeof(iter->spi_set) / sizeof(iter->spi_set[0]) ];
unsigned bit, set, n;
if (!spi_has_more_than_one_queue(iter))
return;
memset(new_set, 0, sizeof(new_set));
find_and_set_lowest_priority(iter);

View file

@ -23,6 +23,7 @@ struct stream_prio_iter
const char *spi_name; /* Used for logging */
uint64_t spi_set[4]; /* 256 bits */
enum stream_q_flags spi_onlist_mask;
unsigned spi_n_added;
unsigned char spi_cur_prio;
unsigned char spi_prev_prio;
struct lsquic_stream *spi_prev_stream,

View file

@ -677,11 +677,25 @@ lsquic_stream_call_on_close (lsquic_stream_t *stream)
}
static int
stream_has_frame_at_read_offset (struct lsquic_stream *stream)
{
if (!((stream->stream_flags & STREAM_CACHED_FRAME)
&& stream->read_offset == stream->sm_last_frame_off))
{
stream->sm_has_frame = stream->data_in->di_if->di_get_frame(
stream->data_in, stream->read_offset) != NULL;
stream->sm_last_frame_off = stream->read_offset;
stream->stream_flags |= STREAM_CACHED_FRAME;
}
return stream->sm_has_frame;
}
static int
stream_readable_non_http (struct lsquic_stream *stream)
{
return stream->data_in->di_if->di_get_frame(stream->data_in,
stream->read_offset) != NULL;
return stream_has_frame_at_read_offset(stream);
}
@ -690,8 +704,7 @@ stream_readable_http_gquic (struct lsquic_stream *stream)
{
return (stream->stream_flags & STREAM_HAVE_UH)
&& (stream->uh
|| stream->data_in->di_if->di_get_frame(stream->data_in,
stream->read_offset));
|| stream_has_frame_at_read_offset(stream));
}
@ -708,8 +721,7 @@ stream_readable_http_ietf (struct lsquic_stream *stream)
(stream->sm_sfi->sfi_readable(stream)
&& (/* Running the filter may result in hitting FIN: */
(stream->stream_flags & STREAM_FIN_REACHED)
|| stream->data_in->di_if->di_get_frame(stream->data_in,
stream->read_offset)));
|| stream_has_frame_at_read_offset(stream)));
}
@ -940,6 +952,7 @@ lsquic_stream_frame_in (lsquic_stream_t *stream, stream_frame_t *frame)
end_ok:
if (free_frame)
lsquic_malo_put(frame);
stream->stream_flags &= ~STREAM_CACHED_FRAME;
return rv;
}
else if (INS_FRAME_DUP == ins_frame)
@ -977,6 +990,7 @@ drop_frames_in (lsquic_stream_t *stream)
* dropped.
*/
stream->data_in = data_in_error_new();
stream->stream_flags &= ~STREAM_CACHED_FRAME;
}
}
@ -1462,7 +1476,7 @@ readv_f (void *ctx_p, const unsigned char *buf, size_t len, int fin)
if (ctx->iov < ctx->end)
ctx->p = ctx->iov->iov_base;
else
ctx->p = NULL;
break;
}
}
@ -2584,7 +2598,9 @@ frame_hq_gen_read (void *ctx, void *begin_buf, size_t len, int *fin)
rem = (1 << 14) - 1;
shf = stream_activate_hq_frame(stream,
stream->sm_payload, HQFT_DATA, 0, rem);
if (!shf)
if (shf)
goto insert;
else
{
/* TODO: abort connection? Handle failure somehow */
break;
@ -2593,10 +2609,10 @@ frame_hq_gen_read (void *ctx, void *begin_buf, size_t len, int *fin)
else
break;
}
avail = stream->sm_n_buffered + stream->sm_write_avail(stream);
if (shf->shf_off == stream->sm_payload
&& !(shf->shf_flags & SHF_WRITTEN))
{
insert:
frame_sz = stream_hq_frame_size(shf);
if (frame_sz > (uintptr_t) (end - p))
{
@ -2634,6 +2650,7 @@ frame_hq_gen_read (void *ctx, void *begin_buf, size_t len, int *fin)
}
else
{
avail = stream->sm_n_buffered + stream->sm_write_avail(stream);
len = stream_hq_frame_end(shf) - stream->sm_payload;
assert(len);
if (len > (unsigned) (end - p))

View file

@ -194,7 +194,7 @@ enum stream_flags {
STREAM_FIN_REACHED = 1 << 7, /* User read data up to FIN */
STREAM_FINISHED = 1 << 8, /* Stream is finished */
STREAM_ONCLOSE_DONE = 1 << 9, /* on_close has been called */
STREAM_UNUSED10 = 1 << 10, /* Unused */
STREAM_CACHED_FRAME = 1 << 10, /* If set, sm_has_frame can be used */
STREAM_HEADERS_SENT = 1 << 11,
STREAM_HAVE_UH = 1 << 12, /* Have uncompressed headers */
STREAM_ENCODER_DEP = 1 << 13, /* Encoder dependency: flush (IETF only) */
@ -310,6 +310,8 @@ struct lsquic_stream
/* Push promises sent on this stream */
SLIST_HEAD(, push_promise) sm_promises;
uint64_t sm_last_frame_off;
/* How much data there is in sm_header_block and how much of it has been
* sent:
*/
@ -327,6 +329,7 @@ struct lsquic_stream
SSHS_HBLOCK_SENDING,/* Sending header block data */
} sm_send_headers_state:8;
signed char sm_saved_want_write;
signed char sm_has_frame;
unsigned char sm_dup_push_off;
unsigned char sm_dup_push_len;

View file

@ -1168,6 +1168,8 @@ interop_server_hset_add_header (void *hset_p, unsigned name_idx,
req->qif_str[req->qif_sz + name_len + 1 + value_len] = '\n';
req->qif_sz += name_len + value_len + 2;
}
else
return LSQUIC_HDR_OK;
if (5 == name_len && 0 == strncmp(name, ":path", 5))
{

View file

@ -368,7 +368,7 @@ prog_connect (struct prog *prog, unsigned char *zero_rtt, size_t zero_rtt_len)
struct service_port *sport;
sport = TAILQ_FIRST(prog->prog_sports);
if (NULL == lsquic_engine_connect(prog->prog_engine,
if (NULL == lsquic_engine_connect(prog->prog_engine, N_LSQVER,
(struct sockaddr *) &sport->sp_local_addr,
(struct sockaddr *) &sport->sas, sport, NULL,
prog->prog_hostname ? prog->prog_hostname : sport->host,

View file

@ -117,8 +117,6 @@ load_cert (struct lsquic_hash *certs, const char *optarg)
end:
free(sni);
if (f)
fclose(f);
if (rv != 0)
{ /* Error: free cert and its components */
if (cert)

View file

@ -1592,15 +1592,14 @@ send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count)
if (n > 0)
return n;
else if (s < 0)
else
{
assert(s < 0);
#if LSQUIC_RANDOM_SEND_FAILURE
random_send_failure:
#endif
return -1;
}
else
return 0;
}

View file

@ -32,6 +32,21 @@ alarm_cb (enum alarm_id al_id, void *ctx, lsquic_time_t expiry,
}
#if __GNUC__
# define popcount __builtin_popcount
#else
static int
popcount (unsigned v)
{
int count, i;
for (i = 0, count = 0; i < sizeof(v) * 8; ++i)
if (v & (1 << i))
++count;
return count;
}
#endif
int
main (void)
{
@ -83,5 +98,32 @@ main (void)
assert(4 == global_ctx.n_calls);
assert(20 == global_ctx.last_expiry);
unsigned t = 1;
for (i = 1; i < (1u << MAX_LSQUIC_ALARMS); ++i)
{
alset.as_armed_set = 0; /* Unset all */
unsigned const count = popcount(i);
unsigned const min_n = i % count;
unsigned const min_t = t++;
unsigned j, n, ids[2];
for (j = 0, n = 0; j < MAX_LSQUIC_ALARMS; ++j)
{
if ((1u << j) & i)
{
if (n == min_n)
{
ids[0] = j;
lsquic_alarmset_set(&alset, j, min_t);
}
else
lsquic_alarmset_set(&alset, j, t++);
++n;
}
}
lsquic_time_t found_min_t = lsquic_alarmset_mintime(&alset, &ids[1]);
assert(min_t == found_min_t);
assert(ids[0] == ids[1]);
}
return 0;
}

View file

@ -690,7 +690,7 @@ test_frame_header_split (unsigned n_packets)
struct lsquic_packet_out *const packet_out
= lsquic_send_ctl_new_packet_out(&tobjs.send_ctl, 0, PNS_APP, &network_path);
assert(packet_out);
const size_t pad_size = packet_out->po_n_alloc
- 2 /* STREAM header */
- 5 /* 3-byte HEADERS frame */

View file

@ -147,7 +147,7 @@ main (int argc, char **argv)
cid.idbuf[2] = i;
puel = lsquic_purga_contains(purga, &cid);
assert(puel && PUTY_CONN_DELETED == puel->puel_type);
~i == puel->puel_time;
assert(~i == puel->puel_time);
}
++cid.idbuf[1];

View file

@ -152,25 +152,31 @@ test_different_priorities (int *priority)
struct stream_info
{
uint32_t stream_id;
enum stream_b_flags bflags;
unsigned char prio;
};
const struct stream_info infos1[] = {
{ LSQUIC_GQUIC_STREAM_HANDSHAKE, 0, },
{ LSQUIC_GQUIC_STREAM_HEADERS, 0, },
{ 5, 0, },
{ 7, 1, },
{ 127, 200, },
{ LSQUIC_GQUIC_STREAM_HANDSHAKE, SMBF_CRITICAL, 0, },
{ LSQUIC_GQUIC_STREAM_HEADERS, SMBF_CRITICAL, 0, },
{ 5, 0, 0, },
{ 7, 0, 1, },
{ 127, 0, 200, },
};
const struct stream_info infos2[] = {
{ LSQUIC_GQUIC_STREAM_HANDSHAKE, 0, },
{ LSQUIC_GQUIC_STREAM_HEADERS, 0, },
{ 5, 4, },
{ 7, 1, },
{ 127, 200, },
{ LSQUIC_GQUIC_STREAM_HANDSHAKE, SMBF_CRITICAL, 0, },
{ LSQUIC_GQUIC_STREAM_HEADERS, SMBF_CRITICAL, 0, },
{ 5, 0, 4, },
{ 7, 0, 1, },
{ 127, 0, 200, },
};
const struct stream_info infos3[] = {
{ 0, 0, 0, },
};
@ -185,6 +191,7 @@ struct drop_test
static const struct drop_test drop_tests[] = {
{ infos1, 5, 0x7, },
{ infos2, 5, 0x3, },
{ infos3, 1, 0x0, },
};
@ -203,7 +210,7 @@ test_drop (const struct drop_test *test)
{
stream_arr[n].sm_priority = test->infos[n].prio;
stream_arr[n].id = test->infos[n].stream_id;
stream_arr[n].sm_bflags = SMBF_USE_HEADERS;
stream_arr[n].sm_bflags = SMBF_USE_HEADERS | test->infos[n].bflags;
}
for (drop_high = 0; drop_high < 2; ++drop_high)
@ -227,7 +234,9 @@ test_drop (const struct drop_test *test)
stream = lsquic_spi_next(&spi))
seen_mask |= 1 << (stream - stream_arr);
if (drop_high)
if (test->n_infos == 1)
assert(seen_mask == (1u << test->infos[0].stream_id));
else if (drop_high)
assert((((1 << test->n_infos) - 1) & ~test->high_streams) == seen_mask);
else
assert(test->high_streams == seen_mask);

View file

@ -144,7 +144,7 @@ run_gvnt (int i)
/* XXX this is never executed, as there is no test case for this */
scid = (lsquic_cid_t) { .len = 0, };
len = lsquic_Q046_gen_ver_nego_pkt(out, gvnt->gvnt_bufsz, &dcid,
&scid, gvnt->gvnt_versions);
&scid, gvnt->gvnt_versions, ((unsigned char) rand()) & 0xF);
}
assert(("Packet length is correct", len == gvnt->gvnt_len));