1.11.0: [FEATURE] Add support for Q044

This commit is contained in:
Dmitri Tikhonov 2018-08-15 15:06:31 -04:00
parent c6457e4258
commit 9626cfc25b
34 changed files with 1157 additions and 279 deletions

View file

@ -6,6 +6,8 @@ SET(lsquic_STAT_SRCS
lsquic_chsk_stream.c
lsquic_engine.c
lsquic_parse_gquic_common.c
lsquic_parse_iquic_common.c
lsquic_parse_common.c
lsquic_parse_gquic_le.c
lsquic_parse_gquic_be.c
lsquic_packet_in.c
@ -50,6 +52,7 @@ SET(lsquic_STAT_SRCS
lsquic_buf.c
lsquic_min_heap.c
lshpack.c
lsquic_parse_Q044.c
)

View file

@ -0,0 +1,23 @@
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_BYTESWAP_H
#define LSQUIC_BYTESWAP_H 1
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
#include <sys/endian.h>
#define bswap_16 bswap16
#define bswap_32 bswap32
#define bswap_64 bswap64
#elif defined(__APPLE__)
#include <libkern/OSByteOrder.h>
#define bswap_16 OSSwapInt16
#define bswap_32 OSSwapInt32
#define bswap_64 OSSwapInt64
#elif defined(WIN32)
#define bswap_16 _byteswap_ushort
#define bswap_32 _byteswap_ulong
#define bswap_64 _byteswap_uint64
#else
#include <byteswap.h>
#endif
#endif

View file

@ -126,8 +126,8 @@ lsquic_conn_decrypt_packet (lsquic_conn_t *lconn,
| (enc_level << PIBIT_ENC_LEV_SHIFT);
packet_in->pi_header_sz = header_len;
packet_in->pi_data_sz = out_len + header_len;
EV_LOG_CONN_EVENT(lconn->cn_cid, "decrypted packet %"PRIu64,
packet_in->pi_packno);
EV_LOG_CONN_EVENT(lconn->cn_cid, "decrypted packet %"PRIu64" crypto: %s",
packet_in->pi_packno, lsquic_enclev2str[ enc_level ]);
return 0;
}

View file

@ -27,6 +27,7 @@
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_alarmset.h"
#include "lsquic_parse_common.h"
#include "lsquic_parse.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
@ -261,6 +262,18 @@ static const struct lsquic_packout_mem_if stock_pmi =
};
static int
hash_conns_by_addr (const struct lsquic_engine *engine)
{
if (engine->pub.enp_settings.es_versions & LSQUIC_FORCED_TCID0_VERSIONS)
return 1;
if ((engine->pub.enp_settings.es_versions & LSQUIC_GQUIC_HEADER_VERSIONS)
&& engine->pub.enp_settings.es_support_tcid0)
return 1;
return 0;
}
lsquic_engine_t *
lsquic_engine_new (unsigned flags,
const struct lsquic_engine_api *api)
@ -295,7 +308,7 @@ lsquic_engine_new (unsigned flags,
engine->pub.enp_settings = *api->ea_settings;
else
lsquic_engine_init_settings(&engine->pub.enp_settings, flags);
tag_buf_len = gen_ver_tags(engine->pub.enp_ver_tags_buf,
tag_buf_len = lsquic_gen_ver_tags(engine->pub.enp_ver_tags_buf,
sizeof(engine->pub.enp_ver_tags_buf),
engine->pub.enp_settings.es_versions);
if (tag_buf_len <= 0)
@ -324,8 +337,7 @@ lsquic_engine_new (unsigned flags,
}
engine->pub.enp_engine = engine;
conn_hash_init(&engine->conns_hash,
!(flags & ENG_SERVER) && engine->pub.enp_settings.es_support_tcid0 ?
CHF_USE_ADDR : 0);
hash_conns_by_addr(engine) ? CHF_USE_ADDR : 0);
engine->attq = attq_create();
eng_hist_init(&engine->history);
engine->batch_size = INITIAL_OUT_BATCH_SIZE;
@ -498,7 +510,7 @@ process_packet_in (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in,
{
lsquic_conn_t *conn;
if (lsquic_packet_in_is_prst(packet_in)
if (lsquic_packet_in_is_gquic_prst(packet_in)
&& !engine->pub.enp_settings.es_honor_prst)
{
lsquic_mm_put_packet_in(&engine->pub.enp_mm, packet_in);
@ -758,44 +770,34 @@ lsquic_engine_process_conns (lsquic_engine_t *engine)
}
static int
generate_header (const lsquic_packet_out_t *packet_out,
const struct parse_funcs *pf, lsquic_cid_t cid,
unsigned char *buf, size_t bufsz)
{
return pf->pf_gen_reg_pkt_header(buf, bufsz,
packet_out->po_flags & PO_CONN_ID ? &cid : NULL,
packet_out->po_flags & PO_VERSION ? &packet_out->po_ver_tag : NULL,
packet_out->po_flags & PO_NONCE ? packet_out->po_nonce : NULL,
packet_out->po_packno, lsquic_packet_out_packno_bits(packet_out));
}
static ssize_t
really_encrypt_packet (const lsquic_conn_t *conn,
const lsquic_packet_out_t *packet_out,
struct lsquic_packet_out *packet_out,
unsigned char *buf, size_t bufsz)
{
int enc, header_sz, is_hello_packet;
int header_sz, is_hello_packet;
enum enc_level enc_level;
size_t packet_sz;
unsigned char header_buf[QUIC_MAX_PUBHDR_SZ];
header_sz = generate_header(packet_out, conn->cn_pf, conn->cn_cid,
header_sz = conn->cn_pf->pf_gen_reg_pkt_header(conn, packet_out,
header_buf, sizeof(header_buf));
if (header_sz < 0)
return -1;
is_hello_packet = !!(packet_out->po_flags & PO_HELLO);
enc = conn->cn_esf->esf_encrypt(conn->cn_enc_session, conn->cn_version, 0,
enc_level = conn->cn_esf->esf_encrypt(conn->cn_enc_session,
conn->cn_version, 0,
packet_out->po_packno, header_buf, header_sz,
packet_out->po_data, packet_out->po_data_sz,
buf, bufsz, &packet_sz, is_hello_packet);
if (0 == enc)
if ((int) enc_level >= 0)
{
LSQ_DEBUG("encrypted packet %"PRIu64"; plaintext is %u bytes, "
lsquic_packet_out_set_enc_level(packet_out, enc_level);
LSQ_DEBUG("encrypted packet %"PRIu64"; plaintext is %zu bytes, "
"ciphertext is %zd bytes",
packet_out->po_packno,
lsquic_po_header_length(packet_out->po_flags) +
conn->cn_pf->pf_packout_header_size(conn, packet_out->po_flags) +
packet_out->po_data_sz,
packet_sz);
return packet_sz;
@ -814,7 +816,7 @@ encrypt_packet (lsquic_engine_t *engine, const lsquic_conn_t *conn,
unsigned sent_sz;
unsigned char *buf;
bufsz = lsquic_po_header_length(packet_out->po_flags) +
bufsz = conn->cn_pf->pf_packout_header_size(conn, packet_out->po_flags) +
packet_out->po_data_sz + QUIC_PACKET_HASH_SZ;
buf = engine->pub.enp_pmi->pmi_allocate(engine->pub.enp_pmi_ctx, bufsz);
if (!buf)
@ -1281,6 +1283,8 @@ lsquic_engine_packet_in (lsquic_engine_t *engine,
{
struct packin_parse_state ppstate;
lsquic_packet_in_t *packet_in;
int (*parse_packet_in_begin) (struct lsquic_packet_in *, size_t length,
int is_server, struct packin_parse_state *);
if (packet_in_size > QUIC_MAX_PACKET_SZ)
{
@ -1290,6 +1294,20 @@ lsquic_engine_packet_in (lsquic_engine_t *engine,
return -1;
}
if (conn_hash_using_addr(&engine->conns_hash))
{
const struct lsquic_conn *conn;
conn = conn_hash_find_by_addr(&engine->conns_hash, sa_local);
if (!conn)
return -1;
if ((1 << conn->cn_version) & LSQUIC_GQUIC_HEADER_VERSIONS)
parse_packet_in_begin = lsquic_gquic_parse_packet_in_begin;
else
parse_packet_in_begin = lsquic_iquic_parse_packet_in_begin;
}
else
parse_packet_in_begin = lsquic_parse_packet_in_begin;
packet_in = lsquic_mm_get_packet_in(&engine->pub.enp_mm);
if (!packet_in)
return -1;

View file

@ -19,6 +19,8 @@
#include "lsquic_parse.h"
#include "lsquic_frame_common.h"
#include "lsquic_frame_reader.h"
#include "lsquic_str.h"
#include "lsquic_handshake.h"
#include "lsquic_ev_log.h"
#define LSQUIC_LOGGER_MODULE LSQLM_EVENT
@ -39,7 +41,17 @@
void
lsquic_ev_log_packet_in (lsquic_cid_t cid, const lsquic_packet_in_t *packet_in)
{
LCID("packet in: %"PRIu64, packet_in->pi_packno);
switch (packet_in->pi_flags & (
PI_GQUIC))
{
case PI_GQUIC:
LCID("packet in: %"PRIu64, packet_in->pi_packno);
break;
default:
LCID("packet in: %"PRIu64", type: %s",
packet_in->pi_packno, lsquic_hety2str[packet_in->pi_header_type]);
break;
}
}
@ -153,7 +165,7 @@ lsquic_ev_log_packet_sent (lsquic_cid_t cid,
packet_out->po_data_sz);
else if (lsquic_packet_out_pubres(packet_out))
LCID("sent public reset packet, size %hu", packet_out->po_data_sz);
else
else if (packet_out->po_flags & PO_GQUIC)
LCID("sent packet %"PRIu64", size %hu, frame types: %s",
packet_out->po_packno, packet_out->po_enc_data_sz,
/* Frame types is a list of different frames types contained
@ -162,6 +174,18 @@ lsquic_ev_log_packet_sent (lsquic_cid_t cid,
*/
lsquic_frame_types_to_str(frames, sizeof(frames),
packet_out->po_frame_types));
else
LCID("sent packet %"PRIu64", type %s, crypto: %s, size %hu, frame "
"types: %s",
packet_out->po_packno, lsquic_hety2str[packet_out->po_header_type],
lsquic_enclev2str[ lsquic_packet_out_enc_level(packet_out) ],
packet_out->po_enc_data_sz,
/* Frame types is a list of different frames types contained
* in the packet, no more. Count and order of frames is not
* printed.
*/
lsquic_frame_types_to_str(frames, sizeof(frames),
packet_out->po_frame_types));
}

View file

@ -383,6 +383,7 @@ set_versions (struct full_conn *conn, unsigned versions)
conn->fc_ver_neg.vn_ver = highest_bit_set(versions);
conn->fc_ver_neg.vn_buf = lsquic_ver2tag(conn->fc_ver_neg.vn_ver);
conn->fc_conn.cn_version = conn->fc_ver_neg.vn_ver;
conn->fc_conn.cn_pf = select_pf_by_ver(conn->fc_ver_neg.vn_ver);
LSQ_DEBUG("negotiating version %s",
lsquic_ver2str[conn->fc_ver_neg.vn_ver]);
}
@ -675,7 +676,6 @@ full_conn_client_new (struct lsquic_engine_public *enpub,
.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);
conn->fc_conn.cn_pf = select_pf_by_ver(conn->fc_ver_neg.vn_ver);
if (conn->fc_settings->es_handshake_to)
lsquic_alarmset_set(&conn->fc_alset, AL_HANDSHAKE,
lsquic_time_now() + conn->fc_settings->es_handshake_to);
@ -1872,6 +1872,18 @@ parse_regular_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
}
static int
conn_is_stateless_reset (const struct full_conn *conn,
const struct lsquic_packet_in *packet_in)
{
return packet_in->pi_data_sz > SRST_LENGTH
&& 0 == conn->fc_conn.cn_esf->esf_verify_reset_token(
conn->fc_conn.cn_enc_session,
packet_in->pi_data + packet_in->pi_data_sz - SRST_LENGTH,
SRST_LENGTH);
}
static int
process_regular_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
{
@ -1893,11 +1905,20 @@ process_regular_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
if (0 == (packet_in->pi_flags & PI_DECRYPTED) &&
0 != conn_decrypt_packet(conn, packet_in))
{
LSQ_INFO("could not decrypt packet");
if (conn_is_stateless_reset(conn, packet_in))
{
LSQ_INFO("received public reset packet: aborting connection");
conn->fc_flags |= FC_GOT_PRST;
return -1;
}
else
{
LSQ_INFO("could not decrypt packet");
#if FULL_CONN_STATS
++conn->fc_stats.n_undec_packets;
++conn->fc_stats.n_undec_packets;
#endif
return 0;
return 0;
}
}
st = lsquic_rechist_received(&conn->fc_rechist, packet_in->pi_packno,
@ -1937,13 +1958,18 @@ process_regular_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
static int
process_incoming_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
{
int is_prst, is_verneg;
recent_packet_hist_new(conn, 0, packet_in->pi_received);
LSQ_DEBUG("Processing packet %"PRIu64, packet_in->pi_packno);
is_prst = lsquic_packet_in_is_gquic_prst(packet_in);
is_verneg = lsquic_packet_in_is_verneg(packet_in);
/* See flowchart in Section 4.1 of [draft-ietf-quic-transport-00]. We test
* for the common case first.
*/
const unsigned flags = lsquic_packet_in_public_flags(packet_in);
if (0 == (flags & (PACKET_PUBLIC_FLAGS_RST|PACKET_PUBLIC_FLAGS_VERSION)))
if (0 == is_prst && 0 == is_verneg)
{
if (conn->fc_ver_neg.vn_tag)
{
@ -1966,7 +1992,7 @@ process_incoming_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in)
}
return process_regular_packet(conn, packet_in);
}
else if (flags & PACKET_PUBLIC_FLAGS_RST)
else if (is_prst)
{
LSQ_INFO("received public reset packet: aborting connection");
conn->fc_flags |= FC_GOT_PRST;

View file

@ -60,6 +60,7 @@ enum enc_sess_history_event
ESHE_SET_STK = 'K',
ESHE_SET_SCID = 'D',
ESHE_SET_PROF = 'P',
ESHE_SET_SRST = 'S',
};
#endif
@ -71,6 +72,7 @@ typedef struct hs_ctx_st
HSET_SMHL = (1 << 1), /* smhl is set */
HSET_SCID = (1 << 2),
HSET_IRTT = (1 << 3),
HSET_SRST = (1 << 4),
} set;
enum {
HOPT_NSTP = (1 << 0), /* NSTP option present in COPT */
@ -95,6 +97,7 @@ typedef struct hs_ctx_st
//unsigned char chlo_hash[32]; //SHA256 HASH of CHLO
unsigned char nonc[DNONC_LENGTH]; /* 4 tm, 8 orbit ---> REJ, 20 rand */
unsigned char pubs[32];
unsigned char srst[SRST_LENGTH];
uint32_t rrej;
struct lsquic_str ccs;
@ -684,6 +687,18 @@ static int parse_hs_data (lsquic_enc_session_t *enc_session, uint32_t tag,
hs_ctx->sttl = get_tag_value_i64(val, len);
break;
case QTAG_SRST:
if (len != sizeof(hs_ctx->srst))
{
LSQ_INFO("Unexpected size of SRST: %u instead of %zu bytes",
len, sizeof(hs_ctx->srst));
return -1;
}
memcpy(hs_ctx->srst, val, len);
hs_ctx->set |= HSET_SRST;
ESHIST_APPEND(enc_session, ESHE_SET_SRST);
break;
default:
LSQ_DEBUG("Ignored tag '%.*s'", 4, (char *)&tag);
break;
@ -848,7 +863,7 @@ struct message_writer
memset(data_ptr + 4 + 2, 0, 2); \
(mw)->mw_entry = (void *) (data_ptr + 8); \
(mw)->mw_p = data_ptr + 8 + \
n_entries * sizeof((mw)->mw_entry[0]); \
(n_entries) * sizeof((mw)->mw_entry[0]); \
(mw)->mw_first_dummy_entry.tag = 0; \
(mw)->mw_first_dummy_entry.off = 0; \
(mw)->mw_prev_entry = &(mw)->mw_first_dummy_entry; \
@ -1575,7 +1590,7 @@ lsquic_enc_session_decrypt (lsquic_enc_session_t *enc_session,
}
static int
static enum enc_level
lsquic_enc_session_encrypt (lsquic_enc_session_t *enc_session,
enum lsquic_version version,
uint8_t path_id, uint64_t pack_num,
@ -1587,6 +1602,7 @@ lsquic_enc_session_encrypt (lsquic_enc_session_t *enc_session,
uint8_t md[HS_PKT_HASH_LENGTH];
uint128 hash;
int ret;
enum enc_level enc_level;
int is_chlo = (is_hello && ((IS_SERVER(enc_session)) == 0));
int is_shlo = (is_hello && (IS_SERVER(enc_session)));
@ -1620,7 +1636,7 @@ lsquic_enc_session_encrypt (lsquic_enc_session_t *enc_session,
memcpy(buf_out, header, header_len);
memcpy(buf_out + header_len, md, HS_PKT_HASH_LENGTH);
memcpy(buf_out + header_len + HS_PKT_HASH_LENGTH, data, data_len);
return 0;
return ENC_LEV_CLEAR;
}
else
{
@ -1635,12 +1651,14 @@ lsquic_enc_session_encrypt (lsquic_enc_session_t *enc_session,
{
enc_session->server_start_use_final_key = 1;
}
enc_level = ENC_LEV_INIT;
}
else
{
LSQ_DEBUG("lsquic_enc_session_encrypt using 'F' key...");
key = enc_session->enc_ctx_f;
memcpy(nonce, enc_session->enc_key_nonce_f, 4);
enc_level = ENC_LEV_FORW;
}
path_id_packet_number = combine_path_id_pack_num(path_id, pack_num);
memcpy(nonce + 4, &path_id_packet_number,
@ -1651,8 +1669,13 @@ lsquic_enc_session_encrypt (lsquic_enc_session_t *enc_session,
ret = aes_aead_enc(key, header, header_len, nonce, 12, data,
data_len, buf_out + header_len, out_len);
*out_len += header_len;
return ret;
if (ret == 0)
{
*out_len += header_len;
return enc_level;
}
else
return -1;
}
}
@ -1801,6 +1824,19 @@ lsquic_enc_session_mem_used (struct lsquic_enc_session *enc_session)
}
static int
lsquic_enc_session_verify_reset_token (lsquic_enc_session_t *enc_session,
const unsigned char *buf, size_t bufsz)
{
if (bufsz == SRST_LENGTH
&& (enc_session->hs_ctx.set & HSET_SRST)
&& 0 == memcmp(buf, enc_session->hs_ctx.srst, SRST_LENGTH))
return 0;
else
return -1;
}
#ifdef NDEBUG
const
#endif
@ -1822,4 +1858,14 @@ struct enc_session_funcs lsquic_enc_session_gquic_1 =
.esf_gen_chlo = lsquic_enc_session_gen_chlo,
.esf_handle_chlo_reply = lsquic_enc_session_handle_chlo_reply,
.esf_mem_used = lsquic_enc_session_mem_used,
.esf_verify_reset_token = lsquic_enc_session_verify_reset_token,
};
const char *const lsquic_enclev2str[] =
{
[ENC_LEV_UNSET] = "unset",
[ENC_LEV_CLEAR] = "clear",
[ENC_LEV_INIT] = "initial",
[ENC_LEV_FORW] = "forw-secure",
};

View file

@ -13,6 +13,7 @@ typedef struct lsquic_enc_session lsquic_enc_session_t;
#define DNONC_LENGTH 32
#define aes128_key_len 16
#define aes128_iv_len 4
#define SRST_LENGTH 16
enum handshake_error /* TODO: rename this enum */
{
@ -33,6 +34,8 @@ enum enc_level
ENC_LEV_FORW,
};
extern const char *const lsquic_enclev2str[];
/* client side need to store 0rtt info per STK */
typedef struct lsquic_session_cache_info_st
{
@ -86,8 +89,8 @@ struct enc_session_funcs
int (*esf_is_hsk_done)(lsquic_enc_session_t *enc_session);
/* Encrypt buffer */
int (*esf_encrypt)(lsquic_enc_session_t *enc_session, enum lsquic_version,
uint8_t path_id, uint64_t pack_num,
enum enc_level (*esf_encrypt)(lsquic_enc_session_t *enc_session,
enum lsquic_version, uint8_t path_id, uint64_t pack_num,
const unsigned char *header, size_t header_len,
const unsigned char *data, size_t data_len,
unsigned char *buf_out, size_t max_out_len, size_t *out_len,
@ -132,6 +135,10 @@ struct enc_session_funcs
size_t
(*esf_mem_used)(lsquic_enc_session_t *);
int
(*esf_verify_reset_token) (lsquic_enc_session_t *, const unsigned char *,
size_t);
};
extern

View file

@ -88,3 +88,14 @@ restore_packno (lsquic_packno_t cur_packno,
return candidates[min];
}
const char *const lsquic_hety2str[] =
{
[HETY_NOT_SET] = "Short",
[HETY_VERNEG] = "Version Negotiation",
[HETY_INITIAL] = "Initial",
[HETY_RETRY] = "Retry",
[HETY_HANDSHAKE] = "Handshake",
[HETY_0RTT] = "0-RTT",
};

View file

@ -116,10 +116,25 @@ lsquic_frame_types_to_str (char *buf, size_t bufsz, enum quic_ft_bit);
#define QFRAME_RETRANSMITTABLE(frame_type) \
((1 << (frame_type)) & QFRAME_RETRANSMITTABLE_MASK)
#define QUIC_MAX_PUBHDR_SZ (1 /* Type */ + 8 /* CID */ + 4 /* Version */ \
#define GQUIC_MAX_PUBHDR_SZ (1 /* Type */ + 8 /* CID */ + 4 /* Version */ \
+ 32 /* Nonce */ + 6 /* Packet Number */ )
#define QUIC_MIN_PUBHDR_SZ (1 /* Type */ + 1 /* Packet number */)
#define GQUIC_MIN_PUBHDR_SZ (1 /* Type */ + 1 /* Packet number */)
#define GQUIC_IETF_LONG_HEADER_SIZE (1 /* Type */ + 4 /* Version */ \
+ 1 /* DCIL/SCIL */ + 8 /* CID */ + 4 /* Packet number */)
/* XXX Nonce? */
#define IQUIC_MAX_PUBHDR_SZ GQUIC_IETF_LONG_HEADER_SIZE
#define IQUIC_MIN_PUBHDR_SZ (1 /* Type */ + 8 /* CID */ \
+ 1 /* Packet number */)
#define QUIC_MAX_PUBHDR_SZ (GQUIC_MAX_PUBHDR_SZ > IQUIC_MAX_PUBHDR_SZ \
? GQUIC_MAX_PUBHDR_SZ : IQUIC_MAX_PUBHDR_SZ)
#define QUIC_MIN_PUBHDR_SZ (GQUIC_MIN_PUBHDR_SZ < IQUIC_MIN_PUBHDR_SZ \
? GQUIC_MIN_PUBHDR_SZ : IQUIC_MIN_PUBHDR_SZ)
/* 12 bytes of FNV hash or encryption IV */
#define QUIC_PACKET_HASH_SZ 12
@ -151,6 +166,19 @@ enum lsquic_packno_bits
PACKNO_LEN_6 = 3,
};
enum header_type
{
HETY_NOT_SET, /* This value must be zero */
HETY_VERNEG,
HETY_INITIAL,
HETY_RETRY,
HETY_HANDSHAKE,
HETY_0RTT,
};
extern const char *const lsquic_hety2str[];
enum lsquic_packno_bits
calc_packno_bits (lsquic_packno_t packno, lsquic_packno_t least_unacked,
uint64_t n_in_flight);

View file

@ -60,7 +60,9 @@ typedef struct lsquic_packet_in
#define PIBIT_ENC_LEV_SHIFT 5
PI_ENC_LEV_BIT_0= (1 << 5), /* Encodes encryption level */
PI_ENC_LEV_BIT_1= (1 << 6), /* (see enum enc_level). */
PI_GQUIC = (1 << 7),
} pi_flags:8;
enum header_type pi_header_type:8;
/* If PI_OWN_DATA flag is not set, `pi_data' points to user-supplied
* packet data, which is NOT TO BE MODIFIED.
*/
@ -69,11 +71,19 @@ typedef struct lsquic_packet_in
#define lsquic_packet_in_public_flags(p) ((p)->pi_data[0])
#define lsquic_packet_in_is_prst(p) \
(lsquic_packet_in_public_flags(p) & PACKET_PUBLIC_FLAGS_RST)
#define lsquic_packet_in_is_gquic_prst(p) \
(((p)->pi_flags & PI_GQUIC) \
&& (lsquic_packet_in_public_flags(p) & PACKET_PUBLIC_FLAGS_RST))
#define lsquic_packet_in_is_verneg(p) \
(((p)->pi_flags & PI_GQUIC) ? \
lsquic_packet_in_public_flags(p) & PACKET_PUBLIC_FLAGS_VERSION : \
(p)->pi_header_type == HETY_VERNEG)
#define lsquic_packet_in_packno_bits(p) \
((lsquic_packet_in_public_flags(p) >> 4) & 3)
(((p)->pi_flags & PI_GQUIC) ? \
((lsquic_packet_in_public_flags(p) >> 4) & 3) : \
((p)->pi_data[0] & 3))
#define lsquic_packet_in_upref(p) (++(p)->pi_refcnt)

View file

@ -22,6 +22,7 @@
#include "lsquic_stream.h"
#include "lsquic_logger.h"
#include "lsquic_ev_log.h"
#include "lsquic_conn.h"
typedef char _stream_rec_arr_is_at_most_64bytes[
(sizeof(struct stream_rec_arr) <= 64)? 1: - 1];
@ -212,12 +213,12 @@ lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,
lsquic_packet_out_t *
lsquic_packet_out_new (struct lsquic_mm *mm, struct malo *malo, int use_cid,
unsigned short max_size, enum lsquic_packno_bits bits,
const struct lsquic_conn *lconn, enum lsquic_packno_bits bits,
const lsquic_ver_tag_t *ver_tag, const unsigned char *nonce)
{
lsquic_packet_out_t *packet_out;
enum packet_out_flags flags;
unsigned short header_size;
unsigned short header_size, max_size;
flags = bits << POBIT_SHIFT;
if (ver_tag)
@ -226,8 +227,22 @@ lsquic_packet_out_new (struct lsquic_mm *mm, struct malo *malo, int use_cid,
flags |= PO_NONCE;
if (use_cid)
flags |= PO_CONN_ID;
if ((1 << lconn->cn_version) & LSQUIC_GQUIC_HEADER_VERSIONS)
flags |= PO_GQUIC;
if (
0 == (lconn->cn_flags & LSCONN_HANDSHAKE_DONE)
)
{
flags |= PO_LONGHEAD;
if (!((1 << lconn->cn_version) & LSQUIC_GQUIC_HEADER_VERSIONS))
{
flags &= ~(3 << POBIT_SHIFT);
flags |= PACKNO_LEN_4 << POBIT_SHIFT;
}
}
header_size = lsquic_po_header_length(flags);
header_size = lsquic_po_header_length(lconn, flags);
max_size = lconn->cn_pack_size;
if (header_size + QUIC_PACKET_HASH_SZ >= max_size)
{
errno = EINVAL;
@ -255,6 +270,8 @@ lsquic_packet_out_new (struct lsquic_mm *mm, struct malo *malo, int use_cid,
}
memcpy(packet_out->po_nonce, nonce, 32);
}
if (flags & PO_LONGHEAD)
packet_out->po_header_type = HETY_HANDSHAKE;
return packet_out;
}
@ -602,6 +619,8 @@ verify_srecs (lsquic_packet_out_t *packet_out)
assert(packet_out->po_data_sz == off);
}
#endif

View file

@ -9,6 +9,7 @@
#include <sys/queue.h>
struct malo;
struct lsquic_conn;
struct lsquic_engine_public;
struct lsquic_mm;
struct lsquic_stream;
@ -84,7 +85,12 @@ typedef struct lsquic_packet_out
*/
PO_SCHED = (1 <<14), /* On scheduled queue */
PO_SENT_SZ = (1 <<15),
} po_flags:16;
PO_LONGHEAD = (1 <<16),
PO_GQUIC = (1 <<17), /* Used for logging */
#define POLEV_SHIFT 18
PO_BITS_2 = (1 <<18), /* PO_BITS_2 and PO_BITS_3 encode the */
PO_BITS_3 = (1 <<19), /* crypto level. Used for logging. */
} po_flags;
enum quic_ft_bit po_frame_types:16; /* Bitmask of QUIC_FRAME_* */
unsigned short po_data_sz; /* Number of usable bytes in data */
unsigned short po_enc_data_sz; /* Number of usable bytes in data */
@ -95,6 +101,7 @@ typedef struct lsquic_packet_out
* frames.
*/
unsigned short po_n_alloc; /* Total number of bytes allocated in po_data */
enum header_type po_header_type:8;
unsigned char *po_data;
lsquic_packno_t po_ack2ed; /* If packet has ACK frame, value of
* largest acked in it.
@ -135,33 +142,27 @@ typedef struct lsquic_packet_out
(p)->po_flags |= ((b) & 0x3) << POBIT_SHIFT; \
} while (0)
#define lsquic_po_header_length(po_flags) ( \
1 /* Type */ \
+ (!!((po_flags) & PO_CONN_ID) << 3) /* Connection ID */ \
+ (!!((po_flags) & PO_VERSION) << 2) /* Version */ \
+ (!!((po_flags) & PO_NONCE) << 5) /* Nonce */ \
+ packno_bits2len(((po_flags) >> POBIT_SHIFT) & 0x3) /* Packet number */ \
)
#define lsquic_po_header_length(lconn, po_flags) ( \
lconn->cn_pf->pf_packout_header_size(lconn, po_flags))
#define lsquic_packet_out_total_sz(p) \
((p)->po_data_sz + lsquic_po_header_length((p)->po_flags) \
+ QUIC_PACKET_HASH_SZ)
#define lsquic_packet_out_total_sz(lconn, p) (\
lconn->cn_pf->pf_packout_size(lconn, p))
#if __GNUC__
#if LSQUIC_EXTRA_CHECKS
#define lsquic_packet_out_sent_sz(p) ( \
#define lsquic_packet_out_sent_sz(lconn, p) ( \
__builtin_expect(((p)->po_flags & PO_SENT_SZ), 1) ? \
(assert((p)->po_sent_sz == lsquic_packet_out_total_sz(p)), \
(p)->po_sent_sz) : lsquic_packet_out_total_sz(p))
(assert((p)->po_sent_sz == lsquic_packet_out_total_sz(lconn, p)), \
(p)->po_sent_sz) : lsquic_packet_out_total_sz(lconn, p))
# else
#define lsquic_packet_out_sent_sz(p) ( \
#define lsquic_packet_out_sent_sz(lconn, p) ( \
__builtin_expect(((p)->po_flags & PO_SENT_SZ), 1) ? \
(p)->po_sent_sz : lsquic_packet_out_total_sz(p))
(p)->po_sent_sz : lsquic_packet_out_total_sz(lconn, p))
#endif
#else
# define lsquic_packet_out_sent_sz(p) ( \
# define lsquic_packet_out_sent_sz(lconn, p) ( \
(p)->po_flags & PO_SENT_SZ ? \
(p)->po_sent_sz : lsquic_packet_out_total_sz(p))
(p)->po_sent_sz : lsquic_packet_out_total_sz(lconn, p))
#endif
#define lsquic_packet_out_verneg(p) \
@ -170,6 +171,13 @@ typedef struct lsquic_packet_out
#define lsquic_packet_out_pubres(p) \
(((p)->po_flags & (PO_NOENCRYPT|PO_VERNEG)) == PO_NOENCRYPT )
#define lsquic_packet_out_set_enc_level(p, level) do { \
(p)->po_flags &= ~(3 << POLEV_SHIFT); \
(p)->po_flags |= level << POLEV_SHIFT; \
} while (0)
#define lsquic_packet_out_enc_level(p) (((p)->po_flags >> POLEV_SHIFT) & 3)
struct packet_out_srec_iter {
lsquic_packet_out_t *packet_out;
struct stream_rec_arr *cur_srec_arr;
@ -185,7 +193,7 @@ posi_next (struct packet_out_srec_iter *posi);
lsquic_packet_out_t *
lsquic_packet_out_new (struct lsquic_mm *, struct malo *, int use_cid,
unsigned short size, enum lsquic_packno_bits,
const struct lsquic_conn *, enum lsquic_packno_bits,
const lsquic_ver_tag_t *, const unsigned char *nonce);
void

View file

@ -7,7 +7,10 @@
#include "lsquic_packet_common.h"
struct lsquic_packet_in;
struct lsquic_packet_out;
struct packin_parse_state;
struct stream_frame;
enum packet_out_flags;
#define LSQUIC_PARSE_ACK_TIMESTAMPS 0
@ -54,27 +57,17 @@ typedef lsquic_time_t
/* gsf_: generate stream frame */
typedef size_t (*gsf_read_f) (void *stream, void *buf, size_t len, int *fin);
struct packin_parse_state {
const unsigned char *pps_p; /* Pointer to packet number */
unsigned pps_nbytes; /* Number of bytes in packet number */
};
/* This structure contains functions that parse and generate packets and
* frames in version-specific manner. To begin with, there is difference
* between GQUIC's little-endian (Q038 and lower) and big-endian formats
* (Q039 and higher).
* (Q039 and higher). Q044 uses different format for packet headers.
*/
struct parse_funcs
{
int
(*pf_gen_ver_nego_pkt) (unsigned char *buf, size_t bufsz, uint64_t conn_id,
unsigned version_bitmask);
/* Return buf length */
int
(*pf_gen_reg_pkt_header) (unsigned char *buf, size_t bufsz,
const lsquic_cid_t *, const lsquic_ver_tag_t *,
const unsigned char *nonce, lsquic_packno_t,
enum lsquic_packno_bits);
(*pf_gen_reg_pkt_header) (const struct lsquic_conn *,
const struct lsquic_packet_out *, unsigned char *, size_t);
void
(*pf_parse_packet_in_finish) (struct lsquic_packet_in *packet_in,
struct packin_parse_state *);
@ -154,28 +147,57 @@ struct parse_funcs
(*pf_calc_stream_frame_header_sz) (uint32_t stream_id, uint64_t offset);
void
(*pf_turn_on_fin) (unsigned char *);
size_t
(*pf_packout_size) (const struct lsquic_conn *,
const struct lsquic_packet_out *);
size_t
(*pf_packout_header_size) (const struct lsquic_conn *,
enum packet_out_flags);
};
extern const struct parse_funcs lsquic_parse_funcs_gquic_le;
/* Q039 and later are big-endian: */
extern const struct parse_funcs lsquic_parse_funcs_gquic_Q039;
extern const struct parse_funcs lsquic_parse_funcs_gquic_Q044;
#define select_pf_by_ver(ver) ( \
((1 << (ver)) & (1 << LSQVER_035)) \
? &lsquic_parse_funcs_gquic_le \
: &lsquic_parse_funcs_gquic_Q039)
: (ver) < LSQVER_044 \
? &lsquic_parse_funcs_gquic_Q039 \
: &lsquic_parse_funcs_gquic_Q044)
/* This function is QUIC-version independent */
int
parse_packet_in_begin (struct lsquic_packet_in *, size_t length,
lsquic_gquic_parse_packet_in_begin (struct lsquic_packet_in *, size_t length,
int is_server, struct packin_parse_state *);
int
lsquic_iquic_parse_packet_in_long_begin (struct lsquic_packet_in *,
size_t length, int is_server, struct packin_parse_state *state);
int
lsquic_iquic_parse_packet_in_short_begin (struct lsquic_packet_in *,
size_t length, int is_server, struct packin_parse_state *state);
enum QUIC_FRAME_TYPE
parse_frame_type_gquic_Q035_thru_Q039 (unsigned char first_byte);
size_t
calc_stream_frame_header_sz_gquic (uint32_t stream_id, uint64_t offset);
size_t
lsquic_gquic_packout_size (const struct lsquic_conn *,
const struct lsquic_packet_out *);
size_t
lsquic_gquic_packout_header_size (const struct lsquic_conn *conn,
enum packet_out_flags flags);
size_t
lsquic_gquic_po_header_sz (enum packet_out_flags flags);
/* This maps two bits as follows:
* 00 -> 1
* 01 -> 2

View file

@ -0,0 +1,213 @@
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_parse_Q044.c -- Parsing functions specific to GQUIC Q044
*/
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <string.h>
#include <sys/queue.h>
#ifndef WIN32
#include <sys/types.h>
#else
#include <vc_compat.h>
#endif
#include "lsquic_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
#include "lsquic_parse.h"
#include "lsquic_parse_common.h"
#include "lsquic_version.h"
#include "lsquic.h"
#include "lsquic_parse_gquic_be.h"
#include "lsquic_byteswap.h"
#include "lsquic_conn.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PARSE
#include "lsquic_logger.h"
static int
gen_short_pkt_header (const struct lsquic_conn *lconn,
const struct lsquic_packet_out *packet_out, unsigned char *buf,
size_t bufsz)
{
unsigned packno_len, need;
enum lsquic_packno_bits bits;
uint32_t packno;
bits = (packet_out->po_flags >> POBIT_SHIFT) & 0x3;
packno_len = packno_bits2len(bits);
need = 1 + 8 /* CID */ + packno_len;
if (need > bufsz)
return -1;
*buf++ = 0x30 | bits;
memcpy(buf, &lconn->cn_cid, 8);
buf += 8;
packno = packet_out->po_packno;
#if __BYTE_ORDER == __LITTLE_ENDIAN
packno = bswap_32(packno);
#endif
memcpy(buf, (unsigned char *) &packno + 4 - packno_len, packno_len);
return need;
}
static size_t
gquic_Q044_packout_header_size_long (const struct lsquic_conn *lconn,
enum packet_out_flags flags)
{
return GQUIC_IETF_LONG_HEADER_SIZE;
}
static const unsigned char header_type_to_bin[] = {
[HETY_NOT_SET] = 0x00,
[HETY_INITIAL] = 0x7F,
[HETY_RETRY] = 0x7E,
[HETY_HANDSHAKE] = 0x7D,
[HETY_0RTT] = 0x7C,
};
static int
gen_long_pkt_header (const struct lsquic_conn *lconn,
const struct lsquic_packet_out *packet_out, unsigned char *buf,
size_t bufsz)
{
lsquic_ver_tag_t ver_tag;
unsigned char *p;
uint32_t packno;
size_t need;
need = gquic_Q044_packout_header_size_long(lconn, packet_out->po_flags);
if (need > bufsz)
{
errno = EINVAL;
return -1;
}
p = buf;
*p++ = 0x80 | header_type_to_bin[ packet_out->po_header_type ];
ver_tag = lsquic_ver2tag(lconn->cn_version);
memcpy(p, &ver_tag, sizeof(ver_tag));
p += sizeof(ver_tag);
*p++ = 0x50;
memcpy(p, &lconn->cn_cid, 8);
p += 8;
packno = packet_out->po_packno;
#if __BYTE_ORDER == __LITTLE_ENDIAN
packno = bswap_32(packno);
#endif
memcpy(p, &packno, 4);
p += 4;
assert(need = p - buf);
return p - buf;
}
static int
gquic_Q044_gen_reg_pkt_header (const struct lsquic_conn *lconn,
const struct lsquic_packet_out *packet_out, unsigned char *buf,
size_t bufsz)
{
if (0 == (packet_out->po_flags & PO_LONGHEAD))
return gen_short_pkt_header(lconn, packet_out, buf, bufsz);
else
return gen_long_pkt_header(lconn, packet_out, buf, bufsz);
}
static size_t
gquic_Q044_packout_header_size_short (const struct lsquic_conn *lconn,
enum packet_out_flags flags)
{
enum lsquic_packno_bits bits;
size_t sz;
bits = (flags >> POBIT_SHIFT) & 0x3;
sz = 1; /* Type */
sz += 8; /* CID */
sz += packno_bits2len(bits);
return sz;
}
static size_t
gquic_Q044_packout_header_size (const struct lsquic_conn *lconn,
enum packet_out_flags flags)
{
if (0 == (flags & PO_LONGHEAD))
return gquic_Q044_packout_header_size_short(lconn, flags);
else
return gquic_Q044_packout_header_size_long(lconn, flags);
}
static size_t
gquic_Q044_packout_size (const struct lsquic_conn *lconn,
const struct lsquic_packet_out *packet_out)
{
size_t sz;
if (0 == (packet_out->po_flags & PO_LONGHEAD))
sz = gquic_Q044_packout_header_size_short(lconn, packet_out->po_flags);
else
sz = gquic_Q044_packout_header_size_long(lconn, packet_out->po_flags);
sz += packet_out->po_data_sz;
sz += QUIC_PACKET_HASH_SZ;
return sz;
}
const struct parse_funcs lsquic_parse_funcs_gquic_Q044 =
{
.pf_gen_reg_pkt_header = gquic_Q044_gen_reg_pkt_header,
.pf_parse_packet_in_finish = gquic_be_parse_packet_in_finish,
.pf_gen_stream_frame = gquic_be_gen_stream_frame,
.pf_calc_stream_frame_header_sz = calc_stream_frame_header_sz_gquic,
.pf_parse_stream_frame = gquic_be_parse_stream_frame,
.pf_parse_ack_frame = gquic_be_parse_ack_frame,
.pf_gen_ack_frame = gquic_be_gen_ack_frame,
.pf_gen_stop_waiting_frame = gquic_be_gen_stop_waiting_frame,
.pf_parse_stop_waiting_frame = gquic_be_parse_stop_waiting_frame,
.pf_skip_stop_waiting_frame = gquic_be_skip_stop_waiting_frame,
.pf_gen_window_update_frame = gquic_be_gen_window_update_frame,
.pf_parse_window_update_frame = gquic_be_parse_window_update_frame,
.pf_gen_blocked_frame = gquic_be_gen_blocked_frame,
.pf_parse_blocked_frame = gquic_be_parse_blocked_frame,
.pf_gen_rst_frame = gquic_be_gen_rst_frame,
.pf_parse_rst_frame = gquic_be_parse_rst_frame,
.pf_gen_connect_close_frame = gquic_be_gen_connect_close_frame,
.pf_parse_connect_close_frame = gquic_be_parse_connect_close_frame,
.pf_gen_goaway_frame = gquic_be_gen_goaway_frame,
.pf_parse_goaway_frame = gquic_be_parse_goaway_frame,
.pf_gen_ping_frame = gquic_be_gen_ping_frame,
#ifndef NDEBUG
.pf_write_float_time16 = gquic_be_write_float_time16,
.pf_read_float_time16 = gquic_be_read_float_time16,
#endif
.pf_parse_frame_type = parse_frame_type_gquic_Q035_thru_Q039,
.pf_turn_on_fin = lsquic_turn_on_fin_Q035_thru_Q039,
.pf_packout_size = gquic_Q044_packout_size,
.pf_packout_header_size = gquic_Q044_packout_header_size,
};

View file

@ -0,0 +1,56 @@
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
#include <string.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_parse_common.h"
#include "lsquic_parse.h"
int
lsquic_parse_packet_in_begin (lsquic_packet_in_t *packet_in, size_t length,
int is_server, struct packin_parse_state *state)
{
if (length > 0)
{
switch (packet_in->pi_data[0] & 0x88)
{
case 0x88:
case 0x80:
return lsquic_iquic_parse_packet_in_long_begin(packet_in, length,
is_server, state);
case 0x08:
return lsquic_gquic_parse_packet_in_begin(packet_in, length,
is_server, state);
default:
return lsquic_iquic_parse_packet_in_short_begin(packet_in, length,
is_server, state);
}
}
else
return -1;
}
int
lsquic_iquic_parse_packet_in_begin (struct lsquic_packet_in *packet_in,
size_t length, int is_server, struct packin_parse_state *state)
{
if (length > 0)
{
if (0 == (packet_in->pi_data[0] & 0x80))
return lsquic_iquic_parse_packet_in_short_begin(packet_in, length,
is_server, state);
else
return lsquic_iquic_parse_packet_in_long_begin(packet_in, length,
is_server, state);
}
else
return -1;
}

View file

@ -0,0 +1,24 @@
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_parse_common.h
*/
#ifndef LSQUIC_PARSE_COMMON_H
#define LSQUIC_PARSE_COMMON_H 1
struct lsquic_packet_in;
struct packin_parse_state {
const unsigned char *pps_p; /* Pointer to packet number */
unsigned pps_nbytes; /* Number of bytes in packet number */
};
int
lsquic_parse_packet_in_begin (struct lsquic_packet_in *,
size_t length, int is_server, struct packin_parse_state *);
int
lsquic_iquic_parse_packet_in_begin (struct lsquic_packet_in *,
size_t length, int is_server, struct packin_parse_state *);
#endif

View file

@ -20,7 +20,9 @@
#include "lsquic_alarmset.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
#include "lsquic_parse.h"
#include "lsquic_parse_common.h"
#include "lsquic_rechist.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
@ -28,7 +30,9 @@
#include "lsquic_malo.h"
#include "lsquic_version.h"
#include "lsquic.h"
#include "lsquic_conn.h"
#include "lsquic_parse_gquic_be.h" /* Include to catch mismatches */
#include "lsquic_byteswap.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PARSE
#include "lsquic_logger.h"
@ -98,41 +102,20 @@ gquic_be_parse_packet_in_finish (lsquic_packet_in_t *packet_in,
}
int
gquic_be_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz, uint64_t conn_id,
unsigned version_bitmask)
{
int sz;
unsigned char *p = buf;
unsigned char *const pend = p + bufsz;
CHECK_SPACE(1, p, pend);
*p = PACKET_PUBLIC_FLAGS_VERSION | PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID;
++p;
CHECK_SPACE(8, p, pend);
memcpy(p, &conn_id, 8);
p += 8;
sz = gen_ver_tags(p, pend - p, version_bitmask);
if (sz < 0)
return -1;
return p + sz - buf;
}
int
gquic_be_gen_reg_pkt_header (unsigned char *buf, size_t bufsz, const lsquic_cid_t *conn_id,
const lsquic_ver_tag_t *ver, const unsigned char *nonce,
lsquic_packno_t packno, enum lsquic_packno_bits bits)
static int
gquic_be_gen_reg_pkt_header (const struct lsquic_conn *lconn,
const struct lsquic_packet_out *packet_out, unsigned char *buf,
size_t bufsz)
{
unsigned packnum_len, header_len;
enum lsquic_packno_bits bits;
lsquic_packno_t packno;
unsigned char *p;
bits = lsquic_packet_out_packno_bits(packet_out);
packnum_len = packno_bits2len(bits);
if (!(conn_id || ver || nonce))
if (0 == (packet_out->po_flags & (PO_CONN_ID|PO_VERSION|PO_NONCE)))
{
header_len = 1 + packnum_len;
if (header_len > bufsz)
@ -146,8 +129,16 @@ gquic_be_gen_reg_pkt_header (unsigned char *buf, size_t bufsz, const lsquic_cid_
}
else
{
header_len = 1 + (!!conn_id << 3) + (!!ver << 2) + ((!!nonce) << 5)
+ packnum_len;
const int
have_cid = packet_out->po_flags & PO_CONN_ID,
have_ver = packet_out->po_flags & PO_VERSION,
have_nonce = packet_out->po_flags & PO_NONCE;
header_len = 1
+ (!!have_cid << 3)
+ (!!have_ver << 2)
+ (!!have_nonce << 5)
+ packnum_len
;
if (header_len > bufsz)
{
errno = ENOBUFS;
@ -156,31 +147,32 @@ gquic_be_gen_reg_pkt_header (unsigned char *buf, size_t bufsz, const lsquic_cid_
p = buf;
*p = (!!conn_id << 3)
*p = (!!have_cid << 3)
| (bits << 4)
| ((!!nonce) << 2)
| !!ver;
| ((!!have_nonce) << 2)
| !!have_ver;
++p;
if (conn_id)
if (have_cid)
{
memcpy(p, conn_id , sizeof(*conn_id));
p += sizeof(*conn_id);
memcpy(p, &lconn->cn_cid, sizeof(lconn->cn_cid));
p += sizeof(lconn->cn_cid);
}
if (ver)
if (have_ver)
{
memcpy(p, ver, 4);
memcpy(p, &packet_out->po_ver_tag, 4);
p += 4;
}
if (nonce)
if (have_nonce)
{
memcpy(p, nonce , 32);
memcpy(p, packet_out->po_nonce , 32);
p += 32;
}
}
packno = packet_out->po_packno;
#if __BYTE_ORDER == __LITTLE_ENDIAN
packno = bswap_64(packno);
#endif
@ -955,7 +947,6 @@ gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
const struct parse_funcs lsquic_parse_funcs_gquic_Q039 =
{
.pf_gen_ver_nego_pkt = gquic_be_gen_ver_nego_pkt,
.pf_gen_reg_pkt_header = gquic_be_gen_reg_pkt_header,
.pf_parse_packet_in_finish = gquic_be_parse_packet_in_finish,
.pf_gen_stream_frame = gquic_be_gen_stream_frame,
@ -983,4 +974,6 @@ const struct parse_funcs lsquic_parse_funcs_gquic_Q039 =
#endif
.pf_parse_frame_type = parse_frame_type_gquic_Q035_thru_Q039,
.pf_turn_on_fin = lsquic_turn_on_fin_Q035_thru_Q039,
.pf_packout_size = lsquic_gquic_packout_size,
.pf_packout_header_size = lsquic_gquic_packout_header_size,
};

View file

@ -8,24 +8,6 @@
* and that would be a mess.
*/
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
#include <sys/endian.h>
#define bswap_16 bswap16
#define bswap_32 bswap32
#define bswap_64 bswap64
#elif defined(__APPLE__)
#include <libkern/OSByteOrder.h>
#define bswap_16 OSSwapInt16
#define bswap_32 OSSwapInt32
#define bswap_64 OSSwapInt64
#elif defined(WIN32)
#define bswap_16 _byteswap_ushort
#define bswap_32 _byteswap_ulong
#define bswap_64 _byteswap_uint64
#else
#include <byteswap.h>
#endif
#define CHECK_SPACE(need, pstart, pend) \
do { if ((intptr_t) (need) > ((pend) - (pstart))) { return -1; } } while (0)
@ -58,11 +40,6 @@ int
gquic_be_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz, uint64_t conn_id,
unsigned version_bitmask);
int
gquic_be_gen_reg_pkt_header (unsigned char *buf, size_t bufsz, const lsquic_cid_t *conn_id,
const lsquic_ver_tag_t *ver, const unsigned char *nonce,
lsquic_packno_t packno, enum lsquic_packno_bits bits);
int
gquic_be_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, int fin, size_t size,

View file

@ -17,8 +17,11 @@
#include "lsquic_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_out.h"
#include "lsquic_packet_in.h"
#include "lsquic_parse.h"
#include "lsquic_parse_common.h"
#include "lsquic_version.h"
#include "lsquic.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PARSE
@ -35,8 +38,8 @@
* pf_parse_packet_in_finish() routine.
*/
int
parse_packet_in_begin (lsquic_packet_in_t *packet_in, size_t length,
int is_server, struct packin_parse_state *state)
lsquic_gquic_parse_packet_in_begin (struct lsquic_packet_in *packet_in,
size_t length, int is_server, struct packin_parse_state *state)
{
int nbytes;
enum PACKET_PUBLIC_FLAGS public_flags;
@ -128,6 +131,7 @@ parse_packet_in_begin (lsquic_packet_in_t *packet_in, size_t length,
packet_in->pi_data_sz = length;
packet_in->pi_refcnt = 0;
packet_in->pi_received = 0;
packet_in->pi_flags |= PI_GQUIC;
return 0;
}
@ -462,3 +466,36 @@ acki2str (const struct ack_info *acki, size_t *sz)
*sz = off;
return buf;
}
size_t
lsquic_gquic_po_header_sz (enum packet_out_flags flags)
{
return 1 /* Type */
+ (!!(flags & PO_CONN_ID) << 3) /* Connection ID */
+ (!!(flags & PO_VERSION) << 2) /* Version */
+ (!!(flags & PO_NONCE) << 5) /* Nonce */
+ packno_bits2len((flags >> POBIT_SHIFT) & 0x3) /* Packet number */
;
}
size_t
lsquic_gquic_packout_size (const struct lsquic_conn *conn,
const struct lsquic_packet_out *packet_out)
{
return lsquic_gquic_po_header_sz(packet_out->po_flags)
+ packet_out->po_data_sz
+ QUIC_PACKET_HASH_SZ
;
}
size_t
lsquic_gquic_packout_header_size (const struct lsquic_conn *conn,
enum packet_out_flags flags)
{
return lsquic_gquic_po_header_sz(flags);
}

View file

@ -20,7 +20,9 @@
#include "lsquic_alarmset.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
#include "lsquic_parse.h"
#include "lsquic_parse_common.h"
#include "lsquic_rechist.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
@ -28,6 +30,7 @@
#include "lsquic_malo.h"
#include "lsquic_version.h"
#include "lsquic.h"
#include "lsquic_conn.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PARSE
#include "lsquic_logger.h"
@ -118,41 +121,23 @@ gquic_le_parse_packet_in_finish (lsquic_packet_in_t *packet_in,
static int
gquic_le_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz, uint64_t conn_id,
unsigned version_bitmask)
{
int sz;
unsigned char *p = buf;
unsigned char *const pend = p + bufsz;
CHECK_SPACE(1, p, pend);
*p = PACKET_PUBLIC_FLAGS_VERSION | PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID;
++p;
CHECK_SPACE(8, p, pend);
memcpy(p, &conn_id, 8);
p += 8;
sz = gen_ver_tags(p, pend - p, version_bitmask);
if (sz < 0)
return -1;
return p + sz - buf;
}
static int
gquic_le_gen_reg_pkt_header (unsigned char *buf, size_t bufsz, const lsquic_cid_t *conn_id,
const lsquic_ver_tag_t *ver, const unsigned char *nonce,
lsquic_packno_t packno, enum lsquic_packno_bits bits)
gquic_le_gen_reg_pkt_header (const struct lsquic_conn *lconn,
const struct lsquic_packet_out *packet_out, unsigned char *buf,
size_t bufsz)
{
unsigned packnum_len, header_len;
unsigned char *p;
enum lsquic_packno_bits bits;
bits = lsquic_packet_out_packno_bits(packet_out);
packnum_len = packno_bits2len(bits);
header_len = 1 + (!!conn_id << 3) + (!!ver << 2) + ((!!nonce) << 5)
+ packnum_len;
header_len = 1
+ (!!(packet_out->po_flags & PO_CONN_ID) << 3)
+ (!!(packet_out->po_flags & PO_VERSION) << 2)
+ (!!(packet_out->po_flags & PO_NONCE) << 5)
+ packnum_len
;
if (header_len > bufsz)
{
errno = ENOBUFS;
@ -161,32 +146,32 @@ gquic_le_gen_reg_pkt_header (unsigned char *buf, size_t bufsz, const lsquic_cid_
p = buf;
*p = (!!conn_id << 3)
*p = (!!(packet_out->po_flags & PO_CONN_ID) << 3)
| (bits << 4)
| ((!!nonce) << 2)
| !!ver;
| ((!!(packet_out->po_flags & PO_NONCE)) << 2)
| !!(packet_out->po_flags & PO_VERSION);
++p;
if (conn_id)
if (packet_out->po_flags & PO_CONN_ID)
{
memcpy(p, conn_id , sizeof(*conn_id));
p += sizeof(*conn_id);
memcpy(p, &lconn->cn_cid, sizeof(lconn->cn_cid));
p += sizeof(lconn->cn_cid);
}
if (ver)
if (packet_out->po_flags & PO_VERSION)
{
memcpy(p, ver, 4);
memcpy(p, &packet_out->po_ver_tag, 4);
p += 4;
}
if (nonce)
if (packet_out->po_flags & PO_NONCE)
{
memcpy(p, nonce , 32);
memcpy(p, packet_out->po_nonce, 32);
p += 32;
}
/* ENDIAN */
memcpy(p, &packno, packnum_len);
memcpy(p, &packet_out->po_packno, packnum_len);
p += packnum_len;
assert(p - buf == (intptr_t) header_len);
@ -832,7 +817,6 @@ gquic_le_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
const struct parse_funcs lsquic_parse_funcs_gquic_le =
{
.pf_gen_ver_nego_pkt = gquic_le_gen_ver_nego_pkt,
.pf_gen_reg_pkt_header = gquic_le_gen_reg_pkt_header,
.pf_parse_packet_in_finish = gquic_le_parse_packet_in_finish,
.pf_gen_stream_frame = gquic_le_gen_stream_frame,
@ -860,4 +844,6 @@ const struct parse_funcs lsquic_parse_funcs_gquic_le =
#endif
.pf_parse_frame_type = parse_frame_type_gquic_Q035_thru_Q039,
.pf_turn_on_fin = lsquic_turn_on_fin_Q035_thru_Q039,
.pf_packout_size = lsquic_gquic_packout_size,
.pf_packout_header_size = lsquic_gquic_packout_header_size,
};

View file

@ -0,0 +1,185 @@
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <openssl/rand.h>
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_parse_common.h"
#include "lsquic_parse.h"
#include "lsquic_version.h"
#include "lsquic.h"
#include "lsquic_logger.h"
#include "lsquic_byteswap.h"
#include "lsquic_str.h"
#include "lsquic_handshake.h"
static const enum header_type bin_2_header_type[0x100] =
{
[0x80 | 0x7F] = HETY_INITIAL,
[0x80 | 0x7E] = HETY_RETRY,
[0x80 | 0x7D] = HETY_HANDSHAKE,
[0x80 | 0x7C] = HETY_0RTT,
};
int
lsquic_iquic_parse_packet_in_long_begin (lsquic_packet_in_t *packet_in,
size_t length, int is_server, struct packin_parse_state *state)
{
const unsigned char *p = packet_in->pi_data;
const unsigned char *const end = p + length;
lsquic_ver_tag_t tag;
enum header_type header_type;
unsigned dcil, scil;
int verneg;
unsigned char first_byte;
const unsigned cid_len = 8;
if (length < 6)
return -1;
first_byte = *p++;
memcpy(&tag, p, 4);
p += 4;
verneg = 0 == tag;
if (!verneg)
{
header_type = bin_2_header_type[ first_byte ];
if (!header_type)
return -1;
}
else
header_type = HETY_VERNEG;
packet_in->pi_header_type = header_type;
dcil = p[0] >> 4;
if (dcil)
dcil += 3;
scil = p[0] & 0xF;
if (scil)
scil += 3;
++p;
/* Chromium comments state that the client sends packets with destination
* CID of 8 bytes and source CID of 0 bytes and the server does it the
* other way around.
*
* XXX When IETF branch is merged, this check for Q044 will have to be
* moved to the pf_parse_packet_in_finish().
*/
if (is_server)
{
if (!(dcil == cid_len && scil == 0))
return -1;
}
else
{
if (!(dcil == 0 && scil == cid_len))
return -1;
}
const unsigned packet_len = 4;
/* XXX This checks both packet length or the first version of the version
* array in a version negotiation packet. This is because the sizes of
* the packet number field and the version tag are the same. The check
* will probably have to be split in the future.
*/
if (end - p < dcil + scil + packet_len)
return -1;
memcpy(&packet_in->pi_conn_id, p, cid_len);
p += cid_len;
packet_in->pi_flags |= PI_CONN_ID;
packet_in->pi_packno = 0;
if (!verneg)
{
state->pps_p = p;
state->pps_nbytes = packet_len;
p += packet_len;
packet_in->pi_quic_ver = 1;
if (is_server || HETY_0RTT != header_type)
packet_in->pi_nonce = 0;
else
{
packet_in->pi_nonce = p - packet_in->pi_data;
p += 32;
}
}
else
{
if ((end - p) & 3)
return -1;
state->pps_p = NULL;
state->pps_nbytes = 0;
packet_in->pi_quic_ver = p - packet_in->pi_data;
p = packet_in->pi_data + length;
packet_in->pi_nonce = 0;
}
packet_in->pi_header_sz = p - packet_in->pi_data;
packet_in->pi_frame_types = 0;
packet_in->pi_data_sz = length;
packet_in->pi_refcnt = 0;
packet_in->pi_received = 0;
return 0;
}
int
lsquic_iquic_parse_packet_in_short_begin (lsquic_packet_in_t *packet_in,
size_t length, int is_server, struct packin_parse_state *state)
{
const unsigned char *p = packet_in->pi_data;
const unsigned char *const pend = packet_in->pi_data + length;
unsigned cid_len = 8; /* XXX this will need to be passed in */
unsigned packet_len;
if ((*p & 0x30) != 0x30 || (*p & 3) == 3)
return -1;
packet_len = 1 << (*p & 3);
if (pend - p < 1 + cid_len + packet_len)
return -1;
++p;
if (is_server)
{
memcpy(&packet_in->pi_conn_id, p, cid_len);
p += cid_len;
packet_in->pi_flags = PI_CONN_ID;
}
/* We could read in the packet number here, but we choose to do it in
* the finish() call instead.
*/
packet_in->pi_packno = 0;
state->pps_p = p;
state->pps_nbytes = packet_len;
p += packet_len;
packet_in->pi_header_type = HETY_NOT_SET;
packet_in->pi_quic_ver = 0;
packet_in->pi_nonce = 0;
packet_in->pi_header_sz = p - packet_in->pi_data;
packet_in->pi_frame_types = 0;
packet_in->pi_data_sz = length;
packet_in->pi_refcnt = 0;
packet_in->pi_received = 0;
return 0;
}

View file

@ -56,4 +56,7 @@
*/
#define QTAG_NSTP TAG('N', 'S', 'T', 'P')
/* Stateless reset token. Used in Q044 and later. */
#define QTAG_SRST TAG('S', 'R', 'S', 'T')
#endif

View file

@ -45,6 +45,10 @@
#define MIN_RTO_DELAY 1000000 /* Microseconds */
#define N_NACKS_BEFORE_RETX 3
#define packet_out_total_sz(p) \
lsquic_packet_out_total_sz(ctl->sc_conn_pub->lconn, p)
#define packet_out_sent_sz(p) \
lsquic_packet_out_sent_sz(ctl->sc_conn_pub->lconn, p)
enum retx_mode {
RETX_MODE_HANDSHAKE,
@ -402,11 +406,11 @@ send_ctl_unacked_append (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
{
TAILQ_INSERT_TAIL(&ctl->sc_unacked_packets, packet_out, po_next);
ctl->sc_bytes_unacked_all += lsquic_packet_out_total_sz(packet_out);
ctl->sc_bytes_unacked_all += packet_out_total_sz(packet_out);
ctl->sc_n_in_flight_all += 1;
if (packet_out->po_frame_types & QFRAME_RETRANSMITTABLE_MASK)
{
ctl->sc_bytes_unacked_retx += lsquic_packet_out_total_sz(packet_out);
ctl->sc_bytes_unacked_retx += packet_out_total_sz(packet_out);
++ctl->sc_n_in_flight_retx;
}
}
@ -434,7 +438,7 @@ send_ctl_sched_Xpend_common (struct lsquic_send_ctl *ctl,
{
packet_out->po_flags |= PO_SCHED;
++ctl->sc_n_scheduled;
ctl->sc_bytes_scheduled += lsquic_packet_out_total_sz(packet_out);
ctl->sc_bytes_scheduled += packet_out_total_sz(packet_out);
lsquic_send_ctl_sanity_check(ctl);
}
@ -465,7 +469,7 @@ send_ctl_sched_remove (struct lsquic_send_ctl *ctl,
packet_out->po_flags &= ~PO_SCHED;
assert(ctl->sc_n_scheduled);
--ctl->sc_n_scheduled;
ctl->sc_bytes_scheduled -= lsquic_packet_out_total_sz(packet_out);
ctl->sc_bytes_scheduled -= packet_out_total_sz(packet_out);
lsquic_send_ctl_sanity_check(ctl);
}
@ -479,7 +483,7 @@ lsquic_send_ctl_sent_packet (lsquic_send_ctl_t *ctl,
packet_out->po_packno, lsquic_frame_types_to_str(frames,
sizeof(frames), packet_out->po_frame_types));
if (account)
ctl->sc_bytes_out -= lsquic_packet_out_total_sz(packet_out);
ctl->sc_bytes_out -= packet_out_total_sz(packet_out);
lsquic_senhist_add(&ctl->sc_senhist, packet_out->po_packno);
send_ctl_unacked_append(ctl, packet_out);
if (packet_out->po_frame_types & QFRAME_RETRANSMITTABLE_MASK)
@ -540,7 +544,7 @@ send_ctl_handle_lost_packet (lsquic_send_ctl_t *ctl,
unsigned packet_sz;
assert(ctl->sc_n_in_flight_all);
packet_sz = lsquic_packet_out_sent_sz(packet_out);
packet_sz = packet_out_sent_sz(packet_out);
send_ctl_unacked_remove(ctl, packet_out, packet_sz);
if (packet_out->po_flags & PO_ENCRYPTED)
send_ctl_release_enc_data(ctl, packet_out);
@ -740,7 +744,7 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
if (!now)
now = lsquic_time_now();
after_checks:
packet_sz = lsquic_packet_out_sent_sz(packet_out);
packet_sz = packet_out_sent_sz(packet_out);
ctl->sc_largest_acked_packno = packet_out->po_packno;
ctl->sc_largest_acked_sent_time = packet_out->po_sent;
send_ctl_unacked_remove(ctl, packet_out, packet_sz);
@ -856,7 +860,7 @@ lsquic_send_ctl_cleanup (lsquic_send_ctl_t *ctl)
while ((packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets)))
{
TAILQ_REMOVE(&ctl->sc_unacked_packets, packet_out, po_next);
ctl->sc_bytes_unacked_all -= lsquic_packet_out_total_sz(packet_out);
ctl->sc_bytes_unacked_all -= packet_out_total_sz(packet_out);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
--ctl->sc_n_in_flight_all;
}
@ -1008,7 +1012,7 @@ lsquic_send_ctl_sanity_check (const lsquic_send_ctl_t *ctl)
count = 0, bytes = 0;
TAILQ_FOREACH(packet_out, &ctl->sc_unacked_packets, po_next)
{
bytes += lsquic_packet_out_sent_sz(packet_out);
bytes += packet_out_sent_sz(packet_out);
++count;
}
assert(count == ctl->sc_n_in_flight_all);
@ -1018,7 +1022,7 @@ lsquic_send_ctl_sanity_check (const lsquic_send_ctl_t *ctl)
TAILQ_FOREACH(packet_out, &ctl->sc_scheduled_packets, po_next)
{
assert(packet_out->po_flags & PO_SCHED);
bytes += lsquic_packet_out_total_sz(packet_out);
bytes += packet_out_total_sz(packet_out);
++count;
}
assert(count == ctl->sc_n_scheduled);
@ -1090,7 +1094,7 @@ lsquic_send_ctl_next_packet_to_send (lsquic_send_ctl_t *ctl)
}
send_ctl_sched_remove(ctl, packet_out);
ctl->sc_bytes_out += lsquic_packet_out_total_sz(packet_out);
ctl->sc_bytes_out += packet_out_total_sz(packet_out);
return packet_out;
}
@ -1100,7 +1104,7 @@ lsquic_send_ctl_delayed_one (lsquic_send_ctl_t *ctl,
lsquic_packet_out_t *packet_out)
{
send_ctl_sched_prepend(ctl, packet_out);
ctl->sc_bytes_out -= lsquic_packet_out_total_sz(packet_out);
ctl->sc_bytes_out -= packet_out_total_sz(packet_out);
LSQ_DEBUG("packet %"PRIu64" has been delayed", packet_out->po_packno);
#if LSQUIC_SEND_STATS
++ctl->sc_stats.n_delayed;
@ -1139,7 +1143,7 @@ send_ctl_allocate_packet (lsquic_send_ctl_t *ctl, enum lsquic_packno_bits bits,
packet_out = lsquic_packet_out_new(&ctl->sc_enpub->enp_mm,
ctl->sc_conn_pub->packet_out_malo,
!(ctl->sc_flags & SC_TCID0), ctl->sc_pack_size, bits,
!(ctl->sc_flags & SC_TCID0), ctl->sc_conn_pub->lconn, bits,
ctl->sc_ver_neg->vn_tag, NULL);
if (!packet_out)
return NULL;

View file

@ -1369,8 +1369,10 @@ lsquic_stream_flush_threshold (const struct lsquic_stream *stream)
flags = bits << POBIT_SHIFT;
if (!(stream->conn_pub->lconn->cn_flags & LSCONN_TCID0))
flags |= PO_CONN_ID;
if (LSQUIC_STREAM_HANDSHAKE == stream->id)
flags |= PO_LONGHEAD;
packet_header_sz = lsquic_po_header_length(flags);
packet_header_sz = lsquic_po_header_length(stream->conn_pub->lconn, flags);
stream_header_sz = stream->conn_pub->lconn->cn_pf
->pf_calc_stream_frame_header_sz(stream->id, stream->tosend_off);
@ -1542,6 +1544,9 @@ stream_write_to_packet (struct frame_gen_ctx *fg_ctx, const size_t size)
packet_out = get_packet[hsk](send_ctl, need_at_least, stream);
if (!packet_out)
return SWTP_STOP;
if (hsk)
packet_out->po_header_type = stream->tosend_off == 0
? HETY_INITIAL : HETY_HANDSHAKE;
off = packet_out->po_data_sz;
len = pf->pf_gen_stream_frame(

View file

@ -11,6 +11,10 @@ static const unsigned char version_tags[N_LSQVER][4] =
[LSQVER_035] = { 'Q', '0', '3', '5', },
[LSQVER_039] = { 'Q', '0', '3', '9', },
[LSQVER_043] = { 'Q', '0', '4', '3', },
[LSQVER_044] = { 'Q', '0', '4', '4', },
#if LSQUIC_USE_Q098
[LSQVER_098] = { 'Q', '0', '9', '8', },
#endif
};
@ -58,11 +62,15 @@ const char *const lsquic_ver2str[N_LSQVER] = {
[LSQVER_035] = "Q035",
[LSQVER_039] = "Q039",
[LSQVER_043] = "Q043",
[LSQVER_044] = "Q044",
#if LSQUIC_USE_Q098
[LSQVER_098] = "Q098",
#endif
};
int
gen_ver_tags (unsigned char *buf, size_t bufsz, unsigned version_bitmask)
lsquic_gen_ver_tags (unsigned char *buf, size_t bufsz, unsigned version_bitmask)
{
unsigned n;
lsquic_ver_tag_t tag;

View file

@ -17,6 +17,6 @@ lsquic_tag2ver (uint32_t ver_tag);
extern const char *const lsquic_ver2str[];
int
gen_ver_tags (unsigned char *buf, size_t bufsz, unsigned versions);
lsquic_gen_ver_tags (unsigned char *buf, size_t bufsz, unsigned versions);
#endif