Release 2.8.1

- [FEATURE] Use occasional packet number gaps to detect optimistic
  ACK attacks.
- [BUGFIX] Q050 client: all packet numbers are in the App PNS.
- [OPTIMIZATION] Merge multi-range ACK frames, not just single-range
  ACK frames.
- IETF QUIC: use RTT estimate in ack timeout calculation.
- IETF handshake: abort conn when unexpected errors occur.
- Use PING rather than MAX_DATA frames to elicit ACKs from peer.
- Server: enforce 1200 byte Initial minimum packet size.
- [CLEANUP] Remove code to disable gQUIC crypto.
- [CLEANUP] Remove n_timestamps from ACK info struct.
- Optimize driver: reuse previous ancillary message when possible.
This commit is contained in:
Dmitri Tikhonov 2019-12-30 11:29:05 -05:00
parent 022d9812f3
commit de46bf2f1f
35 changed files with 708 additions and 768 deletions

View File

@ -1,3 +1,18 @@
2019-12-30
- 2.8.1
- [FEATURE] Use occasional packet number gaps to detect optimistic
ACK attacks.
- [BUGFIX] Q050 client: all packet numbers are in the App PNS.
- [OPTIMIZATION] Merge multi-range ACK frames, not just single-range
ACK frames.
- IETF QUIC: use RTT estimate in ack timeout calculation.
- IETF handshake: abort conn when unexpected errors occur.
- Use PING rather than MAX_DATA frames to elicit ACKs from peer.
- Server: enforce 1200 byte Initial minimum packet size.
- [CLEANUP] Remove code to disable gQUIC crypto.
- [CLEANUP] Remove n_timestamps from ACK info struct.
- Optimize driver: reuse previous ancillary message when possible.
2019-12-23
- 2.8.0
- [FEATURE] Add support for Q050.

View File

@ -166,19 +166,6 @@ LSQUIC_PACKET_OUT_LIMIT
Note 2: see -m option for a related packet-out limitation.
LSQUIC_DISABLE_HANDSHAKE
If set (to anything, not any particular value), the QUIC handshake is
disabled and packets are not encrypted. This can be useful to:
a) profile functions in LSQUIC without accounting for crypto stuff,
which tends to dwarf everything else;
b) see bytes in the clear on the wire; and
c) compare throughput performance to TCP without crypto.
This functionality is compiled in if the somewhat-awkwardly named
LSQUIC_ENABLE_HANDSHAKE_DISABLE is set to 1. By default, it is enabled
in debug builds and disabled in optimized builds.
LSQUIC_LOSE_PACKETS_RE
If set, this regular expression specifies the numbers of packets which
@ -228,6 +215,10 @@ LSQUIC_USE_POOLS
malloc() and free(). This facilitates debugging memory issues.
The default is true.
LSQUIC_ACK_ATTACK
If set to true, generate optimistic ACKs.
Control Network-Related Stuff
-----------------------------
@ -246,10 +237,6 @@ Control Network-Related Stuff
More Compilation Options
------------------------
-DLSQUIC_ENABLE_HANDSHAKE_DISABLE=1
Support disabling of handshake. See above.
-DLSQUIC_CONN_STATS=1
Track some statistics about connections -- packets in, sent, delayed,
@ -296,3 +283,8 @@ More Compilation Options
When compiled with this flag, setting environment variable
LSQUIC_ECN_BLACK_HOLE to 1 will emulate ECN black hole: all received
packets with ECN markings are dropped on the floor.
-DLSQUIC_ACK_ATTACK=1
Enable ACK attack mode. See LSQUIC_ACK_ATTACK environment variable
entry above.

View File

@ -25,7 +25,7 @@ extern "C" {
#define LSQUIC_MAJOR_VERSION 2
#define LSQUIC_MINOR_VERSION 8
#define LSQUIC_PATCH_VERSION 0
#define LSQUIC_PATCH_VERSION 1
/**
* Engine flags:

View File

@ -38,16 +38,6 @@ hsk_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
LSQ_DEBUG("stream created");
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
if (getenv("LSQUIC_DISABLE_HANDSHAKE"))
{
LSQ_WARN("Handshake disabled: faking it");
c_hsk->lconn->cn_flags |= LSCONN_NO_CRYPTO;
c_hsk->lconn->cn_if->ci_handshake_ok(c_hsk->lconn);
return (void *) c_hsk;
}
#endif
lsquic_stream_wantwrite(stream, 1);
return (void *) c_hsk;

View File

@ -52,9 +52,7 @@ enum lsquic_conn_flags {
LSCONN_UNUSED_18 = (1 <<18),
LSCONN_ATTQ = (1 <<19),
LSCONN_SKIP_ON_PROC = (1 <<20),
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
LSCONN_NO_CRYPTO = (1 <<21),
#endif
LSCONN_UNUSED_21 = (1 <<21),
LSCONN_SERVER = (1 <<22),
LSCONN_IETF = (1 <<23),
LSCONN_RETRY_CONN = (1 <<24), /* This is a retry connection */
@ -362,7 +360,7 @@ struct conn_stats {
err_packets; /* Error packets(?) */
unsigned long n_acks,
n_acks_proc,
n_acks_merged[2];
n_acks_merged;
unsigned long bytes; /* Overall bytes in */
unsigned long headers_uncomp; /* Sum of uncompressed header bytes */
unsigned long headers_comp; /* Sum of compressed header bytes */

View File

@ -51,8 +51,6 @@ enum handshake_error /* TODO: rename this enum */
HS_SHLO = 0,
HS_1RTT = 1,
HS_SREJ = 2,
HS_DELAYED = 3,
HS_PK_OFFLOAD = 4,
};
#ifndef LSQUIC_KEEP_ENC_SESS_HISTORY
@ -208,11 +206,6 @@ struct enc_session_funcs_gquic
(*esf_get_dec_key_nonce_f) (enc_session_t *);
#endif /* !defined(NDEBUG) */
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
void
(*esf_set_handshake_completed) (enc_session_t *);
#endif
/* Create client session */
enc_session_t *
(*esf_create_client) (struct lsquic_conn *, const char *domain,

View File

@ -58,14 +58,6 @@
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(enc_sess->esi_conn)
#include "lsquic_logger.h"
/* [draft-ietf-quic-tls-11] Section 5.3.2 */
#define HSK_SECRET_SZ SHA256_DIGEST_LENGTH
/* TODO: Specify ciphers */
#define HSK_CIPHERS "TLS13-AES-128-GCM-SHA256" \
":TLS13-AES-256-GCM-SHA384" \
":TLS13-CHACHA20-POLY1305-SHA256"
#define KEY_LABEL "quic key"
#define KEY_LABEL_SZ (sizeof(KEY_LABEL) - 1)
#define IV_LABEL "quic iv"
@ -1585,7 +1577,8 @@ iquic_esfi_post_handshake (struct enc_sess_iquic *enc_sess)
return IHS_WANT_READ;
else
{
LSQ_DEBUG("TODO: abort connection?");
enc_sess->esi_conn->cn_if->ci_internal_error(enc_sess->esi_conn,
"post-handshake error, code %d", s);
return IHS_STOP;
}
}
@ -2735,8 +2728,8 @@ maybe_write_from_fral (struct enc_sess_iquic *enc_sess,
}
else
{
/* TODO: abort connection */
LSQ_WARN("cannot write to stream: %s", strerror(errno));
enc_sess->esi_conn->cn_if->ci_internal_error(enc_sess->esi_conn,
"cannot write to stream: %s", strerror(errno));
lsquic_stream_wantwrite(stream, 0);
}
}

View File

@ -2237,10 +2237,6 @@ iov_size (const struct iovec *iov, const struct iovec *const end)
}
/* XXX A lot of extra setup -- two extra arguments to this function, two extra
* connection ref flags and queues -- is just to handle the ENCPA_BADCRYPT case,
* which never really happens.
*/
static void
send_packets_out (struct lsquic_engine *engine,
struct conns_tailq *ticked_conns,
@ -2417,7 +2413,6 @@ reset_deadline (lsquic_engine_t *engine, lsquic_time_t now)
}
/* TODO: this is a user-facing function, account for load */
void
lsquic_engine_send_unsent_packets (lsquic_engine_t *engine)
{

View File

@ -21,6 +21,7 @@ enum warning_type
{
WT_ACKPARSE_MINI,
WT_ACKPARSE_FULL,
WT_NO_POISON,
N_WARNING_TYPES,
};

View File

@ -93,14 +93,10 @@ void
lsquic_ev_log_ack_frame_in (const lsquic_cid_t *cid,
const struct ack_info *acki)
{
size_t sz;
char *buf;
char buf[MAX_ACKI_STR_SZ];
if ((buf = acki2str(acki, &sz)))
{
LCID("ACK frame in: %.*s", (int) sz, buf);
free(buf);
}
lsquic_acki2str(acki, buf, sizeof(buf));
LCID("ACK frame in: %s", buf);
}
@ -361,9 +357,8 @@ lsquic_ev_log_generated_ack_frame (const lsquic_cid_t *cid,
size_t ack_buf_sz)
{
struct ack_info acki;
size_t sz;
char *buf;
int len;
char buf[MAX_ACKI_STR_SZ];
len = pf->pf_parse_ack_frame(ack_buf, ack_buf_sz, &acki,
TP_DEF_ACK_DELAY_EXP);
@ -373,11 +368,8 @@ lsquic_ev_log_generated_ack_frame (const lsquic_cid_t *cid,
return;
}
if ((buf = acki2str(&acki, &sz)))
{
LCID("generated ACK frame: %.*s", (int) sz, buf);
free(buf);
}
lsquic_acki2str(&acki, buf, sizeof(buf));
LCID("generated ACK frame: %s", buf);
}

View File

@ -216,11 +216,11 @@ struct full_conn
#endif
STAILQ_HEAD(, stream_id_to_reset)
fc_stream_ids_to_reset;
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 */
enum enc_level fc_crypto_enc_level;
struct ack_info fc_ack;
};
static const struct ver_neg server_ver_neg;
@ -521,31 +521,13 @@ apply_peer_settings (struct full_conn *conn)
return 0;
#endif
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
if (conn->fc_conn.cn_flags & LSCONN_NO_CRYPTO)
{ /* Magically figure out peer's settings */
if (conn->fc_flags & FC_SERVER)
for (n = 0; n < sizeof(tags) / sizeof(tags[0]); ++n)
if (0 != conn->fc_conn.cn_esf.g->esf_get_peer_setting(
conn->fc_conn.cn_enc_session, tags[n].tag, tags[n].val))
{
cfcw = LSQUIC_DF_CFCW_CLIENT;
sfcw = LSQUIC_DF_SFCW_CLIENT;
mids = 100;
LSQ_INFO("peer did not supply value for %s", tags[n].tag_str);
return -1;
}
else
{
cfcw = LSQUIC_DF_CFCW_SERVER;
sfcw = LSQUIC_DF_SFCW_SERVER;
mids = 100;
}
}
else
#endif
for (n = 0; n < sizeof(tags) / sizeof(tags[0]); ++n)
if (0 != conn->fc_conn.cn_esf.g->esf_get_peer_setting(
conn->fc_conn.cn_enc_session, tags[n].tag, tags[n].val))
{
LSQ_INFO("peer did not supply value for %s", tags[n].tag_str);
return -1;
}
LSQ_DEBUG("peer settings: CFCW: %u; SFCW: %u; MIDS: %u",
cfcw, sfcw, mids);
@ -893,6 +875,7 @@ lsquic_gquic_full_conn_server_new (struct lsquic_engine_public *enpub,
lsquic_send_ctl_verneg_done(&conn->fc_send_ctl);
conn->fc_send_ctl.sc_cur_packno = mc->mc_cur_packno;
lsquic_send_ctl_begin_optack_detection(&conn->fc_send_ctl);
/* Remove those that still exist from the set: they will be marked as
* received during regular processing in ci_packet_in() later on.
@ -952,10 +935,6 @@ lsquic_gquic_full_conn_server_new (struct lsquic_engine_public *enpub,
assert(lconn_mini->cn_flags & LSCONN_HANDSHAKE_DONE);
lconn_full->cn_flags |= LSCONN_HANDSHAKE_DONE;
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
if (getenv("LSQUIC_DISABLE_HANDSHAKE"))
lconn_full->cn_flags |= LSCONN_NO_CRYPTO;
#endif
lconn_full->cn_flags |= lconn_mini->cn_flags &
LSCONN_PEER_GOING_AWAY /* We are OK with fc_goaway_stream_id = 0 */;
@ -1130,9 +1109,9 @@ full_conn_ci_destroy (lsquic_conn_t *lconn)
conn->fc_stats.in.dup_packets, conn->fc_stats.in.err_packets,
conn->fc_stats.out.packets,
conn->fc_stats.out.stream_data_sz / conn->fc_stats.out.packets);
LSQ_NOTICE("ACKs: in: %lu; processed: %lu; merged to: new %lu, old %lu",
LSQ_NOTICE("ACKs: in: %lu; processed: %lu; merged: %lu",
conn->fc_stats.in.n_acks, conn->fc_stats.in.n_acks_proc,
conn->fc_stats.in.n_acks_merged[0], conn->fc_stats.in.n_acks_merged[1]);
conn->fc_stats.in.n_acks_merged);
#endif
while ((sitr = STAILQ_FIRST(&conn->fc_stream_ids_to_reset)))
{
@ -1580,9 +1559,6 @@ process_stream_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
enc_level = lsquic_packet_in_enc_level(packet_in);
if (!is_handshake_stream_id(conn, stream_frame->stream_id)
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
&& !(conn->fc_conn.cn_flags & LSCONN_NO_CRYPTO)
#endif
&& enc_level == ENC_LEV_CLEAR)
{
lsquic_malo_put(stream_frame);
@ -1863,28 +1839,21 @@ log_invalid_ack_frame (struct full_conn *conn, const unsigned char *p,
int parsed_len, const struct ack_info *acki)
{
char *buf;
size_t sz;
buf = malloc(0x1000);
if (buf)
if (!buf)
{
lsquic_senhist_tostr(&conn->fc_send_ctl.sc_senhist, buf, 0x1000);
LSQ_WARN("send history: %s", buf);
lsquic_hexdump(p, parsed_len, buf, 0x1000);
LSQ_WARN("raw ACK frame:\n%s", buf);
free(buf);
}
else
LSQ_WARN("malloc failed");
return;
}
buf = acki2str(acki, &sz);
if (buf)
{
LSQ_WARN("parsed ACK frame: %.*s", (int) sz, buf);
free(buf);
}
else
LSQ_WARN("malloc failed");
lsquic_senhist_tostr(&conn->fc_send_ctl.sc_senhist, buf, 0x1000);
LSQ_WARN("send history: %s", buf);
lsquic_hexdump(p, parsed_len, buf, 0x1000);
LSQ_WARN("raw ACK frame:\n%s", buf);
lsquic_acki2str(acki, buf, 0x1000);
LSQ_WARN("parsed ACK frame: %s", buf);
free(buf);
}
@ -1912,108 +1881,11 @@ process_ack (struct full_conn *conn, struct ack_info *acki,
}
static int
process_saved_ack (struct full_conn *conn, int restore_parsed_ack,
lsquic_time_t now)
{
struct ack_info *const acki = conn->fc_pub.mm->acki;
struct lsquic_packno_range range;
unsigned n_ranges, n_timestamps;
lsquic_time_t lack_delta;
int retval;
#ifdef WIN32
/* Useless initialization to mollify MSVC: */
memset(&range, 0, sizeof(range));
n_ranges = 0;
n_timestamps = 0;
lack_delta = 0;
#endif
if (restore_parsed_ack)
{
n_ranges = acki->n_ranges;
n_timestamps = acki->n_timestamps;
lack_delta = acki->lack_delta;
range = acki->ranges[0];
}
acki->pns = PNS_APP;
acki->n_ranges = 1;
acki->n_timestamps = conn->fc_saved_ack_info.sai_n_timestamps;
acki->lack_delta = conn->fc_saved_ack_info.sai_lack_delta;
acki->ranges[0] = conn->fc_saved_ack_info.sai_range;
retval = process_ack(conn, acki, conn->fc_saved_ack_received, now);
if (restore_parsed_ack)
{
acki->n_ranges = n_ranges;
acki->n_timestamps = n_timestamps;
acki->lack_delta = lack_delta;
acki->ranges[0] = range;
}
return retval;
}
static int
new_ack_is_superset (const struct short_ack_info *old, const struct ack_info *new)
{
const struct lsquic_packno_range *new_range;
new_range = &new->ranges[ new->n_ranges - 1 ];
return new_range->low <= old->sai_range.low
&& new_range->high >= old->sai_range.high;
}
static int
merge_saved_to_new (const struct short_ack_info *old, struct ack_info *new)
{
struct lsquic_packno_range *smallest_range;
assert(new->n_ranges > 1);
smallest_range = &new->ranges[ new->n_ranges - 1 ];
if (old->sai_range.high <= smallest_range->high
&& old->sai_range.high >= smallest_range->low
&& old->sai_range.low < smallest_range->low)
{
smallest_range->low = old->sai_range.low;
return 1;
}
else
return 0;
}
static int
merge_new_to_saved (struct short_ack_info *old, const struct ack_info *new)
{
const struct lsquic_packno_range *new_range;
assert(new->n_ranges == 1);
new_range = &new->ranges[0];
/* Only merge if new is higher, for simplicity. This is also the
* expected case.
*/
if (new_range->high > old->sai_range.high
&& new_range->low > old->sai_range.low)
{
old->sai_range.high = new_range->high;
return 1;
}
else
return 0;
}
static unsigned
process_ack_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
const unsigned char *p, size_t len)
{
struct ack_info *const new_acki = conn->fc_pub.mm->acki;
struct ack_info *new_acki;
int parsed_len;
lsquic_time_t warn_time;
@ -2021,6 +1893,11 @@ process_ack_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
++conn->fc_stats.in.n_acks;
#endif
if (conn->fc_flags & FC_HAVE_SAVED_ACK)
new_acki = conn->fc_pub.mm->acki;
else
new_acki = &conn->fc_ack;
parsed_len = conn->fc_conn.cn_pf->pf_parse_ack_frame(p, len, new_acki, 0);
if (parsed_len < 0)
goto err;
@ -2040,77 +1917,33 @@ process_ack_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
EV_LOG_ACK_FRAME_IN(LSQUIC_LOG_CONN_ID, new_acki);
conn->fc_max_ack_packno = packet_in->pi_packno;
if (conn->fc_flags & FC_HAVE_SAVED_ACK)
if (new_acki == &conn->fc_ack)
{
LSQ_DEBUG("old ack [%"PRIu64"-%"PRIu64"]",
conn->fc_saved_ack_info.sai_range.high,
conn->fc_saved_ack_info.sai_range.low);
const int is_superset = new_ack_is_superset(&conn->fc_saved_ack_info,
new_acki);
const int is_1range = new_acki->n_ranges == 1;
switch (
(is_superset << 1)
| (is_1range << 0))
/* | |
| |
V V */ {
case (0 << 1) | (0 << 0):
if (merge_saved_to_new(&conn->fc_saved_ack_info, new_acki))
{
LSQ_DEBUG("Saved ACK");
conn->fc_flags |= FC_HAVE_SAVED_ACK;
conn->fc_saved_ack_received = packet_in->pi_received;
}
else
{
if (0 == lsquic_merge_acks(&conn->fc_ack, new_acki))
{
#if LSQUIC_CONN_STATS
++conn->fc_stats.in.n_acks_merged[0]
++conn->fc_stats.in.n_acks_merged;
#endif
;
}
else
process_saved_ack(conn, 1, packet_in->pi_received);
conn->fc_flags &= ~FC_HAVE_SAVED_ACK;
if (0 != process_ack(conn, new_acki, packet_in->pi_received,
LSQ_DEBUG("merged into saved ACK, getting %s",
(lsquic_acki2str(&conn->fc_ack, conn->fc_pub.mm->ack_str,
MAX_ACKI_STR_SZ), conn->fc_pub.mm->ack_str));
}
else
{
LSQ_DEBUG("could not merge new ACK into saved ACK");
if (0 != process_ack(conn, &conn->fc_ack, packet_in->pi_received,
packet_in->pi_received))
goto err;
break;
case (0 << 1) | (1 << 0):
if (merge_new_to_saved(&conn->fc_saved_ack_info, new_acki))
{
#if LSQUIC_CONN_STATS
++conn->fc_stats.in.n_acks_merged[1]
#endif
;
}
else
{
process_saved_ack(conn, 1, packet_in->pi_received);
conn->fc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
conn->fc_saved_ack_info.sai_range = new_acki->ranges[0];
}
conn->fc_saved_ack_info.sai_lack_delta = new_acki->lack_delta;
conn->fc_saved_ack_received = packet_in->pi_received;
break;
case (1 << 1) | (0 << 0):
conn->fc_flags &= ~FC_HAVE_SAVED_ACK;
if (0 != process_ack(conn, new_acki, packet_in->pi_received,
packet_in->pi_received))
goto err;
break;
case (1 << 1) | (1 << 0):
conn->fc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
conn->fc_saved_ack_info.sai_lack_delta = new_acki->lack_delta;
conn->fc_saved_ack_info.sai_range = new_acki->ranges[0];
conn->fc_saved_ack_received = packet_in->pi_received;
break;
conn->fc_ack = *new_acki;
}
conn->fc_saved_ack_received = packet_in->pi_received;
}
else if (new_acki->n_ranges == 1)
{
conn->fc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
conn->fc_saved_ack_info.sai_lack_delta = new_acki->lack_delta;
conn->fc_saved_ack_info.sai_range = new_acki->ranges[0];
conn->fc_saved_ack_received = packet_in->pi_received;
conn->fc_flags |= FC_HAVE_SAVED_ACK;
}
else if (0 != process_ack(conn, new_acki, packet_in->pi_received,
packet_in->pi_received))
goto err;
return parsed_len;
@ -2439,17 +2272,7 @@ reconstruct_packet_number (struct full_conn *conn, lsquic_packet_in_t *packet_in
static enum dec_packin
conn_decrypt_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
{
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
if (conn->fc_conn.cn_flags & LSCONN_NO_CRYPTO)
{
(void) lsquic_conn_copy_and_release_pi_data(&conn->fc_conn,
conn->fc_enpub, packet_in);
packet_in->pi_flags |= PI_DECRYPTED;
return DECPI_OK;
}
else
#endif
return conn->fc_conn.cn_esf_c->esf_decrypt_packet(
return conn->fc_conn.cn_esf_c->esf_decrypt_packet(
conn->fc_conn.cn_enc_session, conn->fc_enpub,
&conn->fc_conn, packet_in);
}
@ -3460,7 +3283,7 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
if (conn->fc_flags & FC_HAVE_SAVED_ACK)
{
(void) /* If there is an error, we'll fail shortly */
process_saved_ack(conn, 0, now);
process_ack(conn, &conn->fc_ack, conn->fc_saved_ack_received, now);
conn->fc_flags &= ~FC_HAVE_SAVED_ACK;
}
@ -3780,6 +3603,8 @@ full_conn_ci_hsk_done (lsquic_conn_t *lconn, enum lsquic_hsk_status status)
conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_zero_rtt_info);
if (conn->fc_n_delayed_streams)
create_delayed_streams(conn);
if (!(conn->fc_flags & FC_SERVER))
lsquic_send_ctl_begin_optack_detection(&conn->fc_send_ctl);
}
}

View File

@ -313,7 +313,6 @@ struct ietf_full_conn
struct transport_params ifc_peer_param;
STAILQ_HEAD(, stream_id_to_ss)
ifc_stream_ids_to_ss;
struct short_ack_info ifc_saved_ack_info;
lsquic_time_t ifc_saved_ack_received;
lsquic_packno_t ifc_max_ack_packno[N_PNS];
lsquic_packno_t ifc_max_non_probing;
@ -387,6 +386,7 @@ struct ietf_full_conn
*/
lsquic_time_t ifc_idle_to;
lsquic_time_t ifc_ping_period;
struct ack_info ifc_ack;
};
#define CUR_CPATH(conn_) (&(conn_)->ifc_paths[(conn_)->ifc_cur_path_id])
@ -1214,6 +1214,7 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
conn->ifc_process_incoming_packet = process_incoming_packet_fast;
conn->ifc_send_ctl.sc_cur_packno = imc->imc_next_packno - 1;
lsquic_send_ctl_begin_optack_detection(&conn->ifc_send_ctl);
for (pns = 0; pns < N_PNS; ++pns)
{
@ -1386,12 +1387,13 @@ generate_ack_frame_for_pns (struct ietf_full_conn *conn,
else
conn->ifc_flags &= ~IFC_ACK_HAD_MISS;
LSQ_DEBUG("Put %d bytes of ACK frame into packet on outgoing queue", w);
/* TODO: randomize the 20 to be 20 +/- 8 */
if (conn->ifc_n_cons_unretx >= 20 &&
!lsquic_send_ctl_have_outgoing_retx_frames(&conn->ifc_send_ctl))
{
LSQ_DEBUG("schedule MAX_DATA frame after %u non-retx "
LSQ_DEBUG("schedule PING frame after %u non-retx "
"packets sent", conn->ifc_n_cons_unretx);
conn->ifc_send_flags |= SF_SEND_MAX_DATA;
conn->ifc_send_flags |= SF_SEND_PING;
}
conn->ifc_n_slack_akbl[pns] = 0;
@ -2874,7 +2876,12 @@ ietf_full_conn_ci_hsk_done (struct lsquic_conn *lconn,
{
case LSQ_HSK_OK:
case LSQ_HSK_0RTT_OK:
if (0 != handshake_ok(lconn))
if (0 == handshake_ok(lconn))
{
if (!(conn->ifc_flags & IFC_SERVER))
lsquic_send_ctl_begin_optack_detection(&conn->ifc_send_ctl);
}
else
{
LSQ_INFO("handshake was reported successful, but later processing "
"produced an error");
@ -3544,8 +3551,8 @@ generate_connection_close_packet (struct ietf_full_conn *conn)
}
static int
generate_ping_frame (struct ietf_full_conn *conn)
static void
generate_ping_frame (struct ietf_full_conn *conn, lsquic_time_t unused)
{
struct lsquic_packet_out *packet_out;
int sz;
@ -3554,19 +3561,19 @@ generate_ping_frame (struct ietf_full_conn *conn)
if (!packet_out)
{
LSQ_DEBUG("cannot get writeable packet for PING frame");
return 1;
return;
}
sz = conn->ifc_conn.cn_pf->pf_gen_ping_frame(
packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out));
if (sz < 0) {
ABORT_ERROR("gen_ping_frame failed");
return 1;
return;
}
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_PING;
LSQ_DEBUG("wrote PING frame");
return 0;
conn->ifc_send_flags &= ~SF_SEND_PING;
}
@ -3821,103 +3828,6 @@ process_ack (struct ietf_full_conn *conn, struct ack_info *acki,
}
static int
process_saved_ack (struct ietf_full_conn *conn, int restore_parsed_ack,
lsquic_time_t now)
{
struct ack_info *const acki = conn->ifc_pub.mm->acki;
struct lsquic_packno_range range;
unsigned n_ranges, n_timestamps;
lsquic_time_t lack_delta;
int retval;
#ifdef WIN32
/* Useless initialization to mollify MSVC: */
memset(&range, 0, sizeof(range));
n_ranges = 0;
n_timestamps = 0;
lack_delta = 0;
#endif
if (restore_parsed_ack)
{
n_ranges = acki->n_ranges;
n_timestamps = acki->n_timestamps;
lack_delta = acki->lack_delta;
range = acki->ranges[0];
}
acki->pns = PNS_APP;
acki->n_ranges = 1;
acki->n_timestamps = conn->ifc_saved_ack_info.sai_n_timestamps;
acki->lack_delta = conn->ifc_saved_ack_info.sai_lack_delta;
acki->ranges[0] = conn->ifc_saved_ack_info.sai_range;
retval = process_ack(conn, acki, conn->ifc_saved_ack_received, now);
if (restore_parsed_ack)
{
acki->n_ranges = n_ranges;
acki->n_timestamps = n_timestamps;
acki->lack_delta = lack_delta;
acki->ranges[0] = range;
}
return retval;
}
static int
new_ack_is_superset (const struct short_ack_info *old, const struct ack_info *new)
{
const struct lsquic_packno_range *new_range;
new_range = &new->ranges[ new->n_ranges - 1 ];
return new_range->low <= old->sai_range.low
&& new_range->high >= old->sai_range.high;
}
static int
merge_saved_to_new (const struct short_ack_info *old, struct ack_info *new)
{
struct lsquic_packno_range *smallest_range;
assert(new->n_ranges > 1);
smallest_range = &new->ranges[ new->n_ranges - 1 ];
if (old->sai_range.high <= smallest_range->high
&& old->sai_range.high >= smallest_range->low
&& old->sai_range.low < smallest_range->low)
{
smallest_range->low = old->sai_range.low;
return 1;
}
else
return 0;
}
static int
merge_new_to_saved (struct short_ack_info *old, const struct ack_info *new)
{
const struct lsquic_packno_range *new_range;
assert(new->n_ranges == 1);
new_range = &new->ranges[0];
/* Only merge if new is higher, for simplicity. This is also the
* expected case.
*/
if (new_range->high > old->sai_range.high
&& new_range->low > old->sai_range.low)
{
old->sai_range.high = new_range->high;
return 1;
}
else
return 0;
}
static unsigned
process_path_challenge_frame (struct ietf_full_conn *conn,
struct lsquic_packet_in *packet_in, const unsigned char *p, size_t len)
@ -4571,11 +4481,16 @@ static unsigned
process_ack_frame (struct ietf_full_conn *conn,
struct lsquic_packet_in *packet_in, const unsigned char *p, size_t len)
{
struct ack_info *const new_acki = conn->ifc_pub.mm->acki;
struct ack_info *new_acki;
enum packnum_space pns;
int parsed_len;
lsquic_time_t warn_time;
if (conn->ifc_flags & IFC_HAVE_SAVED_ACK)
new_acki = conn->ifc_pub.mm->acki;
else
new_acki = &conn->ifc_ack;
parsed_len = conn->ifc_conn.cn_pf->pf_parse_ack_frame(p, len, new_acki,
conn->ifc_cfg.ack_exp);
if (parsed_len < 0)
@ -4604,67 +4519,32 @@ process_ack_frame (struct ietf_full_conn *conn,
EV_LOG_ACK_FRAME_IN(LSQUIC_LOG_CONN_ID, new_acki);
conn->ifc_max_ack_packno[pns] = packet_in->pi_packno;
new_acki->pns = pns;
if (pns != PNS_APP) /* Don't bother optimizing non-APP */
goto process_ack;
if (conn->ifc_flags & IFC_HAVE_SAVED_ACK)
/* Only cache ACKs for PNS_APP */
if (pns == PNS_APP && new_acki == &conn->ifc_ack)
{
LSQ_DEBUG("old ack [%"PRIu64"-%"PRIu64"]",
conn->ifc_saved_ack_info.sai_range.high,
conn->ifc_saved_ack_info.sai_range.low);
const int is_superset = new_ack_is_superset(&conn->ifc_saved_ack_info,
new_acki);
const int is_1range = new_acki->n_ranges == 1;
switch (
(is_superset << 1)
| (is_1range << 0))
/* | |
| |
V V */ {
case (0 << 1) | (0 << 0):
if (!merge_saved_to_new(&conn->ifc_saved_ack_info, new_acki))
process_saved_ack(conn, 1, packet_in->pi_received);
conn->ifc_flags &= ~IFC_HAVE_SAVED_ACK;
if (0 != process_ack(conn, new_acki, packet_in->pi_received,
packet_in->pi_received))
goto err;
break;
case (0 << 1) | (1 << 0):
if (!merge_new_to_saved(&conn->ifc_saved_ack_info, new_acki))
{
process_saved_ack(conn, 1, packet_in->pi_received);
conn->ifc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
conn->ifc_saved_ack_info.sai_range = new_acki->ranges[0];
}
conn->ifc_saved_ack_info.sai_lack_delta = new_acki->lack_delta;
conn->ifc_saved_ack_received = packet_in->pi_received;
break;
case (1 << 1) | (0 << 0):
conn->ifc_flags &= ~IFC_HAVE_SAVED_ACK;
if (0 != process_ack(conn, new_acki, packet_in->pi_received,
packet_in->pi_received))
goto err;
break;
case (1 << 1) | (1 << 0):
conn->ifc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
conn->ifc_saved_ack_info.sai_lack_delta = new_acki->lack_delta;
conn->ifc_saved_ack_info.sai_range = new_acki->ranges[0];
conn->ifc_saved_ack_received = packet_in->pi_received;
break;
}
}
else if (new_acki->n_ranges == 1)
{
conn->ifc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
conn->ifc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
conn->ifc_saved_ack_info.sai_lack_delta = new_acki->lack_delta;
conn->ifc_saved_ack_info.sai_range = new_acki->ranges[0];
conn->ifc_saved_ack_received = packet_in->pi_received;
LSQ_DEBUG("Saved ACK");
conn->ifc_flags |= IFC_HAVE_SAVED_ACK;
conn->ifc_saved_ack_received = packet_in->pi_received;
}
else if (pns == PNS_APP)
{
if (0 == lsquic_merge_acks(&conn->ifc_ack, new_acki))
LSQ_DEBUG("merged into saved ACK, getting %s",
(lsquic_acki2str(&conn->ifc_ack, conn->ifc_pub.mm->ack_str,
MAX_ACKI_STR_SZ), conn->ifc_pub.mm->ack_str));
else
{
LSQ_DEBUG("could not merge new ACK into saved ACK");
if (0 != process_ack(conn, &conn->ifc_ack, packet_in->pi_received,
packet_in->pi_received))
goto err;
conn->ifc_ack = *new_acki;
}
conn->ifc_saved_ack_received = packet_in->pi_received;
}
else
{
process_ack:
if (0 != process_ack(conn, new_acki, packet_in->pi_received,
packet_in->pi_received))
goto err;
@ -5428,6 +5308,8 @@ static void
try_queueing_ack (struct ietf_full_conn *conn, enum packnum_space pns,
int was_missing, lsquic_time_t now)
{
lsquic_time_t srtt, ack_timeout;
if (conn->ifc_n_slack_akbl[pns] >= MAX_RETR_PACKETS_SINCE_LAST_ACK ||
((conn->ifc_flags & IFC_ACK_HAD_MISS) && was_missing))
{
@ -5441,18 +5323,16 @@ try_queueing_ack (struct ietf_full_conn *conn, enum packnum_space pns,
}
else if (conn->ifc_n_slack_akbl[pns] > 0)
{
/* [draft-ietf-quic-transport-15] Section-7.16.3:
*
* The receiver's delayed acknowledgment timer SHOULD NOT exceed the
* current RTT estimate or the value it indicates in the "max_ack_delay"
* transport parameter
*
* TODO: Need to do MIN(ACK_TIMEOUT, RTT Estimate)
*/
/* See https://github.com/quicwg/base-drafts/issues/3304 for more */
srtt = lsquic_rtt_stats_get_srtt(&conn->ifc_pub.rtt_stats);
if (srtt)
ack_timeout = MAX(1000, MIN(ACK_TIMEOUT, srtt / 4));
else
ack_timeout = ACK_TIMEOUT;
lsquic_alarmset_set(&conn->ifc_alset, AL_ACK_INIT + pns,
now + ACK_TIMEOUT);
now + ack_timeout);
LSQ_DEBUG("%s ACK alarm set to %"PRIu64, lsquic_pns2str[pns],
now + ACK_TIMEOUT);
now + ack_timeout);
}
}
@ -6069,6 +5949,7 @@ static void (*const send_funcs[N_SEND])(
[SEND_PATH_CHAL_PATH_1] = generate_path_chal_1,
[SEND_PATH_RESP_PATH_0] = generate_path_resp_0,
[SEND_PATH_RESP_PATH_1] = generate_path_resp_1,
[SEND_PING] = generate_ping_frame,
};
@ -6078,6 +5959,7 @@ static void (*const send_funcs[N_SEND])(
|SF_SEND_MAX_STREAMS_UNI|SF_SEND_MAX_STREAMS_BIDI\
|SF_SEND_PATH_CHAL_PATH_0|SF_SEND_PATH_CHAL_PATH_1\
|SF_SEND_PATH_RESP_PATH_0|SF_SEND_PATH_RESP_PATH_1\
|SF_SEND_PING\
|SF_SEND_STOP_SENDING)
static enum tick_st
@ -6118,7 +6000,7 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
if (conn->ifc_flags & IFC_HAVE_SAVED_ACK)
{
(void) /* If there is an error, we'll fail shortly */
process_saved_ack(conn, 0, now);
process_ack(conn, &conn->ifc_ack, conn->ifc_saved_ack_received, now);
conn->ifc_flags &= ~IFC_HAVE_SAVED_ACK;
}
@ -6290,12 +6172,9 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
if (conn->ifc_send_flags & SF_SEND_PING)
{
RETURN_IF_OUT_OF_PACKETS();
if (0 == generate_ping_frame(conn))
{
conn->ifc_send_flags &= ~SF_SEND_PING;
CLOSE_IF_NECESSARY();
assert(lsquic_send_ctl_n_scheduled(&conn->ifc_send_ctl) != 0);
}
generate_ping_frame(conn, now);
CLOSE_IF_NECESSARY();
assert(lsquic_send_ctl_n_scheduled(&conn->ifc_send_ctl) != 0);
}
else
{

View File

@ -2199,7 +2199,12 @@ gen_iasn_key (X509 *cert, unsigned char *const out, size_t const sz)
}
static enum { GET_SNI_OK, GET_SNI_ERR, GET_SNI_DELAYED, }
static enum {
GET_SNI_OK,
GET_SNI_ERR,
}
get_sni_SSL_CTX(struct lsquic_enc_session *enc_session, lsquic_lookup_cert_f cb,
void *cb_ctx, const struct sockaddr *local)
{
@ -2315,10 +2320,6 @@ handle_chlo_frames_data(const uint8_t *data, int len,
lsquic_str_setto(&enc_session->chlo, (const char *)data, len);
switch (get_sni_SSL_CTX(enc_session, cb, cb_ctx, local))
{
case GET_SNI_DELAYED:
ESHIST_APPEND(enc_session, ESHE_SNI_DELAYED);
LSQ_DEBUG("%s: sni delayed", __func__);
return HS_DELAYED;
case GET_SNI_ERR:
ESHIST_APPEND(enc_session, ESHE_SNI_FAIL);
LSQ_DEBUG("handle_chlo_frames_data got data format error 2.");
@ -2690,8 +2691,6 @@ he2str (enum handshake_error he)
case HS_SHLO: return "HS_SHLO";
case HS_1RTT: return "HS_1RTT";
case HS_SREJ: return "HS_SREJ";
case HS_DELAYED: return "HS_DELAYED";
case HS_PK_OFFLOAD: return "HS_PK_OFFLOAD";
default:
assert(0); return "<unknown enum value>";
}
@ -3289,11 +3288,6 @@ lsquic_enc_session_handle_chlo(enc_session_t *enc_session_p,
LSQ_DEBUG("lsquic_enc_session_handle_chlo call gen_rej1_data");
len = gen_rej1_data(enc_session, out, *out_len, peer, t);
if (len == -2)
{
rtt = HS_PK_OFFLOAD;
goto end;
}
if (len < 0)
{
rtt = HS_ERROR;
@ -3514,17 +3508,6 @@ lsquic_enc_session_get_ua (enc_session_t *enc_session_p)
}
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
static void
set_handshake_completed (struct lsquic_enc_session *enc_session)
{
enc_session->hsk_state = HSK_COMPLETED;
}
#endif
#ifndef NDEBUG
static uint8_t
lsquic_enc_session_have_key (enc_session_t *enc_session_p)
@ -3773,34 +3756,6 @@ maybe_dispatch_zero_rtt (enc_session_t *enc_session_p,
}
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
static ssize_t
just_copy_packet (const struct lsquic_conn *lconn,
const struct lsquic_packet_out *packet_out, unsigned char *buf,
size_t bufsz)
{
int header_sz;
header_sz = lconn->cn_pf->pf_gen_reg_pkt_header(lconn, packet_out,
buf, bufsz);
if (header_sz < 0)
return -1;
if ((unsigned) header_sz + packet_out->po_data_sz > bufsz)
{
LSQ_WARN("packet too large (%d bytes) to encrypt",
header_sz + packet_out->po_data_sz);
return -1;
}
memcpy(buf + header_sz, packet_out->po_data, packet_out->po_data_sz);
return header_sz + packet_out->po_data_sz;
}
#endif
static enum enc_packout
gquic_encrypt_packet (enc_session_t *enc_session_p,
const struct lsquic_engine_public *enpub,
@ -3809,7 +3764,6 @@ gquic_encrypt_packet (enc_session_t *enc_session_p,
struct lsquic_enc_session *const enc_session = enc_session_p;
ssize_t enc_sz;
size_t bufsz;
unsigned sent_sz;
unsigned char *buf;
int ipv6;
@ -3828,20 +3782,8 @@ gquic_encrypt_packet (enc_session_t *enc_session_p,
return ENCPA_NOMEM;
}
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
if (getenv("LSQUIC_DISABLE_HANDSHAKE"))
{
enc_sz = just_copy_packet(lconn, packet_out, buf, bufsz);
sent_sz = enc_sz + GQUIC_PACKET_HASH_SZ;
}
else
#endif
{
enc_sz = gquic_really_encrypt_packet(enc_session,
lconn, packet_out, buf, bufsz);
sent_sz = enc_sz;
}
enc_sz = gquic_really_encrypt_packet(enc_session,
lconn, packet_out, buf, bufsz);
if (enc_sz < 0)
{
enpub->enp_pmi->pmi_return(enpub->enp_pmi_ctx,
@ -3851,7 +3793,7 @@ gquic_encrypt_packet (enc_session_t *enc_session_p,
packet_out->po_enc_data = buf;
packet_out->po_enc_data_sz = enc_sz;
packet_out->po_sent_sz = sent_sz;
packet_out->po_sent_sz = enc_sz;
packet_out->po_flags &= ~PO_IPv6;
packet_out->po_flags |= PO_ENCRYPTED|PO_SENT_SZ|(ipv6 << POIPv6_SHIFT);
@ -4349,9 +4291,6 @@ struct enc_session_funcs_gquic lsquic_enc_session_gquic_gquic_1 =
.esf_get_enc_key_nonce_f = lsquic_enc_session_get_enc_key_nonce_f,
.esf_get_dec_key_nonce_f = lsquic_enc_session_get_dec_key_nonce_f,
#endif /* !defined(NDEBUG) */
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
.esf_set_handshake_completed = set_handshake_completed,
#endif
.esf_create_client = lsquic_enc_session_create_client,
.esf_gen_chlo = lsquic_enc_session_gen_chlo,
.esf_handle_chlo_reply = lsquic_enc_session_handle_chlo_reply,

View File

@ -104,7 +104,8 @@ lsquic_is_valid_hs_packet (struct lsquic_engine *engine,
case 0x80|0x00|0x20|0x10|0x08:
case 0x80|0x40|0x20|0x10|0x00:
case 0x80|0x00|0x20|0x10|0x00:
is_valid = lsquic_is_valid_iquic_hs_packet(buf, bufsz, &tag);
is_valid = bufsz >= IQUIC_MIN_INIT_PACKET_SZ
&& lsquic_is_valid_iquic_hs_packet(buf, bufsz, &tag);
break;
/* 1X00 XGGG: ID-22 long header */
case 0x80|0x40|0x00|0x00|0x08:
@ -121,8 +122,8 @@ lsquic_is_valid_hs_packet (struct lsquic_engine *engine,
case 0x80|0x00|0x20|0x00|0x08:
case 0x80|0x40|0x20|0x00|0x00:
case 0x80|0x00|0x20|0x00|0x00:
is_valid = lsquic_is_valid_ietf_v1_or_Q046plus_hs_packet(buf, bufsz,
&tag);
is_valid = bufsz >= IQUIC_MIN_INIT_PACKET_SZ
&& lsquic_is_valid_ietf_v1_or_Q046plus_hs_packet(buf, bufsz, &tag);
break;
/* 01XX XGGG: ID-22 short header */
case 0x00|0x40|0x00|0x00|0x00:

View File

@ -35,6 +35,7 @@
#include "lsquic_malo.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_gquic.h"
#include "lsquic_packet_ietf.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
#include "lsquic_util.h"
@ -60,9 +61,6 @@
static const struct conn_iface mini_conn_iface_standard;
static const struct conn_iface mini_conn_iface_standard_Q050;
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
static const struct conn_iface mini_conn_iface_disable_handshake;
#endif
#if LSQUIC_KEEP_MINICONN_HISTORY
@ -156,18 +154,29 @@ mini_destroy_packet (struct mini_conn *mc, struct lsquic_packet_out *packet_out)
static int
packet_in_is_ok (const struct lsquic_packet_in *packet_in)
packet_in_is_ok (enum lsquic_version version,
const struct lsquic_packet_in *packet_in)
{
size_t min_size;
if (packet_in->pi_data_sz > GQUIC_MAX_PACKET_SZ)
{
LSQ_LOG1(LSQ_LOG_DEBUG, "incoming packet too large: %hu bytes",
packet_in->pi_data_sz);
return 0;
}
if (packet_in->pi_data_sz < 200) /* TODO: 64 packets now */
{ /* mini connection is limited to 32 packets, yet it must send out REJ
* and SHLO, which are about 4 KB combined.
if ((1 << version) & LSQUIC_GQUIC_HEADER_VERSIONS)
/* This is a very lax number, it allows the server to send
* 64 * 200 = 12KB of output (REJ and SHLO).
*/
min_size = 200;
else
/* Chrome enforces 1200-byte minimum initial packet limit */
min_size = IQUIC_MIN_INIT_PACKET_SZ;
if (packet_in->pi_data_sz < min_size)
{
LSQ_LOG1(LSQ_LOG_DEBUG, "incoming packet too small: %hu bytes",
packet_in->pi_data_sz);
return 0;
@ -184,23 +193,16 @@ mini_conn_new (struct lsquic_engine_public *enp,
struct mini_conn *mc;
const struct conn_iface *conn_iface;
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
if (getenv("LSQUIC_DISABLE_HANDSHAKE"))
conn_iface = &mini_conn_iface_disable_handshake;
else
#endif
if (!packet_in_is_ok(version, packet_in))
return NULL;
switch (version)
{
if (!packet_in_is_ok(packet_in))
return NULL;
switch (version)
{
case LSQVER_050:
conn_iface = &mini_conn_iface_standard_Q050;
break;
default:
conn_iface = &mini_conn_iface_standard;
break;
}
case LSQVER_050:
conn_iface = &mini_conn_iface_standard_Q050;
break;
default:
conn_iface = &mini_conn_iface_standard;
break;
}
mc = lsquic_malo_get(enp->enp_mm.malo.mini_conn);
@ -217,12 +219,7 @@ mini_conn_new (struct lsquic_engine_public *enp,
TAILQ_INIT(&mc->mc_packets_out);
mc->mc_enpub = enp;
mc->mc_created = packet_in->pi_received;
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
if (conn_iface == &mini_conn_iface_disable_handshake)
mc->mc_path.np_pack_size = 1370;
else
#endif
mc->mc_path.np_pack_size = packet_in->pi_data_sz;
mc->mc_path.np_pack_size = packet_in->pi_data_sz;
mc->mc_conn.cn_cces = mc->mc_cces;
mc->mc_conn.cn_cces_mask = 1;
mc->mc_conn.cn_n_cces = sizeof(mc->mc_cces) / sizeof(mc->mc_cces[0]);
@ -675,17 +672,7 @@ record_largest_recv (struct mini_conn *mc, lsquic_time_t t)
static enum dec_packin
conn_decrypt_packet (struct mini_conn *conn, lsquic_packet_in_t *packet_in)
{
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
if (getenv("LSQUIC_DISABLE_HANDSHAKE"))
{
(void) lsquic_conn_copy_and_release_pi_data(&conn->mc_conn,
conn->mc_enpub, packet_in);
packet_in->pi_flags |= PI_DECRYPTED;
return 0;
}
else
#endif
return conn->mc_conn.cn_esf_c->esf_decrypt_packet(
return conn->mc_conn.cn_esf_c->esf_decrypt_packet(
conn->mc_conn.cn_enc_session, conn->mc_enpub,
&conn->mc_conn, packet_in);
}
@ -2059,44 +2046,6 @@ mini_conn_ci_destroy (struct lsquic_conn *lconn)
}
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
static void
mini_conn_ci_packet_in_disable_handshake (struct lsquic_conn *lconn,
struct lsquic_packet_in *packet_in)
{
struct mini_conn *mc = (struct mini_conn *) lconn;
if (0 == (mc->mc_flags & MC_ERROR))
{
if (packet_in->pi_packno > MINICONN_MAX_PACKETS)
mc->mc_flags |= MC_ERROR;
else if (!(mc->mc_received_packnos &
MCONN_PACKET_MASK(packet_in->pi_packno)))
{
lsquic_packet_in_upref(packet_in);
TAILQ_INSERT_TAIL(&mc->mc_packets_in, packet_in, pi_next);
mc->mc_received_packnos |= MCONN_PACKET_MASK(packet_in->pi_packno);
}
}
}
static enum tick_st
mini_conn_ci_tick_disable_handshake (struct lsquic_conn *lconn,
lsquic_time_t now)
{
struct mini_conn *mc = (struct mini_conn *) lconn;
LSQ_WARN("handshake disabled, promote me now");
lconn->cn_flags |= LSCONN_HANDSHAKE_DONE;
mc->mc_conn.cn_enc_session =
mc->mc_conn.cn_esf.g->esf_create_server(lconn->cn_cid, mc->mc_enpub, 0);
mc->mc_conn.cn_esf.g->esf_set_handshake_completed(mc->mc_conn.cn_enc_session);
return TICK_PROMOTE;
}
#endif
static struct lsquic_engine *
mini_conn_ci_get_engine (struct lsquic_conn *lconn)
{
@ -2263,25 +2212,6 @@ static const struct conn_iface mini_conn_iface_standard_Q050 = {
};
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
static const struct conn_iface mini_conn_iface_disable_handshake = {
.ci_abort_error = mini_conn_ci_abort_error,
.ci_client_call_on_new = mini_conn_ci_client_call_on_new,
.ci_destroy = mini_conn_ci_destroy,
.ci_get_engine = mini_conn_ci_get_engine,
.ci_hsk_done = mini_conn_ci_hsk_done,
.ci_internal_error = mini_conn_ci_internal_error,
.ci_is_tickable = mini_conn_ci_is_tickable,
.ci_next_packet_to_send = mini_conn_ci_next_packet_to_send,
.ci_next_tick_time = mini_conn_ci_next_tick_time,
.ci_packet_in = mini_conn_ci_packet_in_disable_handshake,
.ci_packet_not_sent = mini_conn_ci_packet_not_sent,
.ci_packet_sent = mini_conn_ci_packet_sent,
.ci_tick = mini_conn_ci_tick_disable_handshake,
.ci_tls_alert = mini_conn_ci_tls_alert,
};
#endif
typedef char largest_recv_holds_at_least_16_seconds[
((1 << (sizeof(((struct mini_conn *) 0)->mc_largest_recv) * 8)) / 1000000
>= 16) - 1];

View File

@ -393,6 +393,13 @@ static const struct crypto_stream_if crypto_stream_if =
static int
is_first_packet_ok (const struct lsquic_packet_in *packet_in)
{
if (packet_in->pi_data_sz < IQUIC_MIN_INIT_PACKET_SZ)
{
/* [draft-ietf-quic-transport-24] Section 14 */
LSQ_LOG1(LSQ_LOG_DEBUG, "incoming packet too small: %hu bytes",
packet_in->pi_data_sz);
return 0;
}
/* TODO: Move decryption of the first packet into this function? */
return 1; /* TODO */
}

View File

@ -88,6 +88,7 @@ lsquic_mm_init (struct lsquic_mm *mm)
mm->malo.dcid_elem = lsquic_malo_create(sizeof(struct dcid_elem));
mm->malo.stream_hq_frame
= lsquic_malo_create(sizeof(struct stream_hq_frame));
mm->ack_str = malloc(MAX_ACKI_STR_SZ);
#if LSQUIC_USE_POOLS
TAILQ_INIT(&mm->free_packets_in);
for (i = 0; i < MM_N_OUT_BUCKETS; ++i)
@ -99,7 +100,8 @@ lsquic_mm_init (struct lsquic_mm *mm)
#endif
if (mm->acki && mm->malo.stream_frame && mm->malo.stream_rec_arr
&& mm->malo.mini_conn && mm->malo.mini_conn_ietf && mm->malo.packet_in
&& mm->malo.stream_hq_frame)
&& mm->malo.packet_out && mm->malo.dcid_elem
&& mm->malo.stream_hq_frame && mm->ack_str)
{
return 0;
}
@ -128,6 +130,7 @@ lsquic_mm_cleanup (struct lsquic_mm *mm)
lsquic_malo_destroy(mm->malo.stream_rec_arr);
lsquic_malo_destroy(mm->malo.mini_conn);
lsquic_malo_destroy(mm->malo.mini_conn_ietf);
free(mm->ack_str);
#if LSQUIC_USE_POOLS
for (i = 0; i < MM_N_OUT_BUCKETS; ++i)

View File

@ -48,6 +48,7 @@ struct lsquic_mm {
SLIST_HEAD(, packet_in_buf) packet_in_bufs[MM_N_IN_BUCKETS];
SLIST_HEAD(, four_k_page) four_k_pages;
SLIST_HEAD(, sixteen_k_page) sixteen_k_pages;
char *ack_str;
};
int

View File

@ -20,4 +20,7 @@
*/
#define MIN_INITIAL_DCID_LEN 8
/* [draft-ietf-quic-transport-24] Section 8.1 */
#define IQUIC_MIN_INIT_PACKET_SZ 1200
#endif

View File

@ -110,7 +110,7 @@ typedef struct lsquic_packet_out
PO_PNS_APP = (1 <<23), /* packet number space. */
PO_RETRY = (1 <<24), /* Retry packet */
PO_RETX = (1 <<25), /* Retransmitted packet: don't append to it */
PO_UNUSED26 = (1 <<26), /* Unused */
PO_POISON = (1 <<26), /* Used to detect opt-ACK attack */
PO_LOSS_REC = (1 <<27), /* This structure is a loss record */
/* Only one of PO_SCHED, PO_UNACKED, or PO_LOST can be set. If pressed
* for room in the enum, we can switch to using two bits to represent

View File

@ -15,8 +15,10 @@ struct packet_interval {
struct lsquic_packno_range range;
};
TAILQ_HEAD(pinhead, packet_interval);
struct packints {
TAILQ_HEAD(, packet_interval) pk_intervals;
struct pinhead pk_intervals;
struct packet_interval *pk_cur;
};

View File

@ -17,7 +17,6 @@ enum packet_out_flags;
enum lsquic_version;
enum stream_dir;
#define LSQUIC_PARSE_ACK_TIMESTAMPS 0
struct ack_info
{
@ -28,31 +27,11 @@ struct ack_info
* ran out of elements in `ranges'.
*/
} flags;
unsigned n_timestamps; /* 0 to 255 */
unsigned n_ranges; /* This is at least 1 */
/* Largest acked is ack_info.ranges[0].high */
lsquic_time_t lack_delta;
uint64_t ecn_counts[4];
struct lsquic_packno_range ranges[256];
#if LSQUIC_PARSE_ACK_TIMESTAMPS
struct {
/* Currently we just read these timestamps in (assuming it is
* compiled in, of course), but do not do anything with them.
* When we do, the representation of these fields should be
* switched to whatever is most appropriate/efficient.
*/
unsigned char packet_delta;
uint64_t delta_usec;
} timestamps[255];
#endif
};
struct short_ack_info
{
unsigned sai_n_timestamps;
lsquic_time_t sai_lack_delta;
struct lsquic_packno_range sai_range;
};
#define largest_acked(acki) (+(acki)->ranges[0].high)
@ -394,8 +373,16 @@ lsquic_gquic_po_header_sz (enum packet_out_flags flags);
*/
#define twobit_to_1248(bits) (1 << (bits))
char *
acki2str (const struct ack_info *acki, size_t *sz);
#define ECN_COUNTS_STR " ECT(0): 01234567879012345678790;" \
" ECT(1): 01234567879012345678790;" \
" CE: 01234567879012345678790"
#define RANGES_TRUNCATED_STR " ranges truncated! "
#define MAX_ACKI_STR_SZ (256 * (3 /* [-] */ + 20 /* ~0ULL */ * 2) \
+ sizeof(ECN_COUNTS_STR) + sizeof(RANGES_TRUNCATED_STR))
void
lsquic_acki2str (const struct ack_info *acki, char *, size_t);
void
lsquic_turn_on_fin_Q035_thru_Q046 (unsigned char *);
@ -407,4 +394,7 @@ lsquic_gquic_calc_packno_bits (lsquic_packno_t packno,
unsigned
lsquic_gquic_packno_bits2len (enum packno_bits);
int
lsquic_merge_acks (struct ack_info *dst, const struct ack_info *src);
#endif

View File

@ -375,6 +375,7 @@ parse_ack_frame_without_blocks (const unsigned char *buf, size_t buf_len,
const unsigned char type = buf[0];
const unsigned char *p = buf + 1;
const unsigned char *const pend = buf + buf_len;
unsigned char n_timestamps;
const int ack_block_len = twobit_to_1246(type & 3); /* mm */
const int largest_obs_len = twobit_to_1246((type >> 2) & 3); /* ll */
@ -393,12 +394,12 @@ parse_ack_frame_without_blocks (const unsigned char *buf, size_t buf_len,
ack->n_ranges = 1;
ack->n_timestamps = *p;
n_timestamps = *p;
++p;
if (ack->n_timestamps)
if (n_timestamps)
{
unsigned timestamps_size = 5 + 3 * (ack->n_timestamps - 1);
unsigned timestamps_size = 5 + 3 * (n_timestamps - 1);
CHECK_SPACE(timestamps_size, p, pend);
p += timestamps_size;
}
@ -419,6 +420,7 @@ parse_ack_frame_with_blocks (const unsigned char *buf, size_t buf_len,
const unsigned char type = buf[0];
const unsigned char *p = buf + 1;
const unsigned char *const pend = buf + buf_len;
unsigned char n_timestamps;
assert((type & 0xC0) == 0x40); /* We're passed correct frame type */
@ -461,31 +463,14 @@ parse_ack_frame_with_blocks (const unsigned char *buf, size_t buf_len,
}
ack->n_ranges = n;
ack->n_timestamps = *p;
n_timestamps = *p;
++p;
if (ack->n_timestamps)
if (n_timestamps)
{
#if LSQUIC_PARSE_ACK_TIMESTAMPS
CHECK_SPACE(5, p , pend);
ack->timestamps[0].packet_delta = *p++;
memcpy(&ack->timestamps[0].delta_usec, p, 4);
p += 4;
unsigned i;
for (i = 1; i < ack->n_timestamps; ++i)
{
CHECK_SPACE(3, p , pend);
ack->timestamps[i].packet_delta = *p++;
uint64_t delta_time = read_float_time16(p);
p += 2;
ack->timestamps[i].delta_usec =
ack->timestamps[i - 1].delta_usec + delta_time;
}
#else
unsigned timestamps_size = 5 + 3 * (ack->n_timestamps - 1);
unsigned timestamps_size = 5 + 3 * (n_timestamps - 1);
CHECK_SPACE(timestamps_size, p, pend);
p += timestamps_size;
#endif
}
assert(p <= pend);

View File

@ -508,29 +508,12 @@ static const char *const ecn2str[4] =
};
#define ECN_COUNTS " ECT(0): 01234567879012345678790;" \
" ECT(1): 01234567879012345678790;" \
" CE: 01234567879012345678790"
#define RANGES_TRUNCATED " ranges truncated! "
char *
acki2str (const struct ack_info *acki, size_t *sz)
void
lsquic_acki2str (const struct ack_info *acki, char *buf, size_t bufsz)
{
size_t off, bufsz, nw;
size_t off, nw;
enum ecn ecn;
unsigned n;
char *buf;
bufsz = acki->n_ranges * (3 /* [-] */ + 20 /* ~0ULL */ * 2)
+ (acki->flags & AI_ECN ? sizeof(ECN_COUNTS) : 0)
+ (acki->flags & AI_TRUNCATED ? sizeof(RANGES_TRUNCATED) : 0);
buf = malloc(bufsz);
if (!buf)
{
LSQ_WARN("%s: malloc(%zd) failure: %s", __func__, bufsz,
strerror(errno));
return NULL;
}
off = 0;
for (n = 0; n < acki->n_ranges; ++n)
@ -538,18 +521,15 @@ acki2str (const struct ack_info *acki, size_t *sz)
nw = snprintf(buf + off, bufsz - off, "[%"PRIu64"-%"PRIu64"]",
acki->ranges[n].high, acki->ranges[n].low);
if (nw > bufsz - off)
break;
return;
off += nw;
}
if (acki->flags & AI_TRUNCATED)
{
nw = snprintf(buf + off, bufsz - off, RANGES_TRUNCATED);
nw = snprintf(buf + off, bufsz - off, RANGES_TRUNCATED_STR);
if (nw > bufsz - off)
{
nw = bufsz - off;
goto end;
}
return;
off += nw;
}
@ -560,14 +540,10 @@ acki2str (const struct ack_info *acki, size_t *sz)
nw = snprintf(buf + off, bufsz - off, " %s: %"PRIu64"%.*s",
ecn2str[ecn], acki->ecn_counts[ecn], ecn < 3, ";");
if (nw > bufsz - off)
break;
return;
off += nw;
}
}
end:
*sz = off;
return buf;
}
@ -654,3 +630,68 @@ lsquic_gquic_calc_packno_bits (lsquic_packno_t packno,
return bits;
}
/* `dst' serves both as source and destination. `src' is the new frame */
int
lsquic_merge_acks (struct ack_info *dst, const struct ack_info *src)
{
const struct lsquic_packno_range *a, *a_end, *b, *b_end, **p;
struct lsquic_packno_range *out, *out_end;
unsigned i;
struct lsquic_packno_range out_ranges[256];
if (!(dst->n_ranges && src->n_ranges))
return -1;
a = dst->ranges;
a_end = a + dst->n_ranges;
b = src->ranges;
b_end = b + src->n_ranges;
out = out_ranges;
out_end = out + sizeof(out_ranges) / sizeof(out_ranges[0]);
if (a->high >= b->high)
*out = *a;
else
*out = *b;
while (1)
{
if (a < a_end && b < b_end)
{
if (a->high >= b->high)
p = &a;
else
p = &b;
}
else if (a < a_end)
p = &a;
else if (b < b_end)
p = &b;
else
{
++out;
break;
}
if ((*p)->high + 1 >= out->low)
out->low = (*p)->low;
else if (out + 1 < out_end)
*++out = **p;
else
return -1;
++*p;
}
if (src->flags & AI_ECN)
for (i = 0; i < sizeof(src->ecn_counts)
/ sizeof(src->ecn_counts[0]); ++i)
dst->ecn_counts[i] += src->ecn_counts[i];
dst->flags |= src->flags;
dst->lack_delta = src->lack_delta;
dst->n_ranges = out - out_ranges;
memcpy(dst->ranges, out_ranges, sizeof(out_ranges[0]) * dst->n_ranges);
return 0;
}

View File

@ -681,12 +681,6 @@ ietf_v1_parse_ack_frame (const unsigned char *const buf, size_t buf_len,
ack->flags |= AI_ECN;
}
#if LSQUIC_PARSE_ACK_TIMESTAMPS
#error Pasing ACK timestamps not supported
#else
ack->n_timestamps = 0;
#endif
return p - buf;
}

View File

@ -30,6 +30,14 @@ lsquic_rechist_init (struct lsquic_rechist *rechist,
rechist->rh_cutoff = ietf ? 0 : 1;
lsquic_packints_init(&rechist->rh_pints);
LSQ_DEBUG("instantiated received packet history");
#if LSQUIC_ACK_ATTACK
const char *s = getenv("LSQUIC_ACK_ATTACK");
if (s && atoi(s))
{
LSQ_NOTICE("ACK attack mode ON!");
rechist->rh_flags |= RH_ACK_ATTACK;
}
#endif
}
@ -144,6 +152,23 @@ lsquic_rechist_largest_recv (const lsquic_rechist_t *rechist)
const struct lsquic_packno_range *
lsquic_rechist_first (lsquic_rechist_t *rechist)
{
#if LSQUIC_ACK_ATTACK
if (rechist->rh_flags & RH_ACK_ATTACK)
{
/* This only performs the lazy variant of the attack. An aggressive
* attack would increase the value of high number.
*/
const struct lsquic_packno_range *range;
range = lsquic_packints_first(&rechist->rh_pints);
if (!range)
return NULL;
rechist->rh_first = *range;
range = &TAILQ_LAST(&rechist->rh_pints.pk_intervals, pinhead)->range;
rechist->rh_first.low = range->low;
return &rechist->rh_first;
}
#endif
return lsquic_packints_first(&rechist->rh_pints);
}
@ -151,6 +176,10 @@ lsquic_rechist_first (lsquic_rechist_t *rechist)
const struct lsquic_packno_range *
lsquic_rechist_next (lsquic_rechist_t *rechist)
{
#if LSQUIC_ACK_ATTACK
if (rechist->rh_flags & RH_ACK_ATTACK)
return NULL;
#endif
return lsquic_packints_next(&rechist->rh_pints);
}

View File

@ -23,7 +23,13 @@ struct lsquic_rechist {
unsigned rh_n_packets;
enum {
RH_CUTOFF_SET = (1 << 0),
#if LSQUIC_ACK_ATTACK
RH_ACK_ATTACK = (1 << 1),
#endif
} rh_flags;
#if LSQUIC_ACK_ATTACK
struct lsquic_packno_range rh_first;
#endif
};
typedef struct lsquic_rechist lsquic_rechist_t;

View File

@ -10,6 +10,8 @@
#include <string.h>
#include <sys/queue.h>
#include <openssl/rand.h>
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic.h"
@ -110,6 +112,8 @@ send_ctl_retx_bytes_out (const struct lsquic_send_ctl *ctl);
static unsigned
send_ctl_all_bytes_out (const struct lsquic_send_ctl *ctl);
static void
send_ctl_reschedule_poison (struct lsquic_send_ctl *ctl);
#ifdef NDEBUG
static
@ -141,7 +145,7 @@ lsquic_send_ctl_have_unacked_stream_frames (const lsquic_send_ctl_t *ctl)
const lsquic_packet_out_t *packet_out;
TAILQ_FOREACH(packet_out, &ctl->sc_unacked_packets[PNS_APP], po_next)
if (0 == (packet_out->po_flags & PO_LOSS_REC)
if (0 == (packet_out->po_flags & (PO_LOSS_REC|PO_POISON))
&& (packet_out->po_frame_types &
((1 << QUIC_FRAME_STREAM) | (1 << QUIC_FRAME_RST_STREAM))))
return 1;
@ -157,7 +161,7 @@ send_ctl_first_unacked_retx_packet (const struct lsquic_send_ctl *ctl,
lsquic_packet_out_t *packet_out;
TAILQ_FOREACH(packet_out, &ctl->sc_unacked_packets[pns], po_next)
if (0 == (packet_out->po_flags & PO_LOSS_REC)
if (0 == (packet_out->po_flags & (PO_LOSS_REC|PO_POISON))
&& (packet_out->po_frame_types & ctl->sc_retx_frames))
return packet_out;
@ -172,7 +176,7 @@ send_ctl_last_unacked_retx_packet (const struct lsquic_send_ctl *ctl,
lsquic_packet_out_t *packet_out;
TAILQ_FOREACH_REVERSE(packet_out, &ctl->sc_unacked_packets[pns],
lsquic_packets_tailq, po_next)
if (0 == (packet_out->po_flags & PO_LOSS_REC)
if (0 == (packet_out->po_flags & (PO_LOSS_REC|PO_POISON))
&& (packet_out->po_frame_types & ctl->sc_retx_frames))
return packet_out;
return NULL;
@ -354,6 +358,7 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset,
== LSCONN_IETF)
ctl->sc_flags |= SC_SANITY_CHECK;
#endif
ctl->sc_gap = UINT64_MAX - 1 /* Can't have +1 == 0 */;
}
@ -494,7 +499,7 @@ send_ctl_unacked_append (struct lsquic_send_ctl *ctl,
enum packnum_space pns;
pns = lsquic_packet_out_pns(packet_out);
assert(0 == (packet_out->po_flags & PO_LOSS_REC));
assert(0 == (packet_out->po_flags & (PO_LOSS_REC|PO_POISON)));
TAILQ_INSERT_TAIL(&ctl->sc_unacked_packets[pns], packet_out, po_next);
packet_out->po_flags |= PO_UNACKED;
ctl->sc_bytes_unacked_all += packet_out_sent_sz(packet_out);
@ -569,6 +574,62 @@ send_ctl_sched_remove (struct lsquic_send_ctl *ctl,
}
/* Poisoned packets are used to detect optimistic ACK attacks. We only
* use a single poisoned packet at a time.
*/
static int
send_ctl_add_poison (struct lsquic_send_ctl *ctl)
{
struct lsquic_packet_out *poison;
poison = lsquic_malo_get(ctl->sc_conn_pub->packet_out_malo);
if (!poison)
return -1;
memset(poison, 0, sizeof(*poison));
poison->po_flags = PO_UNACKED|PO_POISON;
poison->po_packno = ctl->sc_gap;
poison->po_loss_chain = poison; /* Won't be used, but just in case */
TAILQ_INSERT_TAIL(&ctl->sc_unacked_packets[PNS_APP], poison, po_next);
LSQ_DEBUG("insert poisoned packet %"PRIu64, poison->po_packno);
ctl->sc_flags |= SC_POISON;
return 0;
}
static void
send_ctl_reschedule_poison (struct lsquic_send_ctl *ctl)
{
struct lsquic_packet_out *poison;
enum lsq_log_level log_level;
lsquic_time_t approx_now;
TAILQ_FOREACH(poison, &ctl->sc_unacked_packets[PNS_APP], po_next)
if (poison->po_flags & PO_POISON)
{
LSQ_DEBUG("remove poisoned packet %"PRIu64, poison->po_packno);
TAILQ_REMOVE(&ctl->sc_unacked_packets[PNS_APP], poison, po_next);
lsquic_malo_put(poison);
lsquic_send_ctl_begin_optack_detection(ctl);
ctl->sc_flags &= ~SC_POISON;
return;
}
approx_now = ctl->sc_last_sent_time;
if (0 == ctl->sc_enpub->enp_last_warning[WT_NO_POISON]
|| ctl->sc_enpub->enp_last_warning[WT_NO_POISON]
+ WARNING_INTERVAL < approx_now)
{
ctl->sc_enpub->enp_last_warning[WT_NO_POISON] = approx_now;
log_level = LSQ_LOG_WARN;
}
else
log_level = LSQ_LOG_DEBUG;
LSQ_LOG(log_level, "odd: poisoned packet %"PRIu64" not found during "
"reschedule, flag: %d", ctl->sc_gap, !!(ctl->sc_flags & SC_POISON));
}
int
lsquic_send_ctl_sent_packet (lsquic_send_ctl_t *ctl,
struct lsquic_packet_out *packet_out)
@ -579,6 +640,13 @@ lsquic_send_ctl_sent_packet (lsquic_send_ctl_t *ctl,
assert(!(packet_out->po_flags & PO_ENCRYPTED));
ctl->sc_last_sent_time = packet_out->po_sent;
pns = lsquic_packet_out_pns(packet_out);
if (packet_out->po_packno == ctl->sc_gap + 1 && pns == PNS_APP)
{
assert(!(ctl->sc_flags & SC_POISON));
lsquic_senhist_add(&ctl->sc_senhist, ctl->sc_gap);
if (0 != send_ctl_add_poison(ctl))
return -1;
}
LSQ_DEBUG("packet %"PRIu64" has been sent (frame types: %s)",
packet_out->po_packno, lsquic_frame_types_to_str(frames,
sizeof(frames), packet_out->po_frame_types));
@ -640,7 +708,7 @@ static void
send_ctl_destroy_packet (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
{
if (0 == (packet_out->po_flags & PO_LOSS_REC))
if (0 == (packet_out->po_flags & (PO_LOSS_REC|PO_POISON)))
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub,
packet_out->po_path->np_peer_ctx);
else
@ -823,7 +891,7 @@ largest_retx_packet_number (const struct lsquic_send_ctl *ctl,
TAILQ_FOREACH_REVERSE(packet_out, &ctl->sc_unacked_packets[pns],
lsquic_packets_tailq, po_next)
{
if (0 == (packet_out->po_flags & PO_LOSS_REC)
if (0 == (packet_out->po_flags & (PO_LOSS_REC|PO_POISON))
&& (packet_out->po_frame_types & ctl->sc_retx_frames))
return packet_out->po_packno;
}
@ -848,7 +916,7 @@ send_ctl_detect_losses (struct lsquic_send_ctl *ctl, enum packnum_space pns,
{
next = TAILQ_NEXT(packet_out, po_next);
if (packet_out->po_flags & PO_LOSS_REC)
if (packet_out->po_flags & (PO_LOSS_REC|PO_POISON))
continue;
if (packet_out->po_packno + N_NACKS_BEFORE_RETX <
@ -1012,7 +1080,7 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
ecn_total_acked += lsquic_packet_out_ecn(packet_out) != ECN_NOT_ECT;
ecn_ce_cnt += lsquic_packet_out_ecn(packet_out) == ECN_CE;
one_rtt_cnt += lsquic_packet_out_enc_level(packet_out) == ENC_LEV_FORW;
if (0 == (packet_out->po_flags & PO_LOSS_REC))
if (0 == (packet_out->po_flags & (PO_LOSS_REC|PO_POISON)))
{
packet_sz = packet_out_sent_sz(packet_out);
send_ctl_unacked_remove(ctl, packet_out, packet_sz);
@ -1020,7 +1088,7 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
LSQ_DEBUG("acking via regular record %"PRIu64,
packet_out->po_packno);
}
else
else if (packet_out->po_flags & PO_LOSS_REC)
{
packet_sz = packet_out->po_sent_sz;
TAILQ_REMOVE(&ctl->sc_unacked_packets[pns], packet_out,
@ -1033,6 +1101,12 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
packet_out->po_packno);
#endif
}
else
{
LSQ_WARN("poisoned packet %"PRIu64" acked",
packet_out->po_packno);
return -1;
}
ack2ed[!!(packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))]
= packet_out->po_ack2ed;
do_rtt |= packet_out->po_packno == largest_acked(acki);
@ -1114,6 +1188,8 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
lsquic_send_ctl_sanity_check(ctl);
if (ctl->sc_ci->cci_end_ack)
ctl->sc_ci->cci_end_ack(CGP(ctl), ctl->sc_bytes_unacked_all);
if (ctl->sc_gap < smallest_acked(acki))
send_ctl_reschedule_poison(ctl);
return 0;
no_unacked_packets:
@ -1191,7 +1267,13 @@ send_ctl_next_lost (lsquic_send_ctl_t *ctl)
static lsquic_packno_t
send_ctl_next_packno (lsquic_send_ctl_t *ctl)
{
return ++ctl->sc_cur_packno;
lsquic_packno_t packno;
packno = ++ctl->sc_cur_packno;
if (packno == ctl->sc_gap)
packno = ++ctl->sc_cur_packno;
return packno;
}
@ -1216,7 +1298,7 @@ lsquic_send_ctl_cleanup (lsquic_send_ctl_t *ctl)
TAILQ_REMOVE(&ctl->sc_unacked_packets[pns], packet_out, po_next);
packet_out->po_flags &= ~PO_UNACKED;
#ifndef NDEBUG
if (0 == (packet_out->po_flags & PO_LOSS_REC))
if (0 == (packet_out->po_flags & (PO_LOSS_REC|PO_POISON)))
{
ctl->sc_bytes_unacked_all -= packet_out_sent_sz(packet_out);
--ctl->sc_n_in_flight_all;
@ -1366,7 +1448,7 @@ send_ctl_expire (struct lsquic_send_ctl *ctl, enum packnum_space pns,
packet_out; packet_out = next)
{
next = TAILQ_NEXT(packet_out, po_next);
if (0 == (packet_out->po_flags & PO_LOSS_REC))
if (0 == (packet_out->po_flags & (PO_LOSS_REC|PO_POISON)))
n_resubmitted += send_ctl_handle_lost_packet(ctl, packet_out,
&next);
}
@ -1433,7 +1515,7 @@ lsquic_send_ctl_do_sanity_check (const struct lsquic_send_ctl *ctl)
prev_packno = packet_out->po_packno;
prev_packno_set = 1;
}
if (0 == (packet_out->po_flags & PO_LOSS_REC))
if (0 == (packet_out->po_flags & (PO_LOSS_REC|PO_POISON)))
{
bytes += packet_out_sent_sz(packet_out);
++count;
@ -1620,6 +1702,8 @@ lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl, size_t size)
packet_out->po_lflags &= ~POL_LOSS_BIT;
if (packet_out->po_header_type == HETY_NOT_SET)
{
if (ctl->sc_gap + 1 == packet_out->po_packno)
++ctl->sc_square_count;
if (ctl->sc_square_count++ & 128)
packet_out->po_lflags |= POL_SQUARE_BIT;
else
@ -2734,7 +2818,7 @@ lsquic_send_ctl_empty_pns (struct lsquic_send_ctl *ctl, enum packnum_space pns)
packet_out = next)
{
next = TAILQ_NEXT(packet_out, po_next);
if (packet_out->po_flags & PO_LOSS_REC)
if (packet_out->po_flags & (PO_LOSS_REC|PO_POISON))
TAILQ_REMOVE(&ctl->sc_unacked_packets[pns], packet_out, po_next);
else
{
@ -2849,3 +2933,13 @@ lsquic_send_ctl_cidlen_change (struct lsquic_send_ctl *ctl,
else
LSQ_DEBUG("no scheduled packets at the time of DCID change");
}
void
lsquic_send_ctl_begin_optack_detection (struct lsquic_send_ctl *ctl)
{
unsigned char buf[1];
RAND_bytes(buf, sizeof(buf));
ctl->sc_gap = ctl->sc_cur_packno + 1 + buf[0];
}

View File

@ -47,6 +47,7 @@ enum send_ctl_flags {
SC_QL_BITS = 1 << 14,
SC_SANITY_CHECK = 1 << 15,
SC_CIDLEN = 1 << 16, /* sc_cidlen is set */
SC_POISON = 1 << 17, /* poisoned packet exists */
};
typedef struct lsquic_send_ctl {
@ -127,6 +128,7 @@ typedef struct lsquic_send_ctl {
unsigned sc_retry_count;
unsigned sc_rt_count; /* Count round trips */
lsquic_packno_t sc_cur_rt_end;
lsquic_packno_t sc_gap;
unsigned sc_loss_count; /* Used to set loss bit */
unsigned sc_square_count;/* Used to set square bit */
signed char sc_cidlen; /* For debug purposes */
@ -372,4 +374,7 @@ void
lsquic_send_ctl_cidlen_change (struct lsquic_send_ctl *,
unsigned orig_cid_len, unsigned new_cid_len);
void
lsquic_send_ctl_begin_optack_detection (struct lsquic_send_ctl *);
#endif

View File

@ -2898,7 +2898,7 @@ stream_write_to_packet_crypto (struct frame_gen_ctx *fg_ctx, const size_t size)
if (stream->sm_bflags & SMBF_IETF)
pns = lsquic_enclev2pns[ crypto_level(stream) ];
else
pns = PNS_INIT;
pns = PNS_APP;
assert(size > 0);
crypto_header_sz = stream->sm_frame_header_sz(stream, size);

View File

@ -1385,6 +1385,7 @@ send_packets_using_sendmmsg (const struct lsquic_out_spec *specs,
enum ctl_what cw;
unsigned i;
int s, saved_errno;
uintptr_t ancil_key, prev_ancil_key;
struct mmsghdr mmsgs[1024];
union {
/* cmsg(3) recommends union for proper alignment */
@ -1404,6 +1405,7 @@ send_packets_using_sendmmsg (const struct lsquic_out_spec *specs,
struct cmsghdr cmsg;
} ancil [ sizeof(mmsgs) / sizeof(mmsgs[0]) ];
prev_ancil_key = 0;
for (i = 0; i < count && i < sizeof(mmsgs) / sizeof(mmsgs[0]); ++i)
{
mmsgs[i].msg_hdr.msg_name = (void *) specs[i].dest_sa;
@ -1414,20 +1416,51 @@ send_packets_using_sendmmsg (const struct lsquic_out_spec *specs,
mmsgs[i].msg_hdr.msg_iovlen = specs[i].iovlen;
mmsgs[i].msg_hdr.msg_flags = 0;
if ((sport->sp_flags & SPORT_SERVER) && specs[i].local_sa->sa_family)
{
cw = CW_SENDADDR;
else
cw = 0;
#if ECN_SUPPORTED
if (sport->sp_prog->prog_api.ea_settings->es_ecn && specs[i].ecn)
cw |= CW_ECN;
#endif
if (cw)
setup_control_msg(&mmsgs[i].msg_hdr, cw, &specs[i], ancil[i].buf,
sizeof(ancil[i].buf));
ancil_key = (uintptr_t) specs[i].local_sa;
assert(0 == (ancil_key & 3));
}
else
{
mmsgs[i].msg_hdr.msg_control = NULL;
cw = 0;
ancil_key = 0;
}
#if ECN_SUPPORTED
if (sport->sp_prog->prog_api.ea_settings->es_ecn && specs[i].ecn)
{
cw |= CW_ECN;
ancil_key |= specs[i].ecn;
}
#endif
if (cw && prev_ancil_key == ancil_key)
{
/* Reuse previous ancillary message */
assert(i > 0);
#ifndef WIN32
mmsgs[i].msg_hdr.msg_control = mmsgs[i - 1].msg_hdr.msg_control;
mmsgs[i].msg_hdr.msg_controllen = mmsgs[i - 1].msg_hdr.msg_controllen;
#else
mmsgs[i].msg_hdr.Control.buf = mmsgs[i - 1].msg_hdr.Control.buf;
mmsgs[i].msg_hdr.Control.len = mmsgs[i - 1].msg_hdr.Control.len;
#endif
}
else if (cw)
{
prev_ancil_key = ancil_key;
setup_control_msg(&mmsgs[i].msg_hdr, cw, &specs[i], ancil[i].buf,
sizeof(ancil[i].buf));
}
else
{
prev_ancil_key = 0;
#ifndef WIN32
mmsgs[i].msg_hdr.msg_control = NULL;
mmsgs[i].msg_hdr.msg_controllen = 0;
#else
mmsgs[i].msg_hdr.Control.buf = NULL;
mmsgs[i].msg_hdr.Control.len = 0;
#endif
}
}
@ -1510,6 +1543,7 @@ send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count)
];
struct cmsghdr cmsg;
} ancil;
uintptr_t ancil_key, prev_ancil_key;
if (0 == count)
return 0;
@ -1538,6 +1572,7 @@ send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count)
#endif
n = 0;
prev_ancil_key = 0;
do
{
sport = specs[n].peer_ctx;
@ -1563,17 +1598,36 @@ send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count)
msg.dwFlags = 0;
#endif
if ((sport->sp_flags & SPORT_SERVER) && specs[n].local_sa->sa_family)
{
cw = CW_SENDADDR;
else
cw = 0;
#if ECN_SUPPORTED
if (sport->sp_prog->prog_api.ea_settings->es_ecn && specs[n].ecn)
cw |= CW_ECN;
#endif
if (cw)
setup_control_msg(&msg, cw, &specs[n], ancil.buf, sizeof(ancil.buf));
ancil_key = (uintptr_t) specs[n].local_sa;
assert(0 == (ancil_key & 3));
}
else
{
cw = 0;
ancil_key = 0;
}
#if ECN_SUPPORTED
if (sport->sp_prog->prog_api.ea_settings->es_ecn && specs[n].ecn)
{
cw |= CW_ECN;
ancil_key |= specs[n].ecn;
}
#endif
if (cw && prev_ancil_key == ancil_key)
{
/* Reuse previous ancillary message */
;
}
else if (cw)
{
prev_ancil_key = ancil_key;
setup_control_msg(&msg, cw, &specs[n], ancil.buf, sizeof(ancil.buf));
}
else
{
prev_ancil_key = 0;
#ifndef WIN32
msg.msg_control = NULL;
msg.msg_controllen = 0;

View File

@ -78,6 +78,11 @@ IF (CMAKE_SYSTEM_NAME STREQUAL "Linux")
SET(TESTS ${TESTS} frame_rw)
ENDIF()
IF (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows")
# No regexes on Windows
SET(TESTS ${TESTS} ack_merge)
ENDIF()
FOREACH(TEST_NAME ${TESTS})
ADD_EXECUTABLE(test_${TEST_NAME} test_${TEST_NAME}.c ${ADDL_SOURCES})

View File

@ -155,7 +155,6 @@ compare_ackis (const struct ack_info *exp, const struct ack_info *got)
assert(exp->flags == got->flags);
assert(exp->n_ranges == got->n_ranges);
assert(exp->lack_delta == got->lack_delta);
assert(exp->n_timestamps == got->n_timestamps);
for (i = 0; i < exp->n_ranges; ++i)
{

View File

@ -0,0 +1,185 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
/* Test ACK merge */
#include <assert.h>
#include <regex.h>
#include <stdlib.h>
#include <string.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_sizes.h"
#include "lsquic_parse.h"
struct test
{
int lineno;
int expect_failure;
char a[MAX_ACKI_STR_SZ];
char b[MAX_ACKI_STR_SZ];
char result[MAX_ACKI_STR_SZ];
};
static const struct test tests[] =
{
{
.lineno = __LINE__,
.expect_failure = 1,
.a = "",
.b = "[3-3]",
},
{
.lineno = __LINE__,
.expect_failure = 1,
.a = "",
.b = "",
},
{
.lineno = __LINE__,
.a = "[3-3]",
.b = "[3-3]",
.result = "[3-3]",
},
{
.lineno = __LINE__,
.a = "[3-2]",
.b = "[1-1]",
.result = "[3-1]",
},
{
.lineno = __LINE__,
.a = "[15-15][3-2]",
.b = "[1-1]",
.result = "[15-15][3-1]",
},
{
.lineno = __LINE__,
.a = "[15-10][5-2]",
.b = "[9-6][1-1]",
.result = "[15-1]",
},
{
.lineno = __LINE__,
.a = "[15-10][5-2]",
.b = "[15-10][5-2]",
.result = "[15-10][5-2]",
},
{
.lineno = __LINE__,
.a = "[33803-33803][33800-33800][33788-33788][33775-33775][33759-33759][33744-33744][33732-33732][33717-33717][33706-33706][33691-33691][33679-33679][33664-33664][33649-33649][33638-33638][33622-33622][33613-33613][33585-33585][33574-33574][33562-33562][33546-33546][33531-33531][33516-33516][33504-33504][33490-33490][33483-33483][33481-32910][32906-32906][32894-32894][32880-32880][32869-32869][32854-32854][32844-32844][32817-32817][32806-32806][32791-32791][32780-32780][32766-32766][32752-32752][32741-32741][32726-32726][32716-32716][32702-32702][32697-32697][32682-32682][32672-32672][32657-32657][32651-32651][32636-32636][32626-32626][32611-32611][32600-32600][32589-32589][32574-32574][32564-32564][32551-32551][32536-32536][32525-32525][32511-32511][32500-32500][32485-32485][32475-32475][32460-32460][32449-32449][32438-32438][32423-32423][32412-32412][32399-32399][32385-32385][32375-32375][32360-32360][32349-32349][32334-32334][32326-32326][32312-32312][32302-32302][32287-32287][32277-32277][32262-32262][32252-32252][32239-32239][32224-32224][32214-32214][32200-32200][32190-32190][32175-32175][32161-32161][32151-32151][32136-32136][32126-32126][32111-32111][32103-32103][32090-32090][32080-32080][32069-32033][32008-30698][30696-30467]",
.b = "[33480-32910][32906-32906][32894-32894][32880-32880][32869-32869][32854-32854][32844-32844][32817-32817][32806-32806][32791-32791][32780-32780][32766-32766][32752-32752][32741-32741][32726-32726][32716-32716][32702-32702][32697-32697][32682-32682][32672-32672][32657-32657][32651-32651][32636-32636][32626-32626][32611-32611][32600-32600][32589-32589][32574-32574][32564-32564][32551-32551][32536-32536][32525-32525][32511-32511][32500-32500][32485-32485][32475-32475][32460-32460][32449-32449][32438-32438][32423-32423][32412-32412][32399-32399][32385-32385][32375-32375][32360-32360][32349-32349][32334-32334][32326-32326][32312-32312][32302-32302][32287-32287][32277-32277][32262-32262][32252-32252][32239-32239][32224-32224][32214-32214][32200-32200][32190-32190][32175-32175][32161-32161][32151-32151][32136-32136][32126-32126][32111-32111][32103-32103][32090-32090][32080-32080][32069-32033][32008-30698][30696-30467]",
.result = "[33803-33803][33800-33800][33788-33788][33775-33775][33759-33759][33744-33744][33732-33732][33717-33717][33706-33706][33691-33691][33679-33679][33664-33664][33649-33649][33638-33638][33622-33622][33613-33613][33585-33585][33574-33574][33562-33562][33546-33546][33531-33531][33516-33516][33504-33504][33490-33490][33483-33483][33481-32910][32906-32906][32894-32894][32880-32880][32869-32869][32854-32854][32844-32844][32817-32817][32806-32806][32791-32791][32780-32780][32766-32766][32752-32752][32741-32741][32726-32726][32716-32716][32702-32702][32697-32697][32682-32682][32672-32672][32657-32657][32651-32651][32636-32636][32626-32626][32611-32611][32600-32600][32589-32589][32574-32574][32564-32564][32551-32551][32536-32536][32525-32525][32511-32511][32500-32500][32485-32485][32475-32475][32460-32460][32449-32449][32438-32438][32423-32423][32412-32412][32399-32399][32385-32385][32375-32375][32360-32360][32349-32349][32334-32334][32326-32326][32312-32312][32302-32302][32287-32287][32277-32277][32262-32262][32252-32252][32239-32239][32224-32224][32214-32214][32200-32200][32190-32190][32175-32175][32161-32161][32151-32151][32136-32136][32126-32126][32111-32111][32103-32103][32090-32090][32080-32080][32069-32033][32008-30698][30696-30467]",
},
};
static regex_t re;
static void
init_ranges (struct ack_info *a, const char *s)
{
regmatch_t m[3];
while (0 == regexec(&re, s, sizeof(m) / sizeof(m[0]), m, 0))
{
a->ranges[a->n_ranges].high = atoi(s + m[1].rm_so);
a->ranges[a->n_ranges].low = atoi(s + m[2].rm_so);
/* Self-check: */
assert(a->ranges[a->n_ranges].high >= a->ranges[a->n_ranges].low);
++a->n_ranges;
s += m[0].rm_eo;
}
}
static void
run_test_ext (const struct test *test, const char *a_str, const char *b_str)
{
struct ack_info a, b;
int s;
char ack_str[MAX_ACKI_STR_SZ];
memset(&a, 0, sizeof(a));
memset(&b, 0, sizeof(b));
init_ranges(&a, a_str);
init_ranges(&b, b_str);
s = lsquic_merge_acks(&a, &b);
if (test->expect_failure)
{
assert(s != 0);
return;
}
assert(s == 0);
lsquic_acki2str(&a, ack_str, sizeof(ack_str));
assert(0 == strcmp(ack_str, test->result));
}
static void
run_test (const struct test *test)
{
run_test_ext(test, test->a, test->b);
/* If flipped result should be the same: */
run_test_ext(test, test->b, test->a);
}
static void
test_out_of_ranges (int success)
{
struct ack_info a, b;
int i, s;
memset(&a, 0, sizeof(a));
memset(&b, 0, sizeof(b));
for (i = 0; i < 129 - success; ++i)
{
a.ranges[i].high = a.ranges[i].low = 100000 - i * 10;
b.ranges[i].high = b.ranges[i].low = 100000 - 5 - i * 10;
}
a.n_ranges = i;
b.n_ranges = i;
s = lsquic_merge_acks(&a, &b);
if (success)
assert(s == 0);
else
assert(s != 0);
}
int
main (void)
{
const struct test *test;
int s;
s = regcomp(&re, "\\[([0-9][0-9]*)-([0-9][0-9]*)\\]", REG_EXTENDED);
assert(s == 0);
for (test = tests; test < tests + sizeof(tests) / sizeof(tests[0]); ++test)
run_test(test);
test_out_of_ranges(0);
test_out_of_ranges(1);
regfree(&re);
return 0;
}

View File

@ -51,7 +51,6 @@ test1 (void)
assert(("Number of ranges is 1", acki.n_ranges == 1));
assert(("Largest acked is 0x1234", acki.ranges[0].high == 0x1234));
assert(("Lowest acked is 1", acki.ranges[0].low == 1));
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 0x1234", n == 0x1234));
@ -112,7 +111,6 @@ test2 (void)
assert(("Parsed length is correct (29)", len == sizeof(ack_buf)));
assert(("Number of ranges is 4", acki.n_ranges == 4));
assert(("Largest acked is 0x1234", acki.ranges[0].high == 0x1234));
assert(("Number of timestamps is 2", acki.n_timestamps == 2));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 4254", n == 4254));
@ -163,7 +161,6 @@ test3 (void)
assert(("Parsed length is correct (9)", len == sizeof(ack_buf)));
assert(("Number of ranges is 2", acki.n_ranges == 2));
assert(("Largest acked is 6", acki.ranges[0].high == 6));
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 4", n == 4));
@ -213,7 +210,6 @@ test4 (void)
assert(("Parsed length is correct (9)", len == sizeof(ack_buf)));
assert(("Number of ranges is 2", acki.n_ranges == 2));
assert(("Largest acked is 3", acki.ranges[0].high == 3));
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
unsigned n = n_acked(&acki);
assert(("Number of acked packets is 2", n == 2));
@ -267,7 +263,6 @@ test5 (void)
assert(("Parsed length is correct (9)", len == sizeof(ack_buf)));
assert(("Number of ranges is 2", acki.n_ranges == 2));
assert(("Largest acked is 0x23456789", acki.ranges[0].high == 0x23456789));
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
lsquic_packno_t n = n_acked(&acki);
assert(("Number of acked packets is correct", n == 0x23456768 + 1));
@ -316,7 +311,6 @@ test6 (void)
assert(("Parsed length is correct", len == sizeof(ack_buf)));
assert(("Number of ranges is 2", acki.n_ranges == 2));
assert(("Largest acked is 0xABCD23456789", acki.ranges[0].high == 0xABCD23456789));
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
lsquic_packno_t n = n_acked(&acki);
assert(("Number of acked packets is correct", n == 0xABCD23456768 + 1));