LSQUIC Client: Initial release

This commit is contained in:
Dmitri Tikhonov 2017-09-22 17:00:03 -04:00
commit 50aadb33c7
183 changed files with 99199 additions and 0 deletions

4
src/CMakeLists.txt Normal file
View file

@ -0,0 +1,4 @@
# Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE.
cmake_minimum_required(VERSION 2.8)
add_subdirectory(liblsquic)

View file

@ -0,0 +1,62 @@
# Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE.
SET(lsquic_STAT_SRCS
lsquic_alarmset.c
lsquic_conn.c
lsquic_full_conn.c
lsquic_chsk_stream.c
lsquic_engine.c
lsquic_parse_gquic_common.c
lsquic_parse_gquic_le.c
lsquic_parse_gquic_be.c
lsquic_parse_gquic_Q040.c
lsquic_packet_in.c
lsquic_packet_out.c
lsquic_crypto.c
lsquic_handshake.c
lsquic_logger.c
lsquic_malo.c
lsquic_mm.c
lsquic_rechist.c
lsquic_rtt.c
lsquic_send_ctl.c
lsquic_senhist.c
lsquic_cfcw.c
lsquic_sfcw.c
lsquic_stream.c
lsquic_util.c
lsquic_cubic.c
lsquic_set.c
lsquic_headers_stream.c
lsquic_frame_reader.c
lsquic_frame_writer.c
lsquic_crt_compress.c
lsquic_conn_hash.c
lsquic_eng_hist.c
lsquic_spi.c
lsquic_di_nocopy.c
lsquic_di_hash.c
lsquic_di_error.c
lsquic_global.c
lsquic_packet_common.c
lsquic_ev_log.c
lsquic_frame_common.c
lsquic_packints.c
lsquic_version.c
lsquic_pacer.c
lsquic_attq.c
lsquic_str.c
lsquic_arr.c
lsquic_hash.c
lsquic_hpack_common.c
lsquic_hpack_dec.c
lsquic_hpack_enc.c
lsquic_xxhash.c
lsquic_buf.c
)
add_library(lsquic STATIC ${lsquic_STAT_SRCS} )
link_directories(${PROJECT_SOURCE_DIR}/ssl/ /usr/local/lib)

View file

@ -0,0 +1,136 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/* Copyright (c) 2015 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE.chrome file.
*/
/* This file contains common certificates. It's designed to be #included in
* another file, in a namespace. */
/* Updated for C style, David Shue */
/*
* Replace "kDERCert" to "der_cert2_"
*
*/
#include <stdint.h>
#include "common_cert_set_2a.inc"
#include "common_cert_set_2b.inc"
#define common_certs2_num 54
static const unsigned char * const common_certs2[common_certs2_num] = {
der_cert2_0,
der_cert2_1,
der_cert2_2,
der_cert2_3,
der_cert2_4,
der_cert2_5,
der_cert2_6,
der_cert2_7,
der_cert2_8,
der_cert2_9,
der_cert2_10,
der_cert2_11,
der_cert2_12,
der_cert2_13,
der_cert2_14,
der_cert2_15,
der_cert2_16,
der_cert2_17,
der_cert2_18,
der_cert2_19,
der_cert2_20,
der_cert2_21,
der_cert2_22,
der_cert2_23,
der_cert2_24,
der_cert2_25,
der_cert2_26,
der_cert2_27,
der_cert2_28,
der_cert2_29,
der_cert2_30,
der_cert2_31,
der_cert2_32,
der_cert2_33,
der_cert2_34,
der_cert2_35,
der_cert2_36,
der_cert2_37,
der_cert2_38,
der_cert2_39,
der_cert2_40,
der_cert2_41,
der_cert2_42,
der_cert2_43,
der_cert2_44,
der_cert2_45,
der_cert2_46,
der_cert2_47,
der_cert2_48,
der_cert2_49,
der_cert2_50,
der_cert2_51,
der_cert2_52,
der_cert2_53,
};
static const size_t common_certs2_lens[common_certs2_num] = {
897,
911,
985,
1012,
1049,
1062,
1065,
1071,
1084,
1096,
1097,
1105,
1107,
1117,
1127,
1133,
1136,
1138,
1153,
1171,
1172,
1176,
1182,
1188,
1194,
1203,
1205,
1206,
1210,
1222,
1226,
1236,
1236,
1236,
1238,
1256,
1270,
1280,
1283,
1284,
1287,
1315,
1327,
1340,
1418,
1447,
1509,
1520,
1570,
1581,
1592,
1628,
1632,
1770,
};
#define common_certs2_hash UINT64_C(0xe81a92926081e801)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,126 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/* Copyright (c) 2015 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE.chrome file.
*/
/* Updated for C style, David Shue */
/*
* Replace "kDERCert" to "der_cert3_"
*
*/
#include "common_cert_set_3a.inc"
#include "common_cert_set_3b.inc"
#define common_certs3_num 52
static const unsigned char* const common_certs3[common_certs3_num] = {
der_cert3_0,
der_cert3_1,
der_cert3_2,
der_cert3_3,
der_cert3_4,
der_cert3_5,
der_cert3_6,
der_cert3_7,
der_cert3_8,
der_cert3_9,
der_cert3_10,
der_cert3_11,
der_cert3_12,
der_cert3_13,
der_cert3_14,
der_cert3_15,
der_cert3_16,
der_cert3_17,
der_cert3_18,
der_cert3_19,
der_cert3_20,
der_cert3_21,
der_cert3_22,
der_cert3_23,
der_cert3_24,
der_cert3_25,
der_cert3_26,
der_cert3_27,
der_cert3_28,
der_cert3_29,
der_cert3_30,
der_cert3_31,
der_cert3_32,
der_cert3_33,
der_cert3_34,
der_cert3_35,
der_cert3_36,
der_cert3_37,
der_cert3_38,
der_cert3_39,
der_cert3_40,
der_cert3_41,
der_cert3_42,
der_cert3_43,
der_cert3_44,
der_cert3_45,
der_cert3_46,
der_cert3_47,
der_cert3_48,
der_cert3_49,
der_cert3_50,
der_cert3_51,
};
static const size_t common_certs3_lens[common_certs3_num] = {
897,
911,
1012,
1049,
1065,
1096,
1097,
1101,
1105,
1105,
1107,
1117,
1127,
1133,
1136,
1138,
1139,
1145,
1149,
1153,
1167,
1172,
1174,
1174,
1176,
1188,
1194,
1196,
1203,
1205,
1206,
1208,
1209,
1210,
1222,
1227,
1236,
1236,
1238,
1283,
1284,
1287,
1298,
1315,
1327,
1340,
1357,
1418,
1447,
1509,
1513,
1632,
};
#define common_certs3_hash UINT64_C(0x918215a28680ed7e)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

38
src/liblsquic/fiu-local.h Normal file
View file

@ -0,0 +1,38 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/* libfiu - Fault Injection in Userspace
*
* This header, part of libfiu, is meant to be included in your project to
* avoid having libfiu as a mandatory build-time dependency.
*
* You can add it to your project, and #include it instead of fiu.h.
* The real fiu.h will be used only when FIU_ENABLE is defined.
*
* This header, as the rest of libfiu, is in the public domain.
*
* You can find more information about libfiu at
* http://blitiri.com.ar/p/libfiu.
*/
#ifndef _FIU_LOCAL_H
#define _FIU_LOCAL_H
/* Only define the stubs when fiu is disabled, otherwise use the real fiu.h
* header */
#ifndef FIU_ENABLE
#define fiu_init(flags) 0
#define fiu_fail(name) 0
#define fiu_failinfo() NULL
#define fiu_do_on(name, action)
#define fiu_exit_on(name)
#define fiu_return_on(name, retval)
#else
#include <fiu.h>
#endif /* FIU_ENABLE */
#endif /* _FIU_LOCAL_H */

View file

@ -0,0 +1,54 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_alarmset.c -- A set of alarms
*/
#include <assert.h>
#include <string.h>
#include "lsquic_types.h"
#include "lsquic_alarmset.h"
#define LSQUIC_LOGGER_MODULE LSQLM_ALARMSET
#define LSQUIC_LOG_CONN_ID alset->as_cid
#include "lsquic_logger.h"
void
lsquic_alarmset_init (lsquic_alarmset_t *alset, lsquic_cid_t cid)
{
alset->as_cid = cid;
alset->as_armed_set = 0;
}
void
lsquic_alarmset_init_alarm (lsquic_alarmset_t *alset, enum alarm_id al_id,
lsquic_alarm_cb_f callback, void *cb_ctx)
{
alset->as_alarms[ al_id ].callback = callback;
alset->as_alarms[ al_id ].cb_ctx = cb_ctx;
}
void
lsquic_alarmset_ring_expired (lsquic_alarmset_t *alset, lsquic_time_t now)
{
enum alarm_id_bit armed_set;
enum alarm_id al_id;
for (al_id = 0, armed_set = alset->as_armed_set;
al_id < MAX_LSQUIC_ALARMS && armed_set;
armed_set &= ~(1 << al_id), ++al_id)
if (armed_set & (1 << al_id))
{
if (alset->as_expiry[al_id] < now)
{
alset->as_armed_set &= ~(1 << al_id);
LSQ_INFO("ring expired alarm %d", al_id);
alset->as_alarms[al_id].callback(
alset->as_alarms[al_id].cb_ctx,
alset->as_expiry[al_id], now);
}
}
}

View file

@ -0,0 +1,70 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_alarmset.h -- A set of alarms
*/
#ifndef LSQUIC_ALARM_H
#define LSQUIC_ALARM_H 1
#include "lsquic_int_types.h"
typedef void (*lsquic_alarm_cb_f)(void *cb_ctx,
lsquic_time_t expiry, lsquic_time_t now);
typedef struct lsquic_alarm {
lsquic_alarm_cb_f callback;
void *cb_ctx;
} lsquic_alarm_t;
enum alarm_id {
AL_HANDSHAKE,
AL_RETX,
AL_ACK,
AL_PING,
AL_IDLE,
MAX_LSQUIC_ALARMS
};
enum alarm_id_bit {
ALBIT_HANDSHAKE = 1 << AL_HANDSHAKE,
ALBIT_RETX = 1 << AL_RETX,
ALBIT_ACK = 1 << AL_ACK,
ALBIT_PING = 1 << AL_PING,
ALBIT_IDLE = 1 << AL_IDLE,
};
typedef struct lsquic_alarmset {
enum alarm_id_bit as_armed_set;
lsquic_time_t as_expiry[MAX_LSQUIC_ALARMS];
lsquic_cid_t as_cid; /* Used for logging */
struct lsquic_alarm as_alarms[MAX_LSQUIC_ALARMS];
} lsquic_alarmset_t;
void
lsquic_alarmset_init (lsquic_alarmset_t *, lsquic_cid_t);
void
lsquic_alarmset_init_alarm (lsquic_alarmset_t *, enum alarm_id,
lsquic_alarm_cb_f, void *cb_ctx);
#define lsquic_alarmset_set(alarmset, al_id, exp) do { \
(alarmset)->as_armed_set |= 1 << (al_id); \
(alarmset)->as_expiry[al_id] = exp; \
} while (0)
#define lsquic_alarmset_unset(alarmset, al_id) do { \
(alarmset)->as_armed_set &= ~(1 << (al_id)); \
} while (0)
#define lsquic_alarmset_is_set(alarmset, al_id) \
((alarmset)->as_armed_set & (1 << (al_id)))
/* Timers "fire," alarms "ring." */
void
lsquic_alarmset_ring_expired (lsquic_alarmset_t *, lsquic_time_t now);
#endif

View file

@ -0,0 +1,46 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_arr.c
*/
#include "lsquic_arr.h"
int
lsquic_arr_push (struct lsquic_arr *arr, uintptr_t val)
{
uintptr_t *new_els;
unsigned n;
if (arr->off + arr->nelem < arr->nalloc)
{
arr->els[arr->off + arr->nelem] = val;
++arr->nelem;
return 0;
}
if (arr->off > arr->nalloc / 2)
{
memmove(arr->els, arr->els + arr->off,
sizeof(arr->els[0]) * arr->nelem);
arr->off = 0;
arr->els[arr->nelem] = val;
++arr->nelem;
return 0;
}
if (arr->nalloc)
n = arr->nalloc * 2;
else
n = 64;
new_els = malloc(n * sizeof(arr->els[0]));
if (!new_els)
return -1;
memcpy(new_els, arr->els + arr->off, sizeof(arr->els[0]) * arr->nelem);
free(arr->els);
arr->off = 0;
arr->els = new_els;
arr->nalloc = n;
arr->els[arr->off + arr->nelem] = val;
++arr->nelem;
return 0;
}

View file

@ -0,0 +1,64 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_arr.h -- Array
*/
#ifndef LSQUIC_ARR_H
#define LSQUIC_ARR_H 1
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
struct lsquic_arr
{
unsigned nalloc,
nelem,
off;
uintptr_t *els;
};
#define lsquic_arr_init(a) do { \
memset((a), 0, sizeof(*(a))); \
} while (0)
#define lsquic_arr_cleanup(a) do { \
free((a)->els); \
memset((a), 0, sizeof(*(a))); \
} while (0)
#define lsquic_arr_clear(a) do { \
(a)->off = 0; \
(a)->nelem = 0; \
} while (0)
#define lsquic_arr_get(a, i) ( \
assert((i) < (a)->nelem), \
(a)->els[(a)->off + (i)] \
)
#define lsquic_arr_shift(a) ( \
assert((a)->nelem > 0), \
(a)->nelem -= 1, \
(a)->els[(a)->off++] \
)
#define lsquic_arr_peek(a) ( \
assert((a)->nelem > 0), \
(a)->els[(a)->off] \
)
#define lsquic_arr_pop(a) ( \
assert((a)->nelem > 0), \
(a)->nelem -= 1, \
(a)->els[(a)->off + (a)->nelem] \
)
#define lsquic_arr_count(a) (+(a)->nelem)
int
lsquic_arr_push (struct lsquic_arr *, uintptr_t);
#endif

282
src/liblsquic/lsquic_attq.c Normal file
View file

@ -0,0 +1,282 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_attq.c -- Advisory Tick Time Queue
*
* This is a collection of connections kept in a binary heap, the top
* element having the minimum advsory time. To speed up removal, each
* element has an index it has in the heap array. The index is updated
* as elements are moved around in the array when heap is updated.
*/
#include <assert.h>
#include <stdlib.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_attq.h"
#include "lsquic_malo.h"
#include "lsquic_conn.h"
struct attq
{
struct malo *aq_elem_malo;
struct attq_elem **aq_heap;
lsquic_time_t aq_min;
unsigned aq_nelem;
unsigned aq_nalloc;
};
struct attq *
attq_create (void)
{
struct attq *q;
struct malo *malo;
malo = lsquic_malo_create(sizeof(struct attq_elem));
if (!malo)
return NULL;
q = calloc(1, sizeof(*q));
if (!q)
{
lsquic_malo_destroy(malo);
return NULL;
}
q->aq_elem_malo = malo;
return q;
}
void
attq_destroy (struct attq *q)
{
lsquic_malo_destroy(q->aq_elem_malo);
free(q->aq_heap);
free(q);
}
#define AE_PARENT(i) ((i - 1) / 2)
#define AE_LCHILD(i) (2 * i + 1)
#define AE_RCHILD(i) (2 * i + 2)
#ifndef NDEBUG
static void
attq_verify (struct attq *q)
{
unsigned i;
for (i = 0; i < q->aq_nelem; ++i)
{
assert(q->aq_heap[i]->ae_heap_idx == i);
if (AE_LCHILD(i) < q->aq_nelem)
assert(q->aq_heap[i]->ae_adv_time <=
q->aq_heap[AE_LCHILD(i)]->ae_adv_time);
if (AE_RCHILD(i) < q->aq_nelem)
assert(q->aq_heap[i]->ae_adv_time <=
q->aq_heap[AE_RCHILD(i)]->ae_adv_time);
}
}
#else
#define attq_verify(q)
#endif
int
attq_maybe_add (struct attq *q, struct lsquic_conn *conn,
lsquic_time_t advisory_time)
{
if (advisory_time < q->aq_min)
return 1;
else
return attq_add(q, conn, advisory_time);
}
static void
attq_swap (struct attq *q, unsigned a, unsigned b)
{
struct attq_elem *el;
el = q->aq_heap[ a ];
q->aq_heap[ a ] = q->aq_heap[ b ];
q->aq_heap[ b ] = el;
q->aq_heap[ a ]->ae_heap_idx = a;
q->aq_heap[ b ]->ae_heap_idx = b;
}
int
attq_add (struct attq *q, struct lsquic_conn *conn,
lsquic_time_t advisory_time)
{
struct attq_elem *el, **heap;
unsigned n, i;
if (q->aq_nelem >= q->aq_nalloc)
{
if (q->aq_nalloc > 0)
n = q->aq_nalloc * 2;
else
n = 8;
heap = realloc(q->aq_heap, n * sizeof(q->aq_heap[0]));
if (!heap)
return -1;
q->aq_heap = heap;
q->aq_nalloc = n;
}
el = lsquic_malo_get(q->aq_elem_malo);
if (!el)
return -1;
el->ae_adv_time = advisory_time;
/* The only place linkage between conn and attq_elem occurs: */
el->ae_conn = conn;
conn->cn_attq_elem = el;
el->ae_heap_idx = q->aq_nelem;
q->aq_heap[ q->aq_nelem++ ] = el;
i = q->aq_nelem - 1;
while (i > 0 && q->aq_heap[ AE_PARENT(i) ]->ae_adv_time >=
q->aq_heap[ i ]->ae_adv_time)
{
attq_swap(q, i, AE_PARENT(i));
i = AE_PARENT(i);
}
attq_verify(q);
return 0;
}
struct lsquic_conn *
attq_pop (struct attq *q, lsquic_time_t cutoff)
{
struct lsquic_conn *conn;
struct attq_elem *el;
if (q->aq_nelem == 0)
return NULL;
el = q->aq_heap[0];
if (el->ae_adv_time >= cutoff)
return NULL;
conn = el->ae_conn;
attq_remove(q, conn);
return conn;
}
static void
attq_heapify (struct attq *q, unsigned i)
{
unsigned smallest;
assert(i < q->aq_nelem);
if (AE_LCHILD(i) < q->aq_nelem)
{
if (q->aq_heap[ AE_LCHILD(i) ]->ae_adv_time <
q->aq_heap[ i ]->ae_adv_time)
smallest = AE_LCHILD(i);
else
smallest = i;
if (AE_RCHILD(i) < q->aq_nelem &&
q->aq_heap[ AE_RCHILD(i) ]->ae_adv_time <
q->aq_heap[ smallest ]->ae_adv_time)
smallest = AE_RCHILD(i);
}
else
smallest = i;
if (smallest != i)
{
attq_swap(q, smallest, i);
attq_heapify(q, smallest);
}
}
void
attq_remove (struct attq *q, struct lsquic_conn *conn)
{
struct attq_elem *el;
unsigned idx;
el = conn->cn_attq_elem;
idx = el->ae_heap_idx;
assert(q->aq_nelem > 0);
assert(q->aq_heap[idx] == el);
assert(conn->cn_attq_elem == el);
conn->cn_attq_elem = NULL;
lsquic_malo_put(el);
q->aq_heap[ idx ] = q->aq_heap[ --q->aq_nelem ];
q->aq_heap[ idx ]->ae_heap_idx = idx;
if (idx > 0 && q->aq_heap[ idx ]->ae_adv_time <
q->aq_heap[ AE_PARENT(idx) ]->ae_adv_time)
{
do
{
attq_swap(q, idx, AE_PARENT(idx));
idx = AE_PARENT(idx);
}
while (idx > 0 && q->aq_heap[ idx ]->ae_adv_time <
q->aq_heap[ AE_PARENT(idx) ]->ae_adv_time);
}
else if (q->aq_nelem > 1 && idx < q->aq_nelem)
attq_heapify(q, idx);
attq_verify(q);
}
unsigned
attq_count_before (struct attq *q, lsquic_time_t cutoff)
{
unsigned level, total_count, level_count, i, level_max;
total_count = 0;
for (i = 0, level = 0;; ++level)
{
level_count = 0;
level_max = i + (1U << level);
for ( ; i < level_max && i < q->aq_nelem; ++i)
level_count += q->aq_heap[i]->ae_adv_time < cutoff;
total_count += level_count;
if (level_count < (1U << level))
return total_count;
}
assert(0);
}
const lsquic_time_t *
attq_next_time (struct attq *q)
{
if (q->aq_nelem > 0)
return &q->aq_heap[0]->ae_adv_time;
else
return NULL;
}
lsquic_time_t
attq_set_min (struct attq *q, lsquic_time_t new_min)
{
lsquic_time_t prev_value;
prev_value = q->aq_min;
q->aq_min = new_min;
return prev_value;
}

View file

@ -0,0 +1,54 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_attq.h -- Advisory Tick Time Queue
*/
#ifndef LSQUIC_ATTQ_H
#define LSQUIC_ATTQ_H
struct attq;
struct lsquic_conn;
/* The extra level of indirection is done for speed: swapping heap elements
* does not need memory associated with lsquic_conn.
*/
struct attq_elem
{
struct lsquic_conn *ae_conn;
lsquic_time_t ae_adv_time;
unsigned ae_heap_idx;
};
struct attq *
attq_create (void);
void
attq_destroy (struct attq *);
/* Return 1 if advisory_time is too small, 0 on success, -1 on failure */
int
attq_maybe_add (struct attq *, struct lsquic_conn *,
lsquic_time_t advisory_time);
/* Return 0 on success, -1 on failure (malloc) */
int
attq_add (struct attq *, struct lsquic_conn *, lsquic_time_t advisory_time);
void
attq_remove (struct attq *, struct lsquic_conn *);
struct lsquic_conn *
attq_pop (struct attq *, lsquic_time_t cutoff);
unsigned
attq_count_before (struct attq *, lsquic_time_t cutoff);
const lsquic_time_t *
attq_next_time (struct attq *);
lsquic_time_t
attq_set_min (struct attq *, lsquic_time_t new_min);
#endif

View file

@ -0,0 +1,89 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_buf.c
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "lsquic_buf.h"
static int
lsquic_buf_reserve (struct lsquic_buf *buf, int size)
{
char *new_buf;
if (buf->bufend - buf->buf == size)
return 0;
new_buf = realloc(buf->buf, size);
if (new_buf != 0 || size == 0)
{
buf->end = new_buf + (buf->end - buf->buf);
buf->buf = new_buf;
buf->bufend = new_buf + size;
if (buf->end > buf->bufend)
buf->end = buf->bufend;
return 0;
}
else
return -1;
}
static int
lsquic_buf_grow (struct lsquic_buf *buf, int size)
{
size = ((size + 511) >> 9) << 9;
return lsquic_buf_reserve(buf, lsquic_buf_capacity(buf) + size);
}
struct lsquic_buf *
lsquic_buf_create (int size)
{
struct lsquic_buf *buf;
buf = calloc(1, sizeof(*buf));
if (!buf)
return NULL;
if (0 != lsquic_buf_reserve(buf, size))
{
free(buf);
return NULL;
}
return buf;
}
int
lsquic_buf_append (struct lsquic_buf *buf, const char *str, int size)
{
if (buf == NULL || size < 0)
{
errno = EINVAL;
return -1;
}
if (size == 0)
return 0;
if (size > lsquic_buf_avail(buf))
{
if (lsquic_buf_grow(buf, size - lsquic_buf_avail(buf)) != 0)
return -1;
}
memmove(buf->end, str, size);
buf->end += size;
return size;
}
void
lsquic_buf_destroy (struct lsquic_buf *buf)
{
free(buf->buf);
free(buf);
}

View file

@ -0,0 +1,35 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_buf.h
*/
#ifndef LSQUIC_BUF_H
#define LSQUIC_BUF_H 1
struct lsquic_buf
{
char *buf, *end, *bufend;
};
struct lsquic_buf *
lsquic_buf_create (int);
int
lsquic_buf_append (struct lsquic_buf *, const char *, int);
#define lsquic_buf_begin(buf_) ((buf_)->buf)
#define lsquic_buf_size(buf_) ((buf_)->end - (buf_)->buf)
#define lsquic_buf_avail(buf_) ((buf_)->bufend - (buf_)->end)
#define lsquic_buf_capacity(buf_) ((buf_)->bufend - (buf_)->buf)
#define lsquic_buf_clear(buf_) do { \
(buf_)->end = (buf_)->buf; \
} while (0)
void
lsquic_buf_destroy (struct lsquic_buf *);
#endif

113
src/liblsquic/lsquic_cfcw.c Normal file
View file

@ -0,0 +1,113 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_rtt.h"
#include "lsquic_conn_flow.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_conn_public.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#include "lsquic_util.h"
#include "lsquic_conn.h"
#include "lsquic_ev_log.h"
#define LSQUIC_LOGGER_MODULE LSQLM_CFCW
#define LSQUIC_LOG_CONN_ID fc->cf_conn_pub->lconn->cn_cid
#include "lsquic_logger.h"
void
lsquic_cfcw_init (struct lsquic_cfcw *fc, struct lsquic_conn_public *cpub,
unsigned max_recv_window)
{
memset(fc, 0, sizeof(*fc));
fc->cf_max_recv_win = max_recv_window;
fc->cf_conn_pub = cpub;
(void) lsquic_cfcw_fc_offsets_changed(fc);
}
static void
cfcw_maybe_increase_max_window (struct lsquic_cfcw *fc)
{
unsigned new_max_window;
new_max_window = fc->cf_max_recv_win * 2;
/* Do not increase past explicitly specified maximum */
if (new_max_window > fc->cf_conn_pub->enpub->enp_settings.es_max_cfcw)
new_max_window = fc->cf_conn_pub->enpub->enp_settings.es_max_cfcw;
if (new_max_window > fc->cf_max_recv_win)
{
LSQ_DEBUG("max window increase %u -> %u", fc->cf_max_recv_win,
new_max_window);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID,
"max CFCW increase %u -> %u", fc->cf_max_recv_win,
new_max_window);
fc->cf_max_recv_win = new_max_window;
}
else
LSQ_DEBUG("max window could use an increase, but we're stuck "
"at %u", fc->cf_max_recv_win);
}
int
lsquic_cfcw_fc_offsets_changed (struct lsquic_cfcw *fc)
{
lsquic_time_t now, since_last_update, srtt;
if (fc->cf_recv_off - fc->cf_read_off >= fc->cf_max_recv_win / 2)
return 0;
now = lsquic_time_now();
since_last_update = now - fc->cf_last_updated;
fc->cf_last_updated = now;
srtt = lsquic_rtt_stats_get_srtt(&fc->cf_conn_pub->rtt_stats);
if (since_last_update < srtt * 2)
cfcw_maybe_increase_max_window(fc);
fc->cf_recv_off = fc->cf_read_off + fc->cf_max_recv_win;
LSQ_DEBUG("recv_off changed: read_off: %"PRIu64"; recv_off: %"
PRIu64"", fc->cf_read_off, fc->cf_recv_off);
return 1;
}
int
lsquic_cfcw_incr_max_recv_off (struct lsquic_cfcw *fc, uint64_t incr)
{
if (fc->cf_max_recv_off + incr <= fc->cf_recv_off)
{
fc->cf_max_recv_off += incr;
LSQ_DEBUG("max_recv_off goes from %"PRIu64" to %"PRIu64"",
fc->cf_max_recv_off - incr, fc->cf_max_recv_off);
return 1;
}
else
{
LSQ_WARN("flow control violation: received at offset %"PRIu64", while "
"flow control receive offset is %"PRIu64,
fc->cf_max_recv_off + incr, fc->cf_recv_off);
return 0;
}
}
void
lsquic_cfcw_incr_read_off (struct lsquic_cfcw *fc, uint64_t incr)
{
fc->cf_read_off += incr;
LSQ_DEBUG("read_off goes from %"PRIu64" to %"PRIu64,
fc->cf_read_off - incr, fc->cf_read_off);
}

View file

@ -0,0 +1,189 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* Stream/crypto handshake adapter for the client side.
*
* The client composes CHLO, writes it to the stream, and wait for the
* server response, which it processes.
*/
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "lsquic_int_types.h"
#include "lsquic.h"
#include "lsquic_handshake.h"
#include "lsquic_chsk_stream.h"
#include "lsquic_ver_neg.h"
#include "lsquic_conn.h"
#include "lsquic_mm.h"
#define LSQUIC_LOGGER_MODULE LSQLM_HSK_ADAPTER
#define LSQUIC_LOG_CONN_ID lsquic_conn_id(c_hsk->lconn)
#include "lsquic_logger.h"
static lsquic_stream_ctx_t *
hsk_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
{
struct client_hsk_ctx *const c_hsk = stream_if_ctx;
LSQ_DEBUG("stream created");
lsquic_stream_wantwrite(stream, 1);
return (void *) c_hsk;
}
static void
hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
{
struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
ssize_t nread;
int s;
if (!c_hsk->buf_in)
{
c_hsk->buf_in = lsquic_mm_get_16k(c_hsk->mm);
if (!c_hsk->buf_in)
{
LSQ_WARN("could not get buffer: %s", strerror(errno));
lsquic_stream_wantread(stream, 0);
lsquic_conn_close(c_hsk->lconn);
return;
}
c_hsk->buf_sz = 16 * 1024;
c_hsk->buf_off = 0;
}
nread = lsquic_stream_read(stream, c_hsk->buf_in + c_hsk->buf_off,
c_hsk->buf_sz - c_hsk->buf_off);
if (nread <= 0)
{
if (nread < 0)
LSQ_INFO("Could not read from handshake stream: %s",
strerror(errno));
else
LSQ_INFO("Handshake stream closed (odd)");
lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0);
lsquic_conn_close(c_hsk->lconn);
return;
}
c_hsk->buf_off += nread;
s = handle_chlo_reply(c_hsk->lconn->cn_enc_session,
c_hsk->buf_in, c_hsk->buf_off);
LSQ_DEBUG("handle_chlo_reply returned %d", s);
switch (s)
{
case DATA_NOT_ENOUGH:
if (c_hsk->buf_off < c_hsk->buf_sz)
LSQ_INFO("not enough server response has arrived, continue "
"buffering");
else
{
LSQ_INFO("read in %u bytes of server response, and it is still "
"not enough: giving up", c_hsk->buf_off);
lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0);
lsquic_conn_close(c_hsk->lconn);
}
break;
case DATA_NO_ERROR:
lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0);
if (is_hs_done(c_hsk->lconn->cn_enc_session))
{
LSQ_DEBUG("handshake is complete, inform connection");
c_hsk->lconn->cn_if->ci_handshake_done(c_hsk->lconn);
}
else
{
LSQ_DEBUG("handshake not yet complete, will generate another "
"message");
lsquic_stream_wantwrite(stream, 1);
}
break;
default:
LSQ_WARN("handle_chlo_reply returned unknown value %d", s);
case DATA_FORMAT_ERROR:
LSQ_INFO("handle_chlo_reply returned an error");
break;
}
}
/* In this function, we assume that we can write the whole message in one
* shot. Otherwise, this is an error.
*/
static void
hsk_client_on_write (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
{
struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
unsigned char *buf;
size_t len;
ssize_t nw;
lsquic_stream_wantwrite(stream, 0);
buf = lsquic_mm_get_4k(c_hsk->mm);
if (!buf)
{
LSQ_WARN("cannot allocate buffer: %s", strerror(errno));
lsquic_conn_close(c_hsk->lconn);
return;
}
len = 4 * 1024;
if (0 != gen_chlo(c_hsk->lconn->cn_enc_session, c_hsk->ver_neg->vn_ver,
buf, &len))
{
LSQ_WARN("cannot create CHLO message");
lsquic_mm_put_4k(c_hsk->mm, buf);
lsquic_conn_close(c_hsk->lconn);
return;
}
nw = lsquic_stream_write(stream, buf, len);
lsquic_mm_put_4k(c_hsk->mm, buf);
if (nw < 0)
LSQ_INFO("error writing to stream: %s", strerror(errno));
else if ((size_t) nw == len)
{
LSQ_INFO("wrote %zd bytes of CHLO to stream", nw);
lsquic_stream_flush(stream);
lsquic_stream_wantread(stream, 1);
}
else
LSQ_INFO("could only write %zd bytes to stream instead of %zd",
nw, len);
}
static void
hsk_client_on_close (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
{
struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
if (c_hsk->buf_in)
lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
LSQ_DEBUG("stream closed");
}
const struct lsquic_stream_if lsquic_client_hsk_stream_if =
{
.on_new_stream = hsk_client_on_new_stream,
.on_read = hsk_client_on_read,
.on_write = hsk_client_on_write,
.on_close = hsk_client_on_close,
};

View file

@ -0,0 +1,24 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* Stream/crypto handshake adapter for the client side.
*/
#ifndef LSQUIC_CHSK_STREAM_H
#define LSQUIC_CHSK_STREAM_H 1
struct lsquic_conn;
struct lsquic_mm;
struct ver_neg;
struct client_hsk_ctx {
struct lsquic_conn *lconn;
struct lsquic_mm *mm;
const struct ver_neg *ver_neg;
unsigned char *buf_in; /* Server response may have to be buffered */
unsigned buf_sz, /* Total number of bytes in `buf_in' */
buf_off; /* Number of bytes read into `buf_in' */
};
extern const struct lsquic_stream_if lsquic_client_hsk_stream_if;
#endif

148
src/liblsquic/lsquic_conn.c Normal file
View file

@ -0,0 +1,148 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <inttypes.h>
#include <string.h>
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_conn.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_handshake.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#include "lsquic_ev_log.h"
#include "lsquic_logger.h"
lsquic_cid_t
lsquic_conn_id (const lsquic_conn_t *lconn)
{
return lconn->cn_cid;
}
void *
lsquic_conn_get_peer_ctx( const lsquic_conn_t *lconn)
{
return lconn->cn_peer_ctx;
}
void
lsquic_conn_record_sockaddr (lsquic_conn_t *lconn,
const struct sockaddr *local, const struct sockaddr *peer)
{
assert(local->sa_family == peer->sa_family);
switch (local->sa_family)
{
case AF_INET:
lconn->cn_flags |= LSCONN_HAS_PEER_SA|LSCONN_HAS_LOCAL_SA;
memcpy(lconn->cn_local_addr, local, sizeof(struct sockaddr_in));
memcpy(lconn->cn_peer_addr, peer, sizeof(struct sockaddr_in));
break;
case AF_INET6:
lconn->cn_flags |= LSCONN_HAS_PEER_SA|LSCONN_HAS_LOCAL_SA;
memcpy(lconn->cn_local_addr, local, sizeof(struct sockaddr_in6));
memcpy(lconn->cn_peer_addr, peer, sizeof(struct sockaddr_in6));
break;
}
}
void
lsquic_conn_record_peer_sa (lsquic_conn_t *lconn, const struct sockaddr *peer)
{
switch (peer->sa_family)
{
case AF_INET:
lconn->cn_flags |= LSCONN_HAS_PEER_SA;
memcpy(lconn->cn_peer_addr, peer, sizeof(struct sockaddr_in));
break;
case AF_INET6:
lconn->cn_flags |= LSCONN_HAS_PEER_SA;
memcpy(lconn->cn_peer_addr, peer, sizeof(struct sockaddr_in6));
break;
}
}
int
lsquic_conn_get_sockaddr (const lsquic_conn_t *lconn,
const struct sockaddr **local, const struct sockaddr **peer)
{
if ((lconn->cn_flags & (LSCONN_HAS_PEER_SA|LSCONN_HAS_LOCAL_SA)) ==
(LSCONN_HAS_PEER_SA|LSCONN_HAS_LOCAL_SA))
{
*local = (struct sockaddr *) lconn->cn_local_addr;
*peer = (struct sockaddr *) lconn->cn_peer_addr;
return 0;
}
else
return -1;
}
int
lsquic_conn_copy_and_release_pi_data (const lsquic_conn_t *conn,
struct lsquic_engine_public *enpub, lsquic_packet_in_t *packet_in)
{
assert(!(packet_in->pi_flags & PI_OWN_DATA));
/* The size should be guarded in lsquic_engine_packet_in(): */
assert(packet_in->pi_data_sz <= QUIC_MAX_PACKET_SZ);
unsigned char *const copy = lsquic_mm_get_1370(&enpub->enp_mm);
if (!copy)
{
LSQ_WARN("cannot allocate memory to copy incoming packet data");
return -1;
}
memcpy(copy, packet_in->pi_data, packet_in->pi_data_sz);
packet_in->pi_data = copy;
packet_in->pi_flags |= PI_OWN_DATA;
return 0;
}
int
lsquic_conn_decrypt_packet (lsquic_conn_t *lconn,
struct lsquic_engine_public *enpub,
lsquic_packet_in_t *packet_in)
{
size_t header_len, data_len;
size_t out_len = 0;
unsigned char *copy = lsquic_mm_get_1370(&enpub->enp_mm);
if (!copy)
{
LSQ_WARN("cannot allocate memory to copy incoming packet data");
return -1;
}
header_len = packet_in->pi_header_sz;
data_len = packet_in->pi_data_sz - packet_in->pi_header_sz;
if (0 == lsquic_dec(lconn->cn_enc_session, lconn->cn_version, 0,
packet_in->pi_packno, packet_in->pi_data,
&header_len, data_len,
lsquic_packet_in_nonce(packet_in),
copy, 1370, &out_len))
{
assert(header_len + out_len <= 1370);
if (packet_in->pi_flags & PI_OWN_DATA)
lsquic_mm_put_1370(&enpub->enp_mm, packet_in->pi_data);
packet_in->pi_data = copy;
packet_in->pi_flags |= PI_OWN_DATA | PI_DECRYPTED;
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);
return 0;
}
else
{
lsquic_mm_put_1370(&enpub->enp_mm, copy);
EV_LOG_CONN_EVENT(lconn->cn_cid, "could not decrypt packet %"PRIu64,
packet_in->pi_packno);
return -1;
}
}

130
src/liblsquic/lsquic_conn.h Normal file
View file

@ -0,0 +1,130 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_conn.h -- Connection interface
*
*/
#ifndef LSQUIC_CONN_H
#define LSQUIC_CONN_H
#include <sys/queue.h>
#include <sys/socket.h>
#include <netinet/in.h>
struct lsquic_conn;
struct lsquic_enc_session;
struct lsquic_engine_public;
struct lsquic_packet_out;
struct lsquic_packet_in;
struct sockaddr;
struct parse_funcs;
struct attq_elem;
enum lsquic_conn_flags {
LSCONN_HAS_INCOMING = (1 << 0),
LSCONN_HAS_OUTGOING = (1 << 1),
LSCONN_HASHED = (1 << 2),
LSCONN_HAS_PEER_SA = (1 << 4),
LSCONN_HAS_LOCAL_SA = (1 << 5),
LSCONN_HANDSHAKE_DONE = (1 << 6),
LSCONN_CLOSING = (1 << 7),
LSCONN_PEER_GOING_AWAY= (1 << 8),
LSCONN_TCID0 = (1 << 9),
LSCONN_VER_SET = (1 <<10), /* cn_version is set */
LSCONN_EVANESCENT = (1 <<11), /* evanescent connection */
LSCONN_RW_PENDING = (1 <<12),
LSCONN_COI_ACTIVE = (1 <<13),
LSCONN_COI_INACTIVE = (1 <<14),
LSCONN_SEND_BLOCKED = (1 <<15), /* Send connection blocked frame */
LSCONN_NEVER_PEND_RW = (1 <<17), /* Do not put onto Pending RW queue */
LSCONN_ATTQ = (1 <<19),
};
#define TICK_BIT_PROGRESS 3
/* A connection may have things to send and be closed at the same time.
*/
enum tick_st {
TICK_SEND = (1 << 0),
TICK_CLOSE = (1 << 1),
/* Progress was made (see @ref es_pendrw_check for definition of
* "progress.")
*/
TICK_PROGRESS= (1 << TICK_BIT_PROGRESS),
};
#define TICK_QUIET 0
struct conn_iface
{
enum tick_st
(*ci_tick) (struct lsquic_conn *, lsquic_time_t now);
void
(*ci_packet_in) (struct lsquic_conn *, struct lsquic_packet_in *);
struct lsquic_packet_out *
(*ci_next_packet_to_send) (struct lsquic_conn *);
void
(*ci_packet_sent) (struct lsquic_conn *, struct lsquic_packet_out *);
void
(*ci_packet_not_sent) (struct lsquic_conn *, struct lsquic_packet_out *);
void
(*ci_handshake_done) (struct lsquic_conn *);
int
(*ci_user_wants_read) (struct lsquic_conn *);
void
(*ci_destroy) (struct lsquic_conn *);
};
#define RW_HIST_BITS 6
typedef unsigned char rw_hist_idx_t;
struct lsquic_conn
{
void *cn_peer_ctx;
struct lsquic_enc_session *cn_enc_session;
lsquic_cid_t cn_cid;
STAILQ_ENTRY(lsquic_conn) cn_next_closed_conn;
TAILQ_ENTRY(lsquic_conn) cn_next_all,
cn_next_in,
cn_next_pend_rw,
cn_next_out,
cn_next_hash;
const struct conn_iface *cn_if;
const struct parse_funcs *cn_pf;
struct attq_elem *cn_attq_elem;
lsquic_time_t cn_last_sent;
enum lsquic_conn_flags cn_flags;
enum lsquic_version cn_version;
unsigned cn_noprogress_count;
unsigned cn_hash;
unsigned short cn_pack_size;
rw_hist_idx_t cn_rw_hist_idx;
unsigned char cn_rw_hist_buf[ 1 << RW_HIST_BITS ];
unsigned char cn_peer_addr[sizeof(struct sockaddr_in6)],
cn_local_addr[sizeof(struct sockaddr_in6)];
};
void
lsquic_conn_record_sockaddr (lsquic_conn_t *lconn, const struct sockaddr *local,
const struct sockaddr *peer);
void
lsquic_conn_record_peer_sa (lsquic_conn_t *lconn, const struct sockaddr *peer);
int
lsquic_conn_decrypt_packet (lsquic_conn_t *lconn,
struct lsquic_engine_public *, struct lsquic_packet_in *);
int
lsquic_conn_copy_and_release_pi_data (const lsquic_conn_t *conn,
struct lsquic_engine_public *, struct lsquic_packet_in *);
#define lsquic_conn_adv_time(c) ((c)->cn_attq_elem->ae_adv_time)
#endif

View file

@ -0,0 +1,74 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_conn_flow.h -- Connection flow control-related functions
*/
#ifndef LSQUIC_CONN_FLOW_H
#define LSQUIC_CONN_FLOW_H 1
struct lsquic_conn_public;
typedef struct lsquic_cfcw {
struct lsquic_conn_public
*cf_conn_pub;
uint64_t cf_max_recv_off; /* Largest offset observed (cumulative) */
uint64_t cf_recv_off; /* Flow control receive offset */
uint64_t cf_read_off; /* Number of bytes consumed (cumulative) */
lsquic_time_t cf_last_updated;
unsigned cf_max_recv_win; /* Maximum receive window */
} lsquic_cfcw_t;
struct lsquic_conn_cap {
uint64_t cc_sent; /* Number of bytes sent on connection */
uint64_t cc_max; /* Maximum cumulative number of bytes allowed
* to be sent on this connection.
*/
uint64_t cc_tosend; /* Number of bytes scheduled to be sent
* accross all streams.
*/
uint64_t cc_blocked; /* Last blocked offset used */
};
#define lsquic_conn_cap_init(cc, max) do { \
(cc)->cc_sent = 0; \
(cc)->cc_tosend = 0; \
(cc)->cc_max = max; \
} while (0)
#define lsquic_conn_cap_avail(cap) ( \
((cap)->cc_sent + (cap)->cc_tosend <= (cap)->cc_max) \
? \
((cap)->cc_max - (cap)->cc_sent - (cap)->cc_tosend) \
: \
0 \
)
void
lsquic_cfcw_init (lsquic_cfcw_t *, struct lsquic_conn_public *,
unsigned initial_max_recv_window);
/* If update is to be sent, updates max_recv_off and returns true. Note
* that if you call this function twice, the second call will return false.
*/
int
lsquic_cfcw_fc_offsets_changed (lsquic_cfcw_t *);
#define lsquic_cfcw_get_fc_recv_off(fc) (+(fc)->cf_recv_off)
#define lsquic_cfcw_get_max_recv_off(fc) (+(fc)->cf_max_recv_off)
#define lsquic_cfcw_get_max_recv_window(fc) (+(fc)->cf_max_recv_win)
/* Returns false if flow control violation is encountered */
int
lsquic_cfcw_incr_max_recv_off (lsquic_cfcw_t *, uint64_t);
/* Void because we do not expect the caller to make a mistake.
*/
void
lsquic_cfcw_incr_read_off (lsquic_cfcw_t *, uint64_t);
#endif

View file

@ -0,0 +1,171 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_conn.h"
#include "lsquic_conn_hash.h"
#include "lsquic_xxhash.h"
#define LSQUIC_LOGGER_MODULE LSQLM_CONN_HASH
#include "lsquic_logger.h"
#define n_buckets(nbits) (1U << (nbits))
#define conn_hash_mask(conn_hash) ((1 << (conn_hash)->ch_nbits) - 1)
#define conn_hash_bucket_no(conn_hash, hash) (hash & conn_hash_mask(conn_hash))
int
conn_hash_init (struct conn_hash *conn_hash, unsigned max_count)
{
unsigned n;
if (!max_count)
max_count = 1000000;
memset(conn_hash, 0, sizeof(*conn_hash));
conn_hash->ch_max_count = max_count;
conn_hash->ch_nbits = 1; /* Start small */
TAILQ_INIT(&conn_hash->ch_all);
conn_hash->ch_buckets = malloc(sizeof(conn_hash->ch_buckets[0]) *
n_buckets(conn_hash->ch_nbits));
if (!conn_hash->ch_buckets)
return -1;
for (n = 0; n < n_buckets(conn_hash->ch_nbits); ++n)
TAILQ_INIT(&conn_hash->ch_buckets[n]);
LSQ_INFO("initialized: max_count: %u", conn_hash->ch_max_count);
return 0;
}
void
conn_hash_cleanup (struct conn_hash *conn_hash)
{
free(conn_hash->ch_buckets);
}
struct lsquic_conn *
conn_hash_find (struct conn_hash *conn_hash, lsquic_cid_t cid, unsigned *hashp)
{
const unsigned hash = XXH32(&cid, sizeof(cid), (uintptr_t) conn_hash);
const unsigned buckno = conn_hash_bucket_no(conn_hash, hash);
struct lsquic_conn *lconn;
TAILQ_FOREACH(lconn, &conn_hash->ch_buckets[buckno], cn_next_hash)
if (lconn->cn_cid == cid)
return lconn;
if (hashp)
*hashp = hash;
return NULL;
}
static int
double_conn_hash_buckets (struct conn_hash *conn_hash)
{
struct lsquic_conn_head *new_buckets, *new[2];
struct lsquic_conn *lconn;
unsigned n, old_nbits;
int idx;
old_nbits = conn_hash->ch_nbits;
LSQ_INFO("doubling number of buckets to %u", n_buckets(old_nbits + 1));
new_buckets = malloc(sizeof(conn_hash->ch_buckets[0])
* n_buckets(old_nbits + 1));
if (!new_buckets)
{
LSQ_WARN("malloc failed: potential trouble ahead");
return -1;
}
for (n = 0; n < n_buckets(old_nbits); ++n)
{
new[0] = &new_buckets[n];
new[1] = &new_buckets[n + n_buckets(old_nbits)];
TAILQ_INIT(new[0]);
TAILQ_INIT(new[1]);
while ((lconn = TAILQ_FIRST(&conn_hash->ch_buckets[n])))
{
TAILQ_REMOVE(&conn_hash->ch_buckets[n], lconn, cn_next_hash);
idx = (lconn->cn_hash >> old_nbits) & 1;
TAILQ_INSERT_TAIL(new[idx], lconn, cn_next_hash);
}
}
free(conn_hash->ch_buckets);
conn_hash->ch_nbits = old_nbits + 1;
conn_hash->ch_buckets = new_buckets;
return 0;
}
int
conn_hash_add (struct conn_hash *conn_hash, struct lsquic_conn *lconn,
unsigned hash)
{
assert(hash == XXH32(&lconn->cn_cid, sizeof(lconn->cn_cid),
(uintptr_t) conn_hash));
if (conn_hash->ch_count >= conn_hash->ch_max_count)
return -1;
if (conn_hash->ch_count >=
n_buckets(conn_hash->ch_nbits) * CONN_HASH_MAX_PER_BUCKET &&
conn_hash->ch_nbits < sizeof(hash) * 8 - 1 &&
0 != double_conn_hash_buckets(conn_hash))
{
return -1;
}
const unsigned buckno = conn_hash_bucket_no(conn_hash, hash);
lconn->cn_hash = hash;
TAILQ_INSERT_TAIL(&conn_hash->ch_all, lconn, cn_next_all);
TAILQ_INSERT_TAIL(&conn_hash->ch_buckets[buckno], lconn, cn_next_hash);
++conn_hash->ch_count;
return 0;
}
int
conn_hash_add_new (struct conn_hash *conn_hash, struct lsquic_conn *lconn)
{
unsigned hash_val;
hash_val = XXH32(&lconn->cn_cid, sizeof(lconn->cn_cid),
(uintptr_t) conn_hash);
return conn_hash_add(conn_hash, lconn, hash_val);
}
void
conn_hash_remove (struct conn_hash *conn_hash, struct lsquic_conn *lconn)
{
const unsigned buckno = conn_hash_bucket_no(conn_hash, lconn->cn_hash);
TAILQ_REMOVE(&conn_hash->ch_all, lconn, cn_next_all);
TAILQ_REMOVE(&conn_hash->ch_buckets[buckno], lconn, cn_next_hash);
--conn_hash->ch_count;
}
void
conn_hash_reset_iter (struct conn_hash *conn_hash)
{
conn_hash->ch_next = TAILQ_FIRST(&conn_hash->ch_all);
}
struct lsquic_conn *
conn_hash_first (struct conn_hash *conn_hash)
{
conn_hash_reset_iter(conn_hash);
return conn_hash_next(conn_hash);
}
struct lsquic_conn *
conn_hash_next (struct conn_hash *conn_hash)
{
struct lsquic_conn *lconn = conn_hash->ch_next;
if (lconn)
conn_hash->ch_next = TAILQ_NEXT(lconn, cn_next_all);
return lconn;
}

View file

@ -0,0 +1,79 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_conn_hash.h -- A hash of connections
*/
#ifndef LSQUIC_MC_SET_H
#define LSQUIC_MC_SET_H
#include <sys/queue.h>
/* Once we reach this many per bucket on average, double the number of
* buckets and redistribute entries.
*
* This value should be a power of two for speed.
*/
#define CONN_HASH_MAX_PER_BUCKET 2
struct lsquic_conn;
TAILQ_HEAD(lsquic_conn_head, lsquic_conn);
struct conn_hash
{
struct lsquic_conn_head ch_all;
struct lsquic_conn_head *ch_buckets;
struct lsquic_conn *ch_next;
unsigned ch_count;
unsigned ch_nbits;
unsigned ch_max_count;
};
#define conn_hash_count(conn_hash) (+(conn_hash)->ch_count)
/* Returns -1 if malloc fails */
int
conn_hash_init (struct conn_hash *, unsigned max_count);
void
conn_hash_cleanup (struct conn_hash *);
/* If entry is not found, `hash' is set. Use it as argument to conn_hash_add.
* `hash' may be NULL.
*/
struct lsquic_conn *
conn_hash_find (struct conn_hash *conn_hash, lsquic_cid_t cid, unsigned *hash);
/* Returns -1 if limit has been reached or if malloc fails */
int
conn_hash_add (struct conn_hash *, struct lsquic_conn *, unsigned hash);
/* Returns -1 if limit has been reached or if malloc fails */
int
conn_hash_add_new (struct conn_hash *, struct lsquic_conn *);
void
conn_hash_remove (struct conn_hash *, struct lsquic_conn *);
/* Two ways to use the iterator:
* 1.
* for (conn = conn_hash_first(hash); conn;
* conn = conn_hash_next(hash))
* { ... }
*
* 2.
* conn_hash_reset_iter(hash);
* while ((conn = conn_hash_next(hash)))
* { ... }
*
*/
void
conn_hash_reset_iter (struct conn_hash *);
struct lsquic_conn *
conn_hash_first (struct conn_hash *);
struct lsquic_conn *
conn_hash_next (struct conn_hash *);
#endif

View file

@ -0,0 +1,35 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_conn_public.h -- Connection's "public interface"
*
* This structure is used to bundle things in connection that stream
* needs access to into a single object. This way, the space per
* stream object is one pointer instead of four or five.
*/
#ifndef LSQUIC_CONN_PUBLIC_H
#define LSQUIC_CONN_PUBLIC_H 1
struct lsquic_conn;
struct lsquic_engine_public;
struct lsquic_mm;
struct lsquic_stream;
struct headers_stream;
struct lsquic_send_ctl;
struct lsquic_conn_public {
struct lsquic_streams_tailq sending_streams,
rw_streams,
service_streams;
struct lsquic_cfcw cfcw;
struct lsquic_conn_cap conn_cap;
struct lsquic_rtt_stats rtt_stats;
struct lsquic_engine_public *enpub;
struct malo *packet_out_malo;
struct lsquic_conn *lconn;
struct lsquic_mm *mm;
struct headers_stream *hs;
struct lsquic_send_ctl *send_ctl;
};
#endif

View file

@ -0,0 +1,562 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <zlib.h>
#include <openssl/ssl.h>
#include "lsquic_int_types.h"
#include "lsquic_crypto.h"
#include "lsquic_crt_compress.h"
#include "lsquic_util.h"
#include "lsquic_str.h"
#include "common_cert_set_2.c"
#include "common_cert_set_3.c"
/*
* common_cert_sub_strings contains ~1500 bytes of common certificate substrings
* as a dictionary of zlib from the Alexa Top 5000 set.
*/
static const unsigned char common_cert_sub_strings[] = {
0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03,
0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30,
0x5f, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01,
0x06, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, 0x6d, 0x01, 0x07,
0x17, 0x01, 0x30, 0x33, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65,
0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x20, 0x53, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x34,
0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31,
0x32, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72,
0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x2d, 0x61, 0x69, 0x61, 0x2e,
0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x45, 0x2d, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73,
0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x45, 0x2e, 0x63, 0x65,
0x72, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x4a, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
0x2f, 0x63, 0x70, 0x73, 0x20, 0x28, 0x63, 0x29, 0x30, 0x30, 0x09, 0x06,
0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x7b, 0x30, 0x1d, 0x06, 0x03, 0x55,
0x1d, 0x0e, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd2,
0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x2e,
0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
0x04, 0x14, 0xb4, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69,
0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x30, 0x0b, 0x06, 0x03,
0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x30, 0x0d, 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30,
0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08,
0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30,
0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74,
0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03,
0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79,
0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x33,
0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68, 0x74, 0x74,
0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
0x6f, 0x72, 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03,
0x13, 0x27, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x53,
0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68,
0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55,
0x04, 0x05, 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32, 0x38, 0x37,
0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x0c,
0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
0x30, 0x1d, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55,
0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
0x03, 0x02, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d,
0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86,
0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e,
0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x67, 0x64, 0x73, 0x31, 0x2d, 0x32, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08,
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74,
0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65,
0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
0x70, 0x73, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17,
0x0d, 0x31, 0x33, 0x30, 0x35, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01,
0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a,
0x2f, 0x2f, 0x73, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01,
0x05, 0x05, 0x07, 0x02, 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
0x3d, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86,
0xf8, 0x45, 0x01, 0x07, 0x17, 0x06, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x1b, 0x53, 0x31, 0x17,
0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, 0x72,
0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, 0x65,
0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74,
0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30, 0x39,
0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d, 0x73,
0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20, 0x68,
0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76,
0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x30, 0x31, 0x10, 0x30, 0x0e,
0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x31, 0x13, 0x30, 0x11,
0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x47, 0x31, 0x13, 0x30, 0x11,
0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x3c, 0x02, 0x01,
0x03, 0x13, 0x02, 0x55, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
0x03, 0x14, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0f, 0x13, 0x14, 0x50,
0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x4f, 0x72, 0x67, 0x61, 0x6e,
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x12, 0x31, 0x21, 0x30,
0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x44, 0x6f, 0x6d, 0x61,
0x69, 0x6e, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x56,
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x31, 0x14, 0x31, 0x31,
0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x28, 0x53, 0x65, 0x65,
0x20, 0x77, 0x77, 0x77, 0x2e, 0x72, 0x3a, 0x2f, 0x2f, 0x73, 0x65, 0x63,
0x75, 0x72, 0x65, 0x2e, 0x67, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53,
0x69, 0x67, 0x6e, 0x31, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41,
0x2e, 0x63, 0x72, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e,
0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x45, 0x63, 0x72,
0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x64, 0x31, 0x1a,
0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x3a,
0x2f, 0x2f, 0x45, 0x56, 0x49, 0x6e, 0x74, 0x6c, 0x2d, 0x63, 0x63, 0x72,
0x74, 0x2e, 0x67, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x69, 0x63, 0x65, 0x72,
0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x31, 0x6f, 0x63, 0x73, 0x70, 0x2e,
0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
0x30, 0x39, 0x72, 0x61, 0x70, 0x69, 0x64, 0x73, 0x73, 0x6c, 0x2e, 0x63,
0x6f, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
0x79, 0x2f, 0x30, 0x81, 0x80, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
0x07, 0x01, 0x01, 0x04, 0x74, 0x30, 0x72, 0x30, 0x24, 0x06, 0x08, 0x2b,
0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74,
0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64,
0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x4a, 0x06,
0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68,
0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64,
0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73,
0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72,
0x74, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
0x80, 0x14, 0xfd, 0xac, 0x61, 0x32, 0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee,
0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76, 0x99, 0x68, 0xcc, 0xe7, 0x30, 0x27,
0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x86, 0x30,
0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73,
};
#define common_certs_num 2
const common_cert_t common_cert_set[common_certs_num] = {
{common_certs2_num, common_certs2, common_certs2_lens, common_certs2_hash},
{common_certs3_num, common_certs3, common_certs3_lens, common_certs3_hash},
};
static lsquic_str_t *s_ccsbuf;
lsquic_str_t * get_common_certs_hash()
{
int i;
if (s_ccsbuf == NULL)
{
s_ccsbuf = lsquic_str_new(NULL, 0);
for (i=0 ;i<common_certs_num; ++i)
{
lsquic_str_append(s_ccsbuf, (const char *)&common_cert_set[i].hash, 8);
}
}
return s_ccsbuf;
}
/* return 0 found, -1 not found */
int get_common_cert(uint64_t hash, uint32_t index, lsquic_str_t *buf)
{
int i;
for (i = 0; i < common_certs_num; i++)
{
if (common_cert_set[i].hash == hash)
{
if (index < common_cert_set[i].num_certs)
{
lsquic_str_setto(buf, (const char *) common_cert_set[i].certs[index],
common_cert_set[i].lens[index]);
return 0;
}
break;
}
}
return -1;
}
/* result is written to dict */
static void
make_zlib_dict_for_entries(cert_entry_t *entries,
lsquic_str_t **certs, size_t certs_count,
lsquic_str_t *dict)
{
int i;
size_t zlib_dict_size = 0;
for (i = certs_count - 1; i >= 0; --i)
{
if (entries[i].type != ENTRY_COMPRESSED)
{
zlib_dict_size += lsquic_str_len(certs[i]);
}
}
// At the end of the dictionary is a block of common certificate substrings.
zlib_dict_size += sizeof(common_cert_sub_strings);
for (i = certs_count - 1; i >= 0; --i)
{
if (entries[i].type != ENTRY_COMPRESSED)
{
lsquic_str_append(dict, lsquic_str_buf(certs[i]), lsquic_str_len(certs[i]));
}
}
lsquic_str_append(dict, (const char *)common_cert_sub_strings, sizeof(common_cert_sub_strings));
assert((size_t)lsquic_str_len(dict) == zlib_dict_size);
}
void get_certs_hash(lsquic_str_t *certs, size_t certs_count, uint64_t *hashs)
{
size_t i;
for(i = 0; i < certs_count; ++i)
{
hashs[i] = fnv1a_64((const uint8_t *)lsquic_str_buf(&certs[i]), lsquic_str_len(&certs[i]));
}
}
size_t get_entries_size(cert_entry_t *entries, size_t entries_count)
{
size_t i;
size_t entries_size = 0;
for(i=0; i<entries_count; ++i)
{
entries_size++;
switch (entries[i].type)
{
case ENTRY_COMPRESSED:
break;
case ENTRY_CACHED:
entries_size += sizeof(uint64_t);
break;
case ENTRY_COMMON:
entries_size += sizeof(uint64_t) + sizeof(uint32_t);
break;
default:
break;
}
}
entries_size++; /* for end marker */
return entries_size;
}
void serialize_cert_entries(uint8_t* out, int *out_len, cert_entry_t *entries,
size_t entries_count)
{
size_t i;
uint8_t *start = out;
for(i=0; i<entries_count; ++i)
{
*out++ = (uint8_t)(entries[i].type);
switch (entries[i].type)
{
case ENTRY_COMPRESSED:
break;
case ENTRY_CACHED:
memcpy(out, &entries[i].hash, sizeof(uint64_t));
out += sizeof(uint64_t);
break;
case ENTRY_COMMON:
memcpy(out, &entries[i].set_hash, sizeof(uint64_t));
out += sizeof(uint64_t);
memcpy(out, &entries[i].index, sizeof(uint32_t));
out += sizeof(uint32_t);
break;
default:
break;
}
}
*out++ = 0; // end marker
*out_len = out - start;
}
int get_certs_count(lsquic_str_t *compressed_crt_buf)
{
char *in = lsquic_str_buf(compressed_crt_buf);
char *in_end = in + lsquic_str_len(compressed_crt_buf);
size_t idx = 0;
uint8_t type_byte;
for (;;)
{
if (in >= in_end)
return -1;
type_byte = in[0];
++in;
if (type_byte == 0)
break;
++idx;
switch(type_byte)
{
case ENTRY_COMPRESSED:
break;
case ENTRY_CACHED:
{
if (in_end - in < (int)sizeof(uint64_t))
return -1;
in += sizeof(uint64_t);
break;
}
case ENTRY_COMMON:
{
if (in_end - in < (int)(sizeof(uint64_t) + sizeof(uint32_t)))
return -1;
in += sizeof(uint64_t) + sizeof(uint32_t);
break;
}
default:
return -1;
}
}
return idx;
}
/* return 0: OK, -1, error */
static int parse_entries(const unsigned char **in_out, const unsigned char *const in_end,
lsquic_str_t *cached_certs, size_t cached_certs_count,
cert_entry_t *out_entries,
lsquic_str_t **out_certs, size_t *out_certs_count)
{
const unsigned char *in = *in_out;
size_t idx = 0;
uint64_t* cached_hashes;
cert_entry_t *entry;
lsquic_str_t *cert;
uint8_t type_byte;
int rv;
size_t i;
cached_hashes = NULL;
for (;;)
{
/* XXX potential invalid read */
type_byte = in[0];
++in;
if (type_byte == 0)
break;
entry = &out_entries[idx];
cert = out_certs[idx];
/* XXX This seems dangerous -- there is no guard that `idx' does not
* exceed `out_certs_count'.
*/
lsquic_str_d(cert);
++idx;
entry->type = type_byte;
switch (entry->type)
{
case ENTRY_COMPRESSED:
break;
case ENTRY_CACHED:
{
memcpy(&entry->hash, in, sizeof(uint64_t));
in += sizeof(uint64_t);
if (!cached_hashes)
{
cached_hashes = malloc(cached_certs_count * sizeof(uint64_t));;
if (!cached_hashes)
goto err;
get_certs_hash(cached_certs, cached_certs_count, cached_hashes);
}
for (i=0; i<cached_certs_count; ++i)
{
if (cached_hashes[i] == entry->hash)
{
lsquic_str_append(cert, lsquic_str_buf(&cached_certs[i]),
lsquic_str_len(&cached_certs[i]));
break;
}
}
/* XXX: return -1 if not found? Logic removed in
4fd7e76bc031ac637e76c7f0930aff53f5b71705 */
break;
}
case ENTRY_COMMON:
{
memcpy(&entry->set_hash, in, sizeof(uint64_t));
in += sizeof(uint64_t);
memcpy(&entry->index, in, sizeof(uint32_t));
in += sizeof(uint32_t);
if (0 == get_common_cert(entry->set_hash, entry->index, cert))
break;
else
goto err;
}
default:
goto err;
}
}
rv = 0;
*in_out = in;
*out_certs_count = idx;
cleanup:
free(cached_hashes);
return rv;
err:
rv = -1;
goto cleanup;
}
/* 0: ok */
int decompress_certs(const unsigned char *in, const unsigned char *in_end,
lsquic_str_t *cached_certs, size_t cached_certs_count,
lsquic_str_t **out_certs, size_t *out_certs_count)
{
int ret;
size_t i;
uint8_t* uncompressed_data, *uncompressed_data_buf;
lsquic_str_t *dict;
uint32_t uncompressed_size;
size_t count = *out_certs_count;
cert_entry_t *entries;
z_stream z;
assert(*out_certs_count > 0 && *out_certs_count < 10000
&& "Call get_certs_count() to get right certificates count first and make enough room for out_certs_count");
if (count == 0 || count > 10000)
return -1;
dict = lsquic_str_new(NULL, 0);
if (!dict)
return -1;
uncompressed_data_buf = NULL;
entries = malloc(count * sizeof(cert_entry_t));
if (!entries)
goto err;
ret = parse_entries(&in, in_end, cached_certs, cached_certs_count,
entries, out_certs, out_certs_count);
if (ret)
goto err;
/* re-assign count with real valus */
count = *out_certs_count;
if (in < in_end)
{
if (in_end - in < (int)sizeof(uint32_t))
goto err;
memcpy(&uncompressed_size, in, sizeof(uncompressed_size));
in += sizeof(uint32_t);
/* XXX Is 128 KB an arbitrary limit or is there a reason behind it? */
if (uncompressed_size > 128 * 1024)
goto err;
uncompressed_data_buf = uncompressed_data = malloc(uncompressed_size);
memset(&z, 0, sizeof(z));
z.next_out = uncompressed_data;
z.avail_out = uncompressed_size;
z.next_in = (unsigned char *) in;
z.avail_in = in_end - in;
if (Z_OK != inflateInit(&z))
goto err;
ret = inflate(&z, Z_FINISH);
if (ret == Z_NEED_DICT)
{
lsquic_str_d(dict);
make_zlib_dict_for_entries(entries, out_certs, count, dict);
if (Z_OK != inflateSetDictionary(&z, (const unsigned char *)lsquic_str_buf(dict), lsquic_str_len(dict)))
goto err;
ret = inflate(&z, Z_FINISH);
}
if (Z_STREAM_END != ret || z.avail_out > 0 || z.avail_in > 0)
goto err;
}
else
uncompressed_size = 0;
for (i = 0; i < count; i++)
{
switch (entries[i].type)
{
case ENTRY_COMPRESSED:
if (uncompressed_size < sizeof(uint32_t))
goto err;
lsquic_str_d(out_certs[i]);
uint32_t cert_len;
memcpy(&cert_len, uncompressed_data, sizeof(cert_len));
uncompressed_data += sizeof(uint32_t);
uncompressed_size -= sizeof(uint32_t);
if (uncompressed_size < cert_len)
goto err;
lsquic_str_append(out_certs[i], (const char *)uncompressed_data, cert_len);
uncompressed_data += cert_len;
uncompressed_size -= cert_len;
break;
case ENTRY_CACHED:
case ENTRY_COMMON:
default:
break;
}
}
cleanup:
lsquic_str_delete(dict);
free(entries);
if (uncompressed_data_buf)
inflateEnd(&z);
free(uncompressed_data_buf);
if (0 == uncompressed_size)
return 0;
else
return -1;
err:
uncompressed_size = 1; /* This triggers return -1 above */
goto cleanup;
}
void
lsquic_crt_cleanup (void)
{
if (s_ccsbuf)
{
lsquic_str_delete(s_ccsbuf);
s_ccsbuf = NULL;
}
}

View file

@ -0,0 +1,50 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef __LSQUIC_CRT_COMPRESS_H__
#define __LSQUIC_CRT_COMPRESS_H__
#include <stdint.h>
struct lsquic_str;
#ifdef __cplusplus
extern "C" {
#endif
enum entry_type {
END_OF_LIST = 0,
ENTRY_COMPRESSED = 1,
ENTRY_CACHED = 2,
ENTRY_COMMON = 3,
};
typedef struct cert_entry_st {
enum entry_type type;
uint32_t index;
uint64_t hash;
uint64_t set_hash;
} cert_entry_t;
typedef struct common_cert_st
{
size_t num_certs;
const unsigned char* const* certs;
const size_t* lens;
uint64_t hash;
} common_cert_t;
struct lsquic_str * get_common_certs_hash();
int get_certs_count(struct lsquic_str *compressed_crt_buf);
int decompress_certs(const unsigned char *in, const unsigned char *in_end,
struct lsquic_str *cached_certs, size_t cached_certs_count,
struct lsquic_str **out_certs,
size_t *out_certs_count);
void
lsquic_crt_cleanup (void);
#ifdef __cplusplus
}
#endif
#endif //__LSQUIC_CRT_COMPRESS_H__

View file

@ -0,0 +1,683 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/crypto.h>
#include <openssl/stack.h>
#include <openssl/x509.h>
#include <openssl/rand.h>
#include <openssl/curve25519.h>
#include <zlib.h>
#include "lsquic_types.h"
#include "lsquic_crypto.h"
#include "lsquic_alarmset.h"
#include "lsquic_parse.h"
#include "lsquic_util.h"
#include "lsquic_str.h"
#define LSQUIC_LOGGER_MODULE LSQLM_CRYPTO
#include "lsquic_logger.h"
static const char s_hs_signature[] = "QUIC CHLO and server config signature";
static int crypto_inited = 0;
void rand_bytes(void *data, int len)
{
RAND_bytes(data, len);
}
uint64_t fnv1a_64(const uint8_t * data, int len)
{
uint64_t hash = UINT64_C(14695981039346656037);
const uint8_t *end = data + len;
while(data < end)
{
hash ^= *data;
hash *= UINT64_C(1099511628211);
++data;
}
return hash;
}
void fnv1a_64_s(const uint8_t * data, int len, char *md)
{
uint64_t hash = fnv1a_64(data, len);
memcpy(md, (void *)&hash, 8);
}
#if defined( __x86_64 )||defined( __x86_64__ )
static uint128 s_prime;
static uint128 s_init_hash;
static inline void make_uint128(uint128 *v, uint64_t hi, uint64_t lo)
{
*v = hi;
*v <<= 64;
*v += lo;
}
void fnv1a_inc(uint128 *hash, const uint8_t *data, int len)
{
const uint8_t* end = data + len;
while(data < end)
{
*hash = (*hash ^ (*data)) * s_prime;
++data;
}
}
uint128 fnv1a_128_2(const uint8_t * data1, int len1, const uint8_t *data2, int len2)
{
uint128 hash;
memcpy(&hash, &s_init_hash, 16);
fnv1a_inc(&hash, data1, len1);
if (data2)
fnv1a_inc(&hash, data2, len2);
return hash;
}
uint128 fnv1a_128_3(const uint8_t *data1, int len1,
const uint8_t *data2, int len2,
const uint8_t *data3, int len3)
{
uint128 hash;
memcpy(&hash, &s_init_hash, 16);
fnv1a_inc(&hash, data1, len1);
fnv1a_inc(&hash, data2, len2);
fnv1a_inc(&hash, data3, len3);
return hash;
}
void fnv1a_128_2_s(const uint8_t * data1, int len1, const uint8_t * data2, int len2, uint8_t *md)
{
uint128 hash = fnv1a_128_2(data1, len1, data2, len2);
memcpy(md, (void *)&hash, 16);
}
/* HS_PKT_HASH_LENGTH bytes of md */
void serialize_fnv128_short(uint128 v, uint8_t *md)
{
memcpy(md, (void *)&v, 12);
}
#else
uint128 *uint128_times(uint128 *v, const uint128 *factor)
{
uint64_t a96 = v->hi_ >> 32;
uint64_t a64 = v->hi_ & 0xffffffffu;
uint64_t a32 = v->lo_ >> 32;
uint64_t a00 = v->lo_ & 0xffffffffu;
uint64_t b96 = factor->hi_ >> 32;
uint64_t b64 = factor->hi_ & 0xffffffffu;
uint64_t b32 = factor->lo_ >> 32;
uint64_t b00 = factor->lo_ & 0xffffffffu;
uint64_t tmp, lolo;
// multiply [a96 .. a00] x [b96 .. b00]
// terms higher than c96 disappear off the high side
// terms c96 and c64 are safe to ignore carry bit
uint64_t c96 = a96 * b00 + a64 * b32 + a32 * b64 + a00 * b96;
uint64_t c64 = a64 * b00 + a32 * b32 + a00 * b64;
v->hi_ = (c96 << 32) + c64;
v->lo_ = 0;
tmp = a32 * b00;
v->hi_ += tmp >> 32;
v->lo_ += tmp << 32;
tmp = a00 * b32;
v->hi_ += tmp >> 32;
v->lo_ += tmp << 32;
tmp = a00 * b00;
lolo = v->lo_ + tmp;
if (lolo < v->lo_)
++v->hi_;
v->lo_ = lolo;
return v;
}
void fnv1a_inc(uint128 *hash, const uint8_t * data, int len)
{
static const uint128 kPrime = {16777216, 315};
const uint8_t* end = data + len;
while(data < end)
{
hash->lo_ = (hash->lo_ ^ (uint64_t)*data);
uint128_times(hash, &kPrime);
++data;
}
}
uint128 fnv1a_128_2(const uint8_t * data1, int len1, const uint8_t * data2, int len2)
{
uint128 hash = {UINT64_C(7809847782465536322), UINT64_C(7113472399480571277)};
fnv1a_inc(&hash, data1, len1);
if (data2)
fnv1a_inc(&hash, data2, len2);
return hash;
}
uint128 fnv1a_128_3(const uint8_t * data1, int len1,
const uint8_t * data2, int len2,
const uint8_t * data3, int len3)
{
uint128 hash = {UINT64_C(7809847782465536322), UINT64_C(7113472399480571277)};
fnv1a_inc(&hash, data1, len1);
fnv1a_inc(&hash, data2, len2);
fnv1a_inc(&hash, data3, len3);
return hash;
}
void fnv1a_128_2_s(const uint8_t * data1, int len1, const uint8_t * data2, int len2, uint8_t *md)
{
uint128 hash = fnv1a_128_2(data1, len1, data2, len2);
memcpy(md, (void *)&hash.lo_, 8);
memcpy(md + 8, (void *)&hash.hi_, 8);
}
/* HS_PKT_HASH_LENGTH bytes of md */
void serialize_fnv128_short(uint128 v, uint8_t *md)
{
assert(HS_PKT_HASH_LENGTH == 8 + 4);
memcpy(md, (void *)&v.lo_, 8);
memcpy(md + 8, (void *)&v.hi_, 4);
}
#endif
uint128 fnv1a_128(const uint8_t * data, int len)
{
return fnv1a_128_2(data, len , NULL, 0);
}
void fnv1a_128_s(const uint8_t * data, int len, uint8_t *md)
{
return fnv1a_128_2_s(data, len, NULL, 0, md);
}
/* packet data = header + MD + payload */
/* return 0 if OK */
int verify_hs_pkt(const uint8_t *pkg_data, size_t header_len, size_t pkg_len)
{
uint8_t md[HS_PKT_HASH_LENGTH];
uint128 hash;
if (pkg_len < header_len + HS_PKT_HASH_LENGTH)
return -1;
hash = fnv1a_128_2(pkg_data, header_len, pkg_data + header_len + HS_PKT_HASH_LENGTH,
pkg_len - header_len - HS_PKT_HASH_LENGTH);
serialize_fnv128_short(hash, md);
return memcmp(md, pkg_data + header_len, HS_PKT_HASH_LENGTH);
}
/* packet data = header + MD + payload, update the MD part */
int update_hs_pkt_hash(uint8_t *pkg_data, int header_len, int pkg_len)
{
uint8_t md[HS_PKT_HASH_LENGTH];
uint128 hash;
if (pkg_len < header_len + HS_PKT_HASH_LENGTH)
return -1;
hash = fnv1a_128_2(pkg_data, header_len, pkg_data + header_len + HS_PKT_HASH_LENGTH,
pkg_len - header_len - HS_PKT_HASH_LENGTH);
serialize_fnv128_short(hash, md);
memcpy(pkg_data + header_len, md, HS_PKT_HASH_LENGTH);
return 0;
}
int get_hs_pkt_hash_len()
{
return HS_PKT_HASH_LENGTH;
}
void sha256(const uint8_t *buf, int len, uint8_t *h)
{
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, buf, len);
SHA256_Final(h, &ctx);
}
/* base on rfc 5869 with sha256, prk is 32 bytes*/
void lshkdf_extract(const unsigned char *ikm, int ikm_len, const unsigned char *salt,
int salt_len, unsigned char *prk)
{
#ifndef NDEBUG
unsigned char *out;
unsigned int out_len;
out =
#endif
HMAC(EVP_sha256(), salt, salt_len, ikm, ikm_len, prk,
#ifndef NDEBUG
&out_len
#else
NULL
#endif
);
assert(out);
assert(out_len == 32);
}
int lshkdf_expand(const unsigned char *prk, const unsigned char *info, int info_len,
uint16_t c_key_len, uint8_t *c_key,
uint16_t s_key_len, uint8_t *s_key,
uint16_t c_key_iv_len, uint8_t *c_key_iv,
uint16_t s_key_iv_len, uint8_t *s_key_iv,
uint16_t sub_key_len, uint8_t *sub_key)
{
const int SHA256LEN = 32;
int L = c_key_len + s_key_len + c_key_iv_len + s_key_iv_len + sub_key_len;
int N = (L + SHA256LEN - 1) / SHA256LEN;
unsigned char *p_org;
uint8_t *buf;
unsigned char *p;
unsigned char T[SHA256LEN + 1];
int T_len = 0;
int i;
uint8_t *pb;
p_org = malloc(N * SHA256LEN);
if (!p_org)
return -1;
buf = malloc(SHA256LEN + info_len + 13);
if (!buf)
{
free(p_org);
return -1;
}
p = p_org;
for (i = 1; i <= N; ++i)
{
pb = buf;
if (T_len > 0)
{
memcpy(pb, T, T_len);
pb += T_len;
}
memcpy(pb, info, info_len);
pb += info_len;
*pb = i;
++pb;
HMAC(EVP_sha256(), prk, SHA256LEN, buf, pb - buf, T, NULL);
if (i != N)
T_len = SHA256LEN;
else
T_len = L - (N - 1) * SHA256LEN;
memcpy(p, T, T_len);
p += T_len;
}
free(buf);
p = p_org;
if (c_key_len)
{
memcpy(c_key, p, c_key_len);
p += c_key_len;
}
if (s_key_len)
{
memcpy(s_key, p, s_key_len);
p += s_key_len;
}
if (c_key_iv_len)
{
memcpy(c_key_iv, p, c_key_iv_len);
p += c_key_iv_len;
}
if (s_key_iv_len)
{
memcpy(s_key_iv, p, s_key_iv_len);
p += s_key_iv_len;
}
if (sub_key_len && sub_key)
{
memcpy(sub_key, p, sub_key_len);
p += sub_key_len;
}
free(p_org);
return 0;
}
int export_key_material_simple(unsigned char *ikm, uint32_t ikm_len,
unsigned char *salt, int salt_len,
char *label, uint32_t label_len,
const uint8_t *context, uint32_t context_len,
uint8_t *key, uint16_t key_len)
{
unsigned char prk[32];
int info_len;
uint8_t *info = NULL;
info = (uint8_t *)malloc(label_len + 1 + sizeof(uint32_t) + context_len);
if (!info)
return -1;
lshkdf_extract(ikm, ikm_len, salt, salt_len, prk);
memcpy(info, label, label_len);
info[label_len] = 0x00;
info_len = label_len + 1;
memcpy(info + info_len, &context_len, sizeof(uint32_t));
info_len += sizeof(uint32_t);
memcpy(info + info_len, context, context_len);
info_len += context_len;
lshkdf_expand(prk, info, info_len, key_len, key,
0, NULL, 0, NULL,0, NULL, 0, NULL);
free(info);
return 0;
}
int export_key_material(const unsigned char *ikm, uint32_t ikm_len,
const unsigned char *salt, int salt_len,
const unsigned char *context, uint32_t context_len,
uint16_t c_key_len, uint8_t *c_key,
uint16_t s_key_len, uint8_t *s_key,
uint16_t c_key_iv_len, uint8_t *c_key_iv,
uint16_t s_key_iv_len, uint8_t *s_key_iv,
uint8_t *sub_key)
{
unsigned char prk[32];
uint16_t sub_key_len = ikm_len;
lshkdf_extract(ikm, ikm_len, salt, salt_len, prk);
lshkdf_expand(prk, context, context_len, c_key_len, c_key,
s_key_len, s_key, c_key_iv_len, c_key_iv, s_key_iv_len,
s_key_iv, sub_key_len, sub_key);
return 0;
}
void c255_get_pub_key(unsigned char *priv_key, unsigned char pub_key[32])
{
X25519_public_from_private(pub_key, priv_key);
}
int c255_gen_share_key(unsigned char *priv_key, unsigned char *peer_pub_key, unsigned char *shared_key)
{
return X25519(shared_key, priv_key, peer_pub_key);
}
/* AEAD nonce is always zero */
/* return 0 for OK */
int aes_aead_enc(EVP_AEAD_CTX *key,
const uint8_t *ad, size_t ad_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *plain, size_t plain_len,
uint8_t *cypher, size_t *cypher_len)
{
int ret = 0;
size_t max_out_len;
max_out_len = *cypher_len;//plain_len + EVP_AEAD_max_overhead(aead_);
assert(*cypher_len >= max_out_len);
LSQ_DEBUG("***aes_aead_enc data %s", get_bin_str(plain, plain_len, 40));
ret = EVP_AEAD_CTX_seal(key, cypher, cypher_len, max_out_len,
nonce, nonce_len, plain, plain_len, ad, ad_len);
// LSQ_DEBUG("***aes_aead_enc nonce: %s", get_bin_str(nonce, nonce_len));
// LSQ_DEBUG("***aes_aead_enc AD: %s", get_bin_str(ad, ad_len));
// LSQ_DEBUG("***aes_aead_enc return %d", (ret ? 0 : -1));
if (ret)
{
LSQ_DEBUG("***aes_aead_enc succeed, cypher content %s",
get_bin_str(cypher, *cypher_len, 40));
return 0;
}
else
{
LSQ_DEBUG("***aes_aead_enc failed.");
return -1;
}
}
/* return 0 for OK */
int aes_aead_dec(EVP_AEAD_CTX *key,
const uint8_t *ad, size_t ad_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *cypher, size_t cypher_len,
uint8_t *plain, size_t *plain_len)
{
int ret = 0;
size_t max_out_len = *plain_len;
assert(max_out_len >= cypher_len);
LSQ_DEBUG("***aes_aead_dec data %s", get_bin_str(cypher, cypher_len, 40));
ret = EVP_AEAD_CTX_open(key, plain, plain_len, max_out_len,
nonce, nonce_len, cypher, cypher_len, ad, ad_len);
// LSQ_DEBUG("***aes_aead_dec nonce: %s", get_bin_str(nonce, nonce_len));
// LSQ_DEBUG("***aes_aead_dec AD: %s", get_bin_str(ad, ad_len));
// LSQ_DEBUG("***aes_aead_dec return %d", (ret ? 0 : -1));
if (ret)
{
LSQ_DEBUG("***aes_aead_dec succeed, plain content %s",
get_bin_str(plain, *plain_len, 20));
return 0;
}
else
{
LSQ_DEBUG("***aes_aead_dec failed.");
return -1;
}
}
/* aes 128, 16 bytes */
int aes_get_key_length()
{
return 16;
}
void gen_nonce_s(char *buf, int length)
{
rand_bytes(buf, length);
}
/* 32 bytes client nonce with 4 bytes tm, 8 bytes orbit */
void gen_nonce_c(unsigned char *buf, uint64_t orbit)
{
time_t tm = time(NULL);
unsigned char *p = buf;
memcpy(p, &tm, 4);
p += 4;
memcpy(p, &orbit, 8);
p += 8;
rand_bytes(p, 20);
p += 20;
}
EVP_PKEY *PEM_to_key(const char *buf, int len)
{
RSA *rsa = NULL;
EVP_PKEY *key = EVP_PKEY_new();
BIO *bio = BIO_new_mem_buf(buf, len);
if (!bio || !key)
return NULL;
rsa = PEM_read_bio_RSAPrivateKey(bio, &rsa, NULL, NULL);
if (!rsa)
return NULL;
EVP_PKEY_assign_RSA(key, rsa);
return key;
}
/* type 0 DER, 1: PEM */
X509 *bio_to_crt(const void *buf, int len, int type)
{
X509 *crt = NULL;
BIO *bio = BIO_new_mem_buf(buf, len);
if (bio == NULL)
return NULL;
if (type == 0)
crt = d2i_X509_bio(bio, NULL);
else
crt = PEM_read_bio_X509(bio, &crt, 0 , NULL);
BIO_free(bio);
return crt;
}
int read_rsa_priv_key(const uint8_t *buf, int len, EVP_PKEY *pkey)
{
RSA *rsa = RSA_private_key_from_bytes(buf, len);
if (!rsa)
return -1;
return EVP_PKEY_assign_RSA(pkey, rsa);
}
int gen_prof(const uint8_t *chlo_data, size_t chlo_data_len,
const uint8_t *scfg_data, uint32_t scfg_data_len,
const EVP_PKEY *priv_key, uint8_t *buf, size_t *buf_len)
{
uint8_t chlo_hash[32] = {0};
size_t chlo_hash_len = 32; /* SHA256 */
EVP_MD_CTX sign_context;
EVP_PKEY_CTX* pkey_ctx = NULL;
sha256(chlo_data, chlo_data_len, chlo_hash);
EVP_MD_CTX_init(&sign_context);
if (!EVP_DigestSignInit(&sign_context, &pkey_ctx, EVP_sha256(), NULL, (EVP_PKEY *)priv_key))
return -1;
EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING);
EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1);
if (!EVP_DigestSignUpdate(&sign_context, s_hs_signature, sizeof(s_hs_signature)) ||
!EVP_DigestSignUpdate(&sign_context, (const uint8_t*)(&chlo_hash_len), 4) ||
!EVP_DigestSignUpdate(&sign_context, chlo_hash, chlo_hash_len) ||
!EVP_DigestSignUpdate(&sign_context, scfg_data, scfg_data_len))
{
return -1;
}
size_t len = 0;
if (!EVP_DigestSignFinal(&sign_context, NULL, &len)) {
return -1;
}
if (len > *buf_len)
return -2;
if (buf)
EVP_DigestSignFinal(&sign_context, buf, buf_len);
EVP_MD_CTX_cleanup(&sign_context);
return 0;
}
int verify_cert(const char *buf, int len)
{
//X509_verify_cert();
return 0;
}
int verify_prof(const uint8_t *chlo_data, size_t chlo_data_len, lsquic_str_t * scfg,
const EVP_PKEY *pub_key, const uint8_t *buf, size_t len)
{
return verify_prof0(chlo_data, chlo_data_len,
(const uint8_t *)lsquic_str_buf(scfg),
lsquic_str_len(scfg), pub_key, buf, len);
}
/* -3 internal error, -1: verify failed, 0: Success */
int verify_prof0(const uint8_t *chlo_data, size_t chlo_data_len,
const uint8_t *scfg_data, uint32_t scfg_data_len,
const EVP_PKEY *pub_key, const uint8_t *buf, size_t len)
{
uint8_t chlo_hash[32] = {0};
size_t chlo_hash_len = 32; /* SHA256 */
EVP_MD_CTX sign_context;
EVP_PKEY_CTX* pkey_ctx = NULL;
int ret = 0;
EVP_MD_CTX_init(&sign_context);
sha256(chlo_data, chlo_data_len, chlo_hash);
// discarding const below to quiet compiler warning on call to ssl library code
if (!EVP_DigestVerifyInit(&sign_context, &pkey_ctx, EVP_sha256(), NULL, (EVP_PKEY *)pub_key))
return -4;
EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING);
EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1);
if (!EVP_DigestVerifyUpdate(&sign_context, s_hs_signature, sizeof(s_hs_signature)) ||
!EVP_DigestVerifyUpdate(&sign_context, (const uint8_t*)(&chlo_hash_len), 4) ||
!EVP_DigestVerifyUpdate(&sign_context, chlo_hash, chlo_hash_len) ||
!EVP_DigestVerifyUpdate(&sign_context, scfg_data, scfg_data_len))
{
return -3; /* set to -3, to avoid same as "not enough data" -2 */
}
ret = EVP_DigestVerifyFinal(&sign_context, buf, len);
EVP_MD_CTX_cleanup(&sign_context);
if (ret == 1)
return 0; //OK
else
return -1; //failed
}
void crypto_init(void *seed, int seed_len)
{
if (crypto_inited)
return ;
//SSL_library_init();
CRYPTO_library_init();
RAND_seed(seed, seed_len);
#if defined( __x86_64 )||defined( __x86_64__ )
make_uint128(&s_prime, 16777216, 315);
make_uint128(&s_init_hash, 7809847782465536322, 7113472399480571277);
#endif
/* MORE .... */
crypto_inited = 1;
}

View file

@ -0,0 +1,124 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef __LSQUIC_CRYPTO_H__
#define __LSQUIC_CRYPTO_H__
#include <stdint.h>
#define HS_PKT_HASH_LENGTH 12
#ifdef __cplusplus
extern "C" {
#endif
struct lsquic_str;
#if defined( __x86_64 )||defined( __x86_64__ )
typedef __uint128_t uint128;
#else
typedef struct uint128_st
{
uint64_t hi_;
uint64_t lo_;
} uint128;
#endif
void crypto_init();
/* XXX: why have a wrapper around RAND_bytes? */
void rand_bytes(void *data, int len);
int export_key_material_simple(unsigned char *ikm, uint32_t ikm_len,
unsigned char *salt, int salt_len,
char *label, uint32_t label_len,
const uint8_t *context, uint32_t context_len,
uint8_t *key, uint16_t key_len);
int export_key_material(const unsigned char *ikm, uint32_t ikm_len,
const unsigned char *salt, int salt_len,
const unsigned char *context, uint32_t context_len,
uint16_t c_key_len, uint8_t *c_key,
uint16_t s_key_len, uint8_t *s_key,
uint16_t c_key_iv_len, uint8_t *c_key_iv,
uint16_t s_key_iv_len, uint8_t *s_key_iv,
uint8_t *sub_key);
void c255_get_pub_key(unsigned char *priv_key, unsigned char pub_key[32]);
int c255_gen_share_key(unsigned char *priv_key, unsigned char *peer_pub_key, unsigned char *shared_key);
uint64_t fnv1a_64(const uint8_t * data, int len);
void fnv1a_64_s(const uint8_t * data, int len, char *md);
uint128 fnv1a_128(const uint8_t * data, int len);
void fnv1a_128_s(const uint8_t * data , int len, uint8_t *md);
uint128 fnv1a_128_2(const uint8_t * data1, int len1, const uint8_t * data2, int len2);
uint128 fnv1a_128_3(const uint8_t * data1, int len1,
const uint8_t * data2, int len2,
const uint8_t * data3, int len3);
void fnv1a_128_2_s(const uint8_t * data1, int len1, const uint8_t * data2, int len2, uint8_t *md);
void serialize_fnv128_short(uint128 v, uint8_t *md);
/* before session handshake complete */
int verify_hs_pkt(const uint8_t *pkg_data, size_t header_len, size_t pkg_len);
int update_hs_pkt_hash(uint8_t *pkg_data, int header_len, int pkg_len);
int get_hs_pkt_hash_len();
/*16 bytes of h outputted */
void sha256(const uint8_t *buf, int len, uint8_t *h);
/* Encrypt plaint text to cipher test */
int aes_aead_enc(EVP_AEAD_CTX *key,
const uint8_t *ad, size_t ad_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *plain, size_t plain_len,
uint8_t *cypher, size_t *cypher_len);
int aes_aead_dec(EVP_AEAD_CTX *key,
const uint8_t *ad, size_t ad_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *cypher, size_t cypher_len,
uint8_t *plain, size_t *plain_len);
int aes_get_key_length();
void gen_nonce_s(char *buf, int length);
/* 32 bytes client nonce with 4 bytes tm, 8 bytes orbit */
void gen_nonce_c(unsigned char *buf, uint64_t orbit);
EVP_PKEY *PEM_to_key(const char *buf, int len);
X509 *bio_to_crt(const void *buf, int len, int type);
int lshkdf_expand(const unsigned char *prk, const unsigned char *info, int info_len,
uint16_t c_key_len, uint8_t *c_key,
uint16_t s_key_len, uint8_t *s_key,
uint16_t c_key_iv_len, uint8_t *c_key_iv,
uint16_t s_key_iv_len, uint8_t *s_key_iv,
uint16_t sub_key_len, uint8_t *sub_key);
void lshkdf_extract(const unsigned char *ikm, int ikm_len, const unsigned char *salt,
int salt_len, unsigned char *prk);
int gen_prof(const uint8_t *chlo_data, size_t chlo_data_len,
const uint8_t *scfg_data, uint32_t scfg_data_len,
const EVP_PKEY *priv_key, uint8_t *buf, size_t *len);
int verify_prof0(const uint8_t *chlo_data, size_t chlo_data_len,
const uint8_t *scfg_data, uint32_t scfg_data_len,
const EVP_PKEY *pub_key, const uint8_t *buf, size_t len);
int verify_prof(const uint8_t *chlo_data, size_t chlo_data_len, struct lsquic_str * scfg,
const EVP_PKEY *pub_key, const uint8_t *buf, size_t len);
#ifdef __cplusplus
}
#endif
#endif //__LSQUIC_CRYPTO_H__

View file

@ -0,0 +1,198 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_cubic.c -- LSQUIC CUBIC implementation.
*/
#include <inttypes.h>
#include <math.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_cubic.h"
#include "lsquic_util.h"
#define LSQUIC_LOGGER_MODULE LSQLM_CUBIC
#define LSQUIC_LOG_CONN_ID cubic->cu_cid
#include "lsquic_logger.h"
#define FAST_CONVERGENCE 1
#define BETA 205 /* 205/1024 */
#define C 410 /* 410/1024 */
#define TWO_MINUS_BETA_OVER_TWO 922 /* 922/1024 */
#define ONE_MINUS_BETA 819 /* 819/1024 */
#define ONE_OVER_C 2560 /* 2560/1024 */
static void
cubic_reset (struct lsquic_cubic *cubic)
{
memset(cubic, 0, offsetof(struct lsquic_cubic, cu_cid));
cubic->cu_cwnd = 32;
cubic->cu_last_max_cwnd = 32;
}
static void
cubic_update (struct lsquic_cubic *cubic, lsquic_time_t now)
{
lsquic_time_t delta_t, t, target;
unsigned tcp_cwnd;
if (0 == cubic->cu_epoch_start)
{
cubic->cu_epoch_start = now;
if (cubic->cu_cwnd < cubic->cu_last_max_cwnd)
{
cubic->cu_K = cbrt((cubic->cu_last_max_cwnd - cubic->cu_cwnd) *
ONE_OVER_C / 1024) * 1000000;
cubic->cu_origin_point = cubic->cu_last_max_cwnd;
}
else
{
cubic->cu_K = 0;
cubic->cu_origin_point = cubic->cu_cwnd;
}
}
else if ((cubic->cu_flags & CU_SHIFT_EPOCH) && cubic->cu_app_limited)
{
LSQ_DEBUG("increment epoch_start by %"PRIu64" microseconds", now - cubic->cu_app_limited);
cubic->cu_epoch_start += now - cubic->cu_app_limited;
}
delta_t = now + cubic->cu_min_delay - cubic->cu_epoch_start;
if (delta_t < cubic->cu_K)
{
t = cubic->cu_K - delta_t;
t /= 62500;
target = cubic->cu_origin_point - C * t * t * t / 1024 / 4096;
}
else
{
t = delta_t - cubic->cu_K;
t /= 62500;
target = cubic->cu_origin_point + C * t * t * t / 1024 / 4096;
if (cubic->cu_flags & CU_TCP_FRIENDLY)
{
tcp_cwnd = cubic->cu_last_max_cwnd * ONE_MINUS_BETA / 1024 +
(delta_t - cubic->cu_K) * C / 1024 / cubic->cu_min_delay;
if (tcp_cwnd > target)
target = tcp_cwnd;
}
}
if (target == 0)
target = 1;
cubic->cu_cwnd = target;
}
void
lsquic_cubic_init_ext (struct lsquic_cubic *cubic, lsquic_cid_t cid,
enum cubic_flags flags)
{
cubic_reset(cubic);
cubic->cu_ssthresh = 10000; /* Emulate "unbounded" slow start */
cubic->cu_cid = cid;
cubic->cu_flags = flags;
LSQ_DEBUG("%s(cubic, %"PRIu64", 0x%X)", __func__, cid, flags);
#ifndef NDEBUG
{
const char *shift;
shift = getenv("LSQUIC_CUBIC_SHIFT_EPOCH");
if (shift)
{
if (atoi(shift))
cubic->cu_flags |= CU_SHIFT_EPOCH;
else
cubic->cu_flags &= ~CU_SHIFT_EPOCH;
}
}
#endif
LSQ_INFO("initialized");
}
#define LOG_CWND(c) do { \
if (LSQ_LOG_ENABLED(LSQ_LOG_INFO)) { \
lsquic_time_t now = lsquic_time_now(); \
now -= now % 100000; \
if (now > (c)->cu_last_logged) { \
LSQ_INFO("CWND: %u", (c)->cu_cwnd); \
(c)->cu_last_logged = now; \
} \
} \
} while (0)
void
lsquic_cubic_ack (struct lsquic_cubic *cubic, lsquic_time_t now,
lsquic_time_t rtt, int app_limited)
{
LSQ_DEBUG("%s(cubic, %"PRIu64", %"PRIu64", %d)", __func__, now, rtt,
app_limited);
if (0 == cubic->cu_min_delay || rtt < cubic->cu_min_delay)
{
cubic->cu_min_delay = rtt;
LSQ_INFO("min_delay: %"PRIu64, rtt);
}
if (cubic->cu_cwnd <= cubic->cu_ssthresh)
{
++cubic->cu_cwnd;
LSQ_DEBUG("ACK: slow threshold, cwnd: %u", cubic->cu_cwnd);
}
else
{
if (app_limited)
{
if (cubic->cu_flags & CU_SHIFT_EPOCH)
{
if (0 == cubic->cu_app_limited)
{
cubic->cu_app_limited = now;
LSQ_DEBUG("set app_limited to %"PRIu64, now);
}
}
else
cubic->cu_epoch_start = 0;
}
else
{
cubic_update(cubic, now);
cubic->cu_app_limited = 0;
}
LSQ_DEBUG("ACK: cwnd: %u", cubic->cu_cwnd);
}
LOG_CWND(cubic);
}
void
lsquic_cubic_loss (struct lsquic_cubic *cubic)
{
LSQ_DEBUG("%s(cubic)", __func__);
cubic->cu_epoch_start = 0;
cubic->cu_app_limited = 0;
if (FAST_CONVERGENCE && cubic->cu_cwnd < cubic->cu_last_max_cwnd)
cubic->cu_last_max_cwnd = cubic->cu_cwnd * TWO_MINUS_BETA_OVER_TWO / 1024;
else
cubic->cu_last_max_cwnd = cubic->cu_cwnd;
cubic->cu_cwnd = cubic->cu_cwnd * ONE_MINUS_BETA / 1024;
cubic->cu_ssthresh = cubic->cu_cwnd;
LSQ_INFO("loss detected, last_max_cwnd: %u, cwnd: %u",
cubic->cu_last_max_cwnd, cubic->cu_cwnd);
LOG_CWND(cubic);
}
void
lsquic_cubic_timeout (struct lsquic_cubic *cubic)
{
LSQ_DEBUG("%s(cubic)", __func__);
cubic_reset(cubic);
cubic->cu_ssthresh = cubic->cu_cwnd;
LSQ_INFO("timeout, cwnd: %u", cubic->cu_cwnd);
LOG_CWND(cubic);
}

View file

@ -0,0 +1,49 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_cubic.h -- CUBIC congestion control protocol.
*/
#ifndef LSQUIC_CUBIC_H
#define LSQUIC_CUBIC_H 1
struct lsquic_cubic {
lsquic_time_t cu_min_delay;
lsquic_time_t cu_epoch_start;
lsquic_time_t cu_K;
lsquic_time_t cu_app_limited;
unsigned cu_origin_point;
unsigned cu_last_max_cwnd;
unsigned cu_cwnd;
unsigned cu_ssthresh;
lsquic_cid_t cu_cid; /* Used for logging */
enum cubic_flags {
CU_TCP_FRIENDLY = (1 << 0),
CU_SHIFT_EPOCH = (1 << 1),
} cu_flags;
lsquic_time_t cu_last_logged;
};
#define DEFAULT_CUBIC_FLAGS (CU_TCP_FRIENDLY|CU_SHIFT_EPOCH)
void
lsquic_cubic_init_ext (struct lsquic_cubic *, lsquic_cid_t, enum cubic_flags);
#define lsquic_cubic_init(cubic, cid) \
lsquic_cubic_init_ext(cubic, cid, DEFAULT_CUBIC_FLAGS)
void
lsquic_cubic_ack (struct lsquic_cubic *cubic, lsquic_time_t now,
lsquic_time_t rtt, int app_limited);
void
lsquic_cubic_loss (struct lsquic_cubic *cubic);
void
lsquic_cubic_timeout (struct lsquic_cubic *cubic);
#define lsquic_cubic_get_cwnd(c) (+(c)->cu_cwnd)
#define lsquic_cubic_in_slow_start(cubic) \
((cubic)->cu_cwnd < (cubic)->cu_ssthresh)
#endif

View file

@ -0,0 +1,81 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_data_in_if.h -- DATA in interface
*/
#ifndef LSQUIC_DATA_IN_IF_H
#define LSQUIC_DATA_IN_IF_H 1
struct data_frame;
struct data_in;
struct lsquic_conn_public;
struct stream_frame;
enum ins_frame
{
INS_FRAME_OK,
INS_FRAME_ERR,
INS_FRAME_DUP,
};
struct data_in_iface
{
void
(*di_destroy) (struct data_in *);
int
(*di_empty) (struct data_in *);
/* The caller releases control of stream frame. Do not reference it
* after the call.
*/
enum ins_frame
(*di_insert_frame) (struct data_in *, struct stream_frame *,
uint64_t read_offset);
struct data_frame *
(*di_get_frame) (struct data_in *, uint64_t read_offset);
void
(*di_frame_done) (struct data_in *, struct data_frame *);
/* Creates a new data_in object, feeds its stream frames to it, deletes
* itself and returns the new object.
*/
struct data_in *
(*di_switch_impl) (struct data_in *, uint64_t read_offset);
};
struct data_in
{
const struct data_in_iface *di_if;
enum {
/* If DI_SWITCH_IMPL is set, switching data_in implementation is
* recommended in order to get better performance for current
* incoming stream frame scenario. Check the value of this flag
* after calls to di_insert_frame() and di_frame_done().
*/
DI_SWITCH_IMPL = (1 << 0),
} di_flags;
};
struct data_in *
data_in_nocopy_new (struct lsquic_conn_public *, uint32_t stream_id);
struct data_in *
data_in_hash_new (struct lsquic_conn_public *, uint32_t stream_id,
uint64_t byteage);
enum ins_frame
data_in_hash_insert_data_frame (struct data_in *data_in,
const struct data_frame *data_frame, uint64_t read_offset);
struct data_in *
data_in_error_new ();
#endif

View file

@ -0,0 +1,81 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_di_error.c -- A placeholder when things go wrong
*
* This object is used in order to avoid dereferencing NULLs in stream.c
*/
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "lsquic_data_in_if.h"
static const struct data_in_iface di_if_error;
static const struct data_in error_data_in = {
.di_if = &di_if_error,
.di_flags = 0,
};
struct data_in *
data_in_error_new (struct lsquic_conn_public *conn_pub)
{
return (struct data_in *) &error_data_in;
}
static void
error_di_destroy (struct data_in *data_in)
{
}
static enum ins_frame
error_di_insert_frame (struct data_in *data_in,
struct stream_frame *new_frame, uint64_t read_offset)
{
return INS_FRAME_ERR;
}
static struct data_frame *
error_di_get_frame (struct data_in *data_in, uint64_t read_offset)
{
return NULL;
}
static void
error_di_frame_done (struct data_in *data_in, struct data_frame *data_frame)
{
}
static int
error_di_empty (struct data_in *data_in)
{
return 1;
}
struct data_in *
error_di_switch_impl (struct data_in *data_in, uint64_t read_offset)
{
assert(0);
return data_in;
}
static const struct data_in_iface di_if_error = {
.di_destroy = error_di_destroy,
.di_empty = error_di_empty,
.di_frame_done = error_di_frame_done,
.di_get_frame = error_di_get_frame,
.di_insert_frame = error_di_insert_frame,
.di_switch_impl = error_di_switch_impl,
};

View file

@ -0,0 +1,617 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_di_hash.c -- Copy incoming data into a hash
*
* While this implementation copies the data, its memory use is limited,
* which makes it a good choice when we have a lot of stream frames
* coming in.
*
* Another difference is that it does not check for frame overlap, which
* is something that is present in Chrome, but it is not required by QUIC.
*/
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_conn_flow.h"
#include "lsquic_packet_in.h"
#include "lsquic_rtt.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_mm.h"
#include "lsquic_malo.h"
#include "lsquic_conn.h"
#include "lsquic_conn_public.h"
#include "lsquic_data_in_if.h"
#define LSQUIC_LOGGER_MODULE LSQLM_DI
#define LSQUIC_LOG_CONN_ID hdi->hdi_conn_pub->lconn->cn_cid
#define LSQUIC_LOG_STREAM_ID hdi->hdi_stream_id
#include "lsquic_logger.h"
#define N_DB_SETS 57
#define DB_DATA_SIZE (0x1000 - sizeof(TAILQ_ENTRY(data_block)) - \
sizeof(uint64_t) - N_DB_SETS * sizeof(uint64_t))
struct data_block
{
TAILQ_ENTRY(data_block) db_next;
uint64_t db_off;
uint64_t db_set[N_DB_SETS]; /* bit for each valid byte */
unsigned char db_data[DB_DATA_SIZE];
};
typedef char db_set_covers_all_db_data[(N_DB_SETS * 64 >= DB_DATA_SIZE) - 1];
typedef char db_set_no_waste[(N_DB_SETS * 64 - 64 <= DB_DATA_SIZE) - 1];
typedef char db_block_is_4K[(sizeof(struct data_block) == 0x1000) - 1];
TAILQ_HEAD(dblock_head, data_block);
static const struct data_in_iface di_if_hash;
struct hash_data_in
{
struct data_in hdi_data_in;
struct lsquic_conn_public *hdi_conn_pub;
uint64_t hdi_fin_off;
struct dblock_head *hdi_buckets;
struct data_block *hdi_last_block;
struct data_frame hdi_data_frame;
uint32_t hdi_stream_id;
unsigned hdi_count;
unsigned hdi_nbits;
enum {
HDI_FIN = (1 << 0),
} hdi_flags;
};
#define HDI_PTR(data_in) (struct hash_data_in *) \
((unsigned char *) (data_in) - offsetof(struct hash_data_in, hdi_data_in))
#define N_BUCKETS(n_bits) (1U << (n_bits))
#define BUCKNO(n_bits, off) ((off / DB_DATA_SIZE) & (N_BUCKETS(n_bits) - 1))
static unsigned
my_log2 /* silly name to suppress compiler warning */ (unsigned sz)
{
#if __GNUC__
unsigned clz = __builtin_clz(sz);
return 32 - clz;
#else
unsigned clz;
size_t y;
clz = 32;
y = sz >> 16; if (y) { clz -= 16; sz = y; }
y = sz >> 8; if (y) { clz -= 8; sz = y; }
y = sz >> 4; if (y) { clz -= 4; sz = y; }
y = sz >> 2; if (y) { clz -= 2; sz = y; }
y = sz >> 1; if (y) return 32 - clz + 1;
return 32 - clz + sz;
#endif
}
struct data_in *
data_in_hash_new (struct lsquic_conn_public *conn_pub, uint32_t stream_id,
uint64_t byteage)
{
struct hash_data_in *hdi;
unsigned n;
hdi = malloc(sizeof(*hdi));
if (!hdi)
return NULL;
hdi->hdi_data_in.di_if = &di_if_hash;
hdi->hdi_data_in.di_flags = 0;
hdi->hdi_conn_pub = conn_pub;
hdi->hdi_stream_id = stream_id;
hdi->hdi_fin_off = 0;
hdi->hdi_flags = 0;
hdi->hdi_last_block = NULL;
if (byteage >= DB_DATA_SIZE /* __builtin_clz is undefined if
argument is 0 */)
hdi->hdi_nbits = my_log2(byteage / DB_DATA_SIZE) + 2;
else
hdi->hdi_nbits = 3;
hdi->hdi_count = 0;
hdi->hdi_buckets = malloc(sizeof(hdi->hdi_buckets[0]) *
N_BUCKETS(hdi->hdi_nbits));
if (!hdi->hdi_buckets)
{
free(hdi);
return NULL;
}
for (n = 0; n < N_BUCKETS(hdi->hdi_nbits); ++n)
TAILQ_INIT(&hdi->hdi_buckets[n]);
return &hdi->hdi_data_in;
}
static void
hash_di_destroy (struct data_in *data_in)
{
struct hash_data_in *const hdi = HDI_PTR(data_in);
struct data_block *block;
unsigned n;
for (n = 0; n < N_BUCKETS(hdi->hdi_nbits); ++n)
{
while ((block = TAILQ_FIRST(&hdi->hdi_buckets[n])))
{
TAILQ_REMOVE(&hdi->hdi_buckets[n], block, db_next);
free(block);
}
}
free(hdi->hdi_buckets);
free(hdi);
}
static int
hash_grow (struct hash_data_in *hdi)
{
struct dblock_head *new_buckets, *new[2];
struct data_block *block;
unsigned n, old_nbits;
int idx;
old_nbits = hdi->hdi_nbits;
LSQ_DEBUG("doubling number of buckets to %u", N_BUCKETS(old_nbits + 1));
new_buckets = malloc(sizeof(hdi->hdi_buckets[0])
* N_BUCKETS(old_nbits + 1));
if (!new_buckets)
{
LSQ_WARN("malloc failed: potential trouble ahead");
return -1;
}
for (n = 0; n < N_BUCKETS(old_nbits); ++n)
{
new[0] = &new_buckets[n];
new[1] = &new_buckets[n + N_BUCKETS(old_nbits)];
TAILQ_INIT(new[0]);
TAILQ_INIT(new[1]);
while ((block = TAILQ_FIRST(&hdi->hdi_buckets[n])))
{
TAILQ_REMOVE(&hdi->hdi_buckets[n], block, db_next);
idx = (BUCKNO(old_nbits + 1, block->db_off) >> old_nbits) & 1;
TAILQ_INSERT_TAIL(new[idx], block, db_next);
}
}
free(hdi->hdi_buckets);
hdi->hdi_nbits = old_nbits + 1;
hdi->hdi_buckets = new_buckets;
return 0;
}
static int
hash_insert (struct hash_data_in *hdi, struct data_block *block)
{
unsigned buckno;
if (hdi->hdi_count >= N_BUCKETS(hdi->hdi_nbits) / 2 && 0 != hash_grow(hdi))
return -1;
buckno = BUCKNO(hdi->hdi_nbits, block->db_off);
TAILQ_INSERT_TAIL(&hdi->hdi_buckets[buckno], block, db_next);
++hdi->hdi_count;
return 0;
}
static struct data_block *
hash_find (const struct hash_data_in *hdi, uint64_t off)
{
struct data_block *block;
unsigned buckno;
buckno = BUCKNO(hdi->hdi_nbits, off);
TAILQ_FOREACH(block, &hdi->hdi_buckets[buckno], db_next)
if (off == block->db_off)
return block;
return NULL;
}
static void
hash_remove (struct hash_data_in *hdi, struct data_block *block)
{
unsigned buckno;
buckno = BUCKNO(hdi->hdi_nbits, block->db_off);
TAILQ_REMOVE(&hdi->hdi_buckets[buckno], block, db_next);
--hdi->hdi_count;
}
static struct data_block *
new_block (struct hash_data_in *hdi, uint64_t off)
{
struct data_block *block;
assert(0 == off % DB_DATA_SIZE);
block = malloc(sizeof(*block));
if (!block)
return NULL;
block->db_off = off;
if (0 != hash_insert(hdi, block))
{
free(block);
return NULL;
}
memset(block->db_set, 0, sizeof(block->db_set));
return block;
}
static unsigned
block_write (struct data_block *block, unsigned block_off,
const unsigned char *data, unsigned data_sz)
{
const unsigned char *begin, *end;
unsigned set, bit, n_full_sets, n;
uint64_t mask;
if (data_sz > DB_DATA_SIZE - block_off)
data_sz = DB_DATA_SIZE - block_off;
begin = data;
end = begin + data_sz;
set = block_off >> 6;
bit = block_off & 0x3F;
assert(set <= N_DB_SETS);
if (bit)
{
n = 64 - bit;
if (n > data_sz)
n = data_sz;
mask = ~((1ULL << bit ) - 1)
& ((1ULL << (bit + n - 1)) | ((1ULL << (bit + n - 1)) - 1));
block->db_set[ set ] |= mask;
memcpy(block->db_data + block_off, data, n);
data += n;
block_off += n;
++set;
}
n_full_sets = (end - data) >> 6;
if (n_full_sets)
{
memcpy(block->db_data + block_off, data, n_full_sets * 64);
data += n_full_sets * 64;
block_off += n_full_sets * 64;
memset(&block->db_set[ set ], 0xFF, n_full_sets * 8);
set += n_full_sets;
}
if (data < end)
{
assert(end - data < 64);
block->db_set[ set ] |= ((1ULL << (end - data)) - 1);
memcpy(block->db_data + block_off, data, end - data);
data = end;
}
assert(set <= N_DB_SETS);
return data - begin;
}
static int
has_bytes_after (const struct data_block *block, unsigned off)
{
unsigned bit, set;
int has;
set = off >> 6;
bit = off & 0x3F;
has = 0 != (block->db_set[ set ] >> bit);
++set;
for ( ; set < N_DB_SETS; ++set)
has += 0 != block->db_set[ set ];
return has > 0;
}
enum ins_frame
data_in_hash_insert_data_frame (struct data_in *data_in,
const struct data_frame *data_frame, uint64_t read_offset)
{
struct hash_data_in *const hdi = HDI_PTR(data_in);
struct data_block *block;
uint64_t key, off, diff, fin_off;
const unsigned char *data;
unsigned size, nw;
if (data_frame->df_offset + data_frame->df_size < read_offset)
return INS_FRAME_DUP;
if ((hdi->hdi_flags & HDI_FIN) &&
(
(data_frame->df_fin &&
data_frame->df_offset + data_frame->df_size != hdi->hdi_fin_off)
||
data_frame->df_offset + data_frame->df_size > hdi->hdi_fin_off
)
)
{
return INS_FRAME_ERR;
}
if (data_frame->df_offset < read_offset)
{
diff = read_offset - data_frame->df_offset;
assert(diff <= data_frame->df_size);
size = data_frame->df_size - diff;
off = data_frame->df_offset + diff;
data = data_frame->df_data + diff;
}
else
{
size = data_frame->df_size;
off = data_frame->df_offset;
data = data_frame->df_data;
}
key = off - (off % DB_DATA_SIZE);
do
{
block = hash_find(hdi, key);
if (!block)
{
block = new_block(hdi, key);
if (!block)
return INS_FRAME_ERR;
}
nw = block_write(block, off % DB_DATA_SIZE, data, size);
size -= nw;
off += nw;
data += nw;
key += DB_DATA_SIZE;
}
while (size > 0);
if (data_frame->df_fin)
{
fin_off = data_frame->df_offset + data_frame->df_size;
if (has_bytes_after(block, fin_off - block->db_off) ||
hash_find(hdi, key))
{
return INS_FRAME_ERR;
}
hdi->hdi_flags |= HDI_FIN;
hdi->hdi_fin_off = fin_off;
}
return INS_FRAME_OK;
}
static enum ins_frame
hash_di_insert_frame (struct data_in *data_in,
struct stream_frame *new_frame, uint64_t read_offset)
{
struct hash_data_in *const hdi = HDI_PTR(data_in);
const struct data_frame *const data_frame = &new_frame->data_frame;
enum ins_frame ins;
ins = data_in_hash_insert_data_frame(data_in, data_frame, read_offset);
lsquic_packet_in_put(hdi->hdi_conn_pub->mm, new_frame->packet_in);
lsquic_malo_put(new_frame);
return ins;
}
#if __GNUC__
# define ctz __builtin_ctzll
#else
static unsigned
ctz (unsigned long long x)
{
unsigned n = 0;
if (0 == (x & ((1ULL << 32) - 1))) { n += 32; x >>= 32; }
if (0 == (x & ((1ULL << 16) - 1))) { n += 16; x >>= 16; }
if (0 == (x & ((1ULL << 8) - 1))) { n += 8; x >>= 8; }
if (0 == (x & ((1ULL << 4) - 1))) { n += 4; x >>= 4; }
if (0 == (x & ((1ULL << 2) - 1))) { n += 2; x >>= 2; }
if (0 == (x & ((1ULL << 1) - 1))) { n += 1; x >>= 1; }
return n;
}
#endif
static unsigned
n_avail_bytes (const struct data_block *block, unsigned set, unsigned bit)
{
unsigned count;
uint64_t part;
part = ~(block->db_set[ set ] >> bit);
if (part)
{
count = ctz(part);
if (count < 64 - bit)
return count;
}
else
count = 64;
++set;
for ( ; set < N_DB_SETS && ~0ULL == block->db_set[ set ]; ++set)
count += 64;
if (set < N_DB_SETS)
{
part = ~block->db_set[ set ];
if (part)
count += ctz(part);
else
count += 64;
}
return count;
}
/* Data block is readable if there is at least one readable byte at
* `read_offset' or there is FIN at that offset.
*/
static int
setup_data_frame (struct hash_data_in *hdi, const uint64_t read_offset,
struct data_block *block)
{
unsigned set, bit;
uint64_t offset;
offset = read_offset % DB_DATA_SIZE;
set = offset >> 6;
bit = offset & 0x3F;
if (block->db_set[ set ] & (1ULL << bit))
{
hdi->hdi_last_block = block;
hdi->hdi_data_frame.df_data = block->db_data;
hdi->hdi_data_frame.df_offset = block->db_off;
hdi->hdi_data_frame.df_read_off = offset;
hdi->hdi_data_frame.df_size = offset +
n_avail_bytes(block, set, bit);
hdi->hdi_data_frame.df_fin =
(hdi->hdi_flags & HDI_FIN) &&
hdi->hdi_data_frame.df_read_off +
hdi->hdi_data_frame.df_size == hdi->hdi_fin_off;
return 1;
}
else if ((hdi->hdi_flags & HDI_FIN) && read_offset == hdi->hdi_fin_off)
{
hdi->hdi_last_block = block;
hdi->hdi_data_frame.df_data = NULL;
hdi->hdi_data_frame.df_offset = block->db_off;
hdi->hdi_data_frame.df_read_off = offset;
hdi->hdi_data_frame.df_size = offset;
hdi->hdi_data_frame.df_fin = 1;
return 1;
}
else
return 0;
}
static struct data_frame *
hash_di_get_frame (struct data_in *data_in, uint64_t read_offset)
{
struct hash_data_in *const hdi = HDI_PTR(data_in);
struct data_block *block;
uint64_t key;
key = read_offset - (read_offset % DB_DATA_SIZE);
block = hash_find(hdi, key);
if (!block)
{
if ((hdi->hdi_flags & HDI_FIN) && read_offset == hdi->hdi_fin_off)
{
hdi->hdi_last_block = NULL;
hdi->hdi_data_frame.df_data = NULL;
hdi->hdi_data_frame.df_offset = read_offset -
read_offset % DB_DATA_SIZE;
hdi->hdi_data_frame.df_read_off = 0;
hdi->hdi_data_frame.df_size = 0;
hdi->hdi_data_frame.df_fin = 1;
return &hdi->hdi_data_frame;
}
else
return NULL;
}
if (setup_data_frame(hdi, read_offset, block))
return &hdi->hdi_data_frame;
else
return NULL;
}
static void
hash_di_frame_done (struct data_in *data_in, struct data_frame *data_frame)
{
struct hash_data_in *const hdi = HDI_PTR(data_in);
struct data_block *const block = hdi->hdi_last_block;
if (block)
{
if (data_frame->df_read_off == DB_DATA_SIZE ||
!has_bytes_after(block, data_frame->df_read_off))
{
hash_remove(hdi, block);
free(block);
if (0 == hdi->hdi_count && 0 == (hdi->hdi_flags & HDI_FIN))
{
LSQ_DEBUG("hash empty, want to switch");
hdi->hdi_data_in.di_flags |= DI_SWITCH_IMPL;
}
}
}
else
assert(data_frame->df_fin && data_frame->df_size == 0);
}
static int
hash_di_empty (struct data_in *data_in)
{
struct hash_data_in *const hdi = HDI_PTR(data_in);
return hdi->hdi_count == 0;
}
struct data_in *
hash_di_switch_impl (struct data_in *data_in, uint64_t read_offset)
{
struct hash_data_in *const hdi = HDI_PTR(data_in);
struct data_in *new_data_in;
assert(hdi->hdi_count == 0);
new_data_in = data_in_nocopy_new(hdi->hdi_conn_pub, hdi->hdi_stream_id);
data_in->di_if->di_destroy(data_in);
return new_data_in;
}
static const struct data_in_iface di_if_hash = {
.di_destroy = hash_di_destroy,
.di_empty = hash_di_empty,
.di_frame_done = hash_di_frame_done,
.di_get_frame = hash_di_get_frame,
.di_insert_frame = hash_di_insert_frame,
.di_switch_impl = hash_di_switch_impl,
};

View file

@ -0,0 +1,426 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_di_nocopy.c -- The "no-copy" data in stream.
*
* Data from packets is not copied: the packets are referenced by stream
* frames. When all data from stream frame is read, the frame is released
* and packet reference count is decremented, which possibly results in
* packet being released as well.
*
* This approach works well in regular circumstances; there are two scenarios
* when it does not:
*
* A. If we have many out-of-order frames, insertion into the list becomes
* expensive. In the degenerate case, we'd have to traverse the whole
* list to find appropriate position.
*
* B. Having many frames ties up resources, as each frame keeps a reference
* to the packet that contains it. This is a possible attack vector:
* send many one-byte packets; a single hole at the beginning will stop
* the server from being able to read the stream, thus tying up resources.
*
* If we detect that either (A) or (B) is true, we request that the stream
* switch to a more robust incoming stream frame handler by setting
* DI_SWITCH_IMPL flag.
*
* For a small number of elements, (A) and (B) do not matter and the checks
* are not performed. This number is defined by EFF_CHECK_THRESH_LOW. On
* the other side of the spectrum, if the number of frames grows very high,
* we want to switch to a more memory-efficient implementation even if (A)
* and (B) are not true. EFF_CHECK_THRESH_HIGH defines this threshold.
*
* Between the low and high thresholds, we detect efficiency problems as
* follows.
*
* To detect (A), we count how many elements we have to traverse during
* insertion. If we have to traverse at least half the list
* EFF_FAR_TRAVERSE_COUNT in a row, DI_SWITCH_IMPL is issued.
*
* If average stream frame size is smaller than EFF_TINY_FRAME_SZ bytes,
* (B) condition is true. In addition, if there are more than EFF_MAX_HOLES
* in the stream, this is also indicative of (B).
*/
#include <assert.h>
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_conn_flow.h"
#include "lsquic_packet_in.h"
#include "lsquic_rtt.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_mm.h"
#include "lsquic_malo.h"
#include "lsquic_conn.h"
#include "lsquic_conn_public.h"
#include "lsquic_data_in_if.h"
#define LSQUIC_LOGGER_MODULE LSQLM_DI
#define LSQUIC_LOG_CONN_ID ncdi->ncdi_conn_pub->lconn->cn_cid
#define LSQUIC_LOG_STREAM_ID ncdi->ncdi_stream_id
#include "lsquic_logger.h"
/* If number of elements is at or below this number, we do not bother to check
* efficiency conditions.
*/
#define EFF_CHECK_THRESH_LOW 10
/* If number of elements is higher than this number, efficiency alert
* is issued unconditionally.
*/
#define EFF_CHECK_THRESH_HIGH 1000
/* Maximum number of consecutive far traversals */
#define EFF_FAR_TRAVERSE_COUNT 4
/* Maximum number of holes that is not deemed suspicious */
#define EFF_MAX_HOLES 5
/* What is deemed a tiny frame, in bytes. If it is a power of two, calculation
* is cheaper.
*/
#define EFF_TINY_FRAME_SZ 64
TAILQ_HEAD(stream_frames_tailq, stream_frame);
struct nocopy_data_in
{
struct stream_frames_tailq ncdi_frames_in;
struct data_in ncdi_data_in;
struct lsquic_conn_public *ncdi_conn_pub;
uint64_t ncdi_byteage;
uint32_t ncdi_stream_id;
unsigned ncdi_n_frames;
unsigned ncdi_n_holes;
unsigned ncdi_cons_far;
};
#define NCDI_PTR(data_in) (struct nocopy_data_in *) \
((unsigned char *) (data_in) - offsetof(struct nocopy_data_in, ncdi_data_in))
#define STREAM_FRAME_PTR(data_frame) (struct stream_frame *) \
((unsigned char *) (data_frame) - offsetof(struct stream_frame, data_frame))
static const struct data_in_iface di_if_nocopy;
struct data_in *
data_in_nocopy_new (struct lsquic_conn_public *conn_pub, uint32_t stream_id)
{
struct nocopy_data_in *ncdi;
ncdi = malloc(sizeof(*ncdi));
if (!ncdi)
return NULL;
TAILQ_INIT(&ncdi->ncdi_frames_in);
ncdi->ncdi_data_in.di_if = &di_if_nocopy;
ncdi->ncdi_data_in.di_flags = 0;
ncdi->ncdi_conn_pub = conn_pub;
ncdi->ncdi_stream_id = stream_id;
ncdi->ncdi_byteage = 0;
ncdi->ncdi_n_frames = 0;
ncdi->ncdi_n_holes = 0;
ncdi->ncdi_cons_far = 0;
return &ncdi->ncdi_data_in;
}
static void
nocopy_di_destroy (struct data_in *data_in)
{
struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
stream_frame_t *frame;
while ((frame = TAILQ_FIRST(&ncdi->ncdi_frames_in)))
{
TAILQ_REMOVE(&ncdi->ncdi_frames_in, frame, next_frame);
lsquic_packet_in_put(ncdi->ncdi_conn_pub->mm, frame->packet_in);
lsquic_malo_put(frame);
}
free(ncdi);
}
#if 1
#define CHECK_ORDER(ncdi)
#else
static int
ordered (const struct nocopy_data_in *ncdi)
{
const stream_frame_t *frame;
uint64_t off = 0;
int ordered = 1;
TAILQ_FOREACH(frame, &ncdi->ncdi_frames_in, next_frame)
{
ordered &= off <= frame->data_frame.df_offset;
off = frame->data_frame.df_offset;
}
return ordered;
}
#define CHECK_ORDER(ncdi) assert(ordered(ncdi))
#endif
/* To reduce the number of conditionals, logical operators have been replaced
* with arithmetic operators. Return value is an integer in range [0, 3].
* Bit 0 is set due to FIN in previous frame. If bit 1 is set, it means that
* it's a dup.
*/
static int
insert_frame (struct nocopy_data_in *ncdi, struct stream_frame *new_frame,
uint64_t read_offset, unsigned *p_n_frames)
{
int ins;
unsigned count;
stream_frame_t *prev_frame, *next_frame;
/* Find position in the list, going backwards. We go backwards because
* that is the most likely scenario.
*/
next_frame = TAILQ_LAST(&ncdi->ncdi_frames_in, stream_frames_tailq);
if (next_frame && new_frame->data_frame.df_offset < next_frame->data_frame.df_offset)
{
count = 1;
prev_frame = TAILQ_PREV(next_frame, stream_frames_tailq, next_frame);
for ( ; prev_frame &&
new_frame->data_frame.df_offset < next_frame->data_frame.df_offset;
next_frame = prev_frame,
prev_frame = TAILQ_PREV(prev_frame, stream_frames_tailq, next_frame))
{
if (new_frame->data_frame.df_offset >= prev_frame->data_frame.df_offset)
break;
++count;
}
}
else
{
count = 0;
prev_frame = NULL;
}
if (!prev_frame && next_frame && new_frame->data_frame.df_offset >=
next_frame->data_frame.df_offset)
{
prev_frame = next_frame;
next_frame = TAILQ_NEXT(next_frame, next_frame);
}
/* Perform checks */
if (prev_frame)
ins =
(((prev_frame->data_frame.df_offset == new_frame->data_frame.df_offset) &
(prev_frame->data_frame.df_size == new_frame->data_frame.df_size) &
(prev_frame->data_frame.df_fin == new_frame->data_frame.df_fin)) << 1) /* Duplicate */
| prev_frame->data_frame.df_fin /* FIN in the middle or dup */
| (prev_frame->data_frame.df_offset + prev_frame->data_frame.df_size
> new_frame->data_frame.df_offset) /* Overlap */
;
else
ins = 0;
if (next_frame)
ins |=
(((next_frame->data_frame.df_offset == new_frame->data_frame.df_offset) &
(next_frame->data_frame.df_size == new_frame->data_frame.df_size) &
(next_frame->data_frame.df_fin == new_frame->data_frame.df_fin)) << 1) /* Duplicate */
| (new_frame->data_frame.df_offset < read_offset) << 1 /* Duplicate */
| new_frame->data_frame.df_fin /* FIN in the middle or dup */
| (new_frame->data_frame.df_offset + new_frame->data_frame.df_size
> next_frame->data_frame.df_offset) /* Overlap */
;
else
ins |=
(new_frame->data_frame.df_offset < read_offset) << 1 /* Duplicate */
;
if (ins)
return ins;
if (prev_frame)
{
TAILQ_INSERT_AFTER(&ncdi->ncdi_frames_in, prev_frame, new_frame, next_frame);
ncdi->ncdi_n_holes += prev_frame->data_frame.df_offset +
prev_frame->data_frame.df_size != new_frame->data_frame.df_offset;
if (next_frame)
{
ncdi->ncdi_n_holes += new_frame->data_frame.df_offset +
new_frame->data_frame.df_size != next_frame->data_frame.df_offset;
--ncdi->ncdi_n_holes;
}
}
else
{
ncdi->ncdi_n_holes += next_frame && new_frame->data_frame.df_offset +
new_frame->data_frame.df_size != next_frame->data_frame.df_offset;
TAILQ_INSERT_HEAD(&ncdi->ncdi_frames_in, new_frame, next_frame);
}
CHECK_ORDER(ncdi);
++ncdi->ncdi_n_frames;
ncdi->ncdi_byteage += new_frame->data_frame.df_size;
*p_n_frames = count;
return 0;
}
static int
check_efficiency (struct nocopy_data_in *ncdi, unsigned count)
{
if (ncdi->ncdi_n_frames <= EFF_CHECK_THRESH_LOW)
{
ncdi->ncdi_cons_far = 0;
return 0;
}
if (ncdi->ncdi_n_frames > EFF_CHECK_THRESH_HIGH)
return 1;
if (count >= ncdi->ncdi_n_frames / 2)
{
++ncdi->ncdi_cons_far;
if (ncdi->ncdi_cons_far > EFF_FAR_TRAVERSE_COUNT)
return 1;
}
else
ncdi->ncdi_cons_far = 0;
if (ncdi->ncdi_n_holes > EFF_MAX_HOLES)
return 1;
if (ncdi->ncdi_byteage / EFF_TINY_FRAME_SZ < ncdi->ncdi_n_frames)
return 1;
return 0;
}
static void
set_eff_alert (struct nocopy_data_in *ncdi)
{
LSQ_DEBUG("low efficiency: n_frames: %u; n_holes: %u; cons_far: %u; "
"byteage: %"PRIu64, ncdi->ncdi_n_frames, ncdi->ncdi_n_holes,
ncdi->ncdi_cons_far, ncdi->ncdi_byteage);
ncdi->ncdi_data_in.di_flags |= DI_SWITCH_IMPL;
}
static enum ins_frame
nocopy_di_insert_frame (struct data_in *data_in,
struct stream_frame *new_frame, uint64_t read_offset)
{
struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
unsigned count;
int ins;
assert(0 == (new_frame->data_frame.df_fin & ~1));
ins = insert_frame(ncdi, new_frame, read_offset, &count);
switch (ins)
{
case 0:
if (check_efficiency(ncdi, count))
set_eff_alert(ncdi);
return INS_FRAME_OK;
case 2:
case 3:
lsquic_packet_in_put(ncdi->ncdi_conn_pub->mm, new_frame->packet_in);
lsquic_malo_put(new_frame);
return INS_FRAME_DUP;
default:
assert(1 == ins);
lsquic_packet_in_put(ncdi->ncdi_conn_pub->mm, new_frame->packet_in);
lsquic_malo_put(new_frame);
return INS_FRAME_ERR;
}
}
static struct data_frame *
nocopy_di_get_frame (struct data_in *data_in, uint64_t read_offset)
{
struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
struct stream_frame *frame = TAILQ_FIRST(&ncdi->ncdi_frames_in);
if (frame && frame->data_frame.df_offset +
frame->data_frame.df_read_off == read_offset)
return &frame->data_frame;
else
return NULL;
}
static void
nocopy_di_frame_done (struct data_in *data_in, struct data_frame *data_frame)
{
struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
struct stream_frame *const frame = STREAM_FRAME_PTR(data_frame), *first;
assert(data_frame->df_read_off == data_frame->df_size);
TAILQ_REMOVE(&ncdi->ncdi_frames_in, frame, next_frame);
first = TAILQ_FIRST(&ncdi->ncdi_frames_in);
ncdi->ncdi_n_holes -= first && frame->data_frame.df_offset +
frame->data_frame.df_size != first->data_frame.df_offset;
--ncdi->ncdi_n_frames;
ncdi->ncdi_byteage -= frame->data_frame.df_size;
lsquic_packet_in_put(ncdi->ncdi_conn_pub->mm, frame->packet_in);
lsquic_malo_put(frame);
}
static int
nocopy_di_empty (struct data_in *data_in)
{
struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
return TAILQ_EMPTY(&ncdi->ncdi_frames_in);
}
struct data_in *
nocopy_di_switch_impl (struct data_in *data_in, uint64_t read_offset)
{
struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
struct data_in *new_data_in;
stream_frame_t *frame;
enum ins_frame ins;
new_data_in = data_in_hash_new(ncdi->ncdi_conn_pub, ncdi->ncdi_stream_id,
ncdi->ncdi_byteage);
if (!new_data_in)
goto end;
while ((frame = TAILQ_FIRST(&ncdi->ncdi_frames_in)))
{
TAILQ_REMOVE(&ncdi->ncdi_frames_in, frame, next_frame);
ins = data_in_hash_insert_data_frame(new_data_in, &frame->data_frame,
read_offset);
lsquic_packet_in_put(ncdi->ncdi_conn_pub->mm, frame->packet_in);
lsquic_malo_put(frame);
if (INS_FRAME_ERR == ins)
{
new_data_in->di_if->di_destroy(new_data_in);
new_data_in = NULL;
goto end;
}
}
end:
data_in->di_if->di_destroy(data_in);
return new_data_in;
}
static const struct data_in_iface di_if_nocopy = {
.di_destroy = nocopy_di_destroy,
.di_empty = nocopy_di_empty,
.di_frame_done = nocopy_di_frame_done,
.di_get_frame = nocopy_di_get_frame,
.di_insert_frame = nocopy_di_insert_frame,
.di_switch_impl = nocopy_di_switch_impl,
};

View file

@ -0,0 +1,53 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <time.h>
#include "lsquic_eng_hist.h"
#if ENG_HIST_ENABLED
#define LSQUIC_LOGGER_MODULE LSQLM_ENG_HIST
#include "lsquic_logger.h"
static void
log_hist_slice (const struct hist_slice *slice, time_t t)
{
size_t strftime(char *s, size_t max, const char *format,
const struct tm *tm);
if (slice->sl_packets_in == 0 &&
slice->sl_packets_out == 0 &&
slice->sl_del_mini_conns == 0 &&
slice->sl_del_full_conns == 0)
return;
struct tm tm;
char timestr[sizeof("12:00:00")];
localtime_r(&t, &tm);
strftime(timestr, sizeof(timestr), "%T", &tm);
LSQ_DEBUG("%s: pi: %u; po: %u; +mc: %u; -mc: %u; +fc: %u; -fc: %u",
timestr,
slice->sl_packets_in,
slice->sl_packets_out,
slice->sl_new_mini_conns,
slice->sl_del_mini_conns,
slice->sl_new_full_conns,
slice->sl_del_full_conns);
}
void
eng_hist_log (const struct eng_hist *hist)
{
unsigned i, idx;
time_t t0 = time(NULL) - ENG_HIST_NELEMS + 1;
for (i = 0; i < ENG_HIST_NELEMS; ++i)
{
idx = (hist->eh_prev_idx + i + 1) & (ENG_HIST_NELEMS - 1);
if (i >= ENG_HIST_NELEMS - ENG_HIST_N_TO_PRINT)
log_hist_slice(&hist->eh_slices[idx], t0 + i);
}
}
#endif

View file

@ -0,0 +1,96 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_eng_hist.h - Engine history.
*
* Keep track of new and destroyed connections, packets in and packets out.
*/
#ifndef LSQUIC_ENG_HIST
#define LSQUIC_ENG_HIST
#define ENG_HIST_ENABLED 1
#define ENG_HIST_BITS 2
#define ENG_HIST_NELEMS (1 << ENG_HIST_BITS)
#ifndef ENG_HIST_N_TO_PRINT
/* By default, we do not print the whole history every second just
* the latest entry.
*/
# define ENG_HIST_N_TO_PRINT 1
#endif
/* Keeps history per slice of time -- one second */
struct hist_slice
{
unsigned sl_packets_in,
sl_packets_out,
sl_new_full_conns,
sl_new_mini_conns,
sl_del_full_conns,
sl_del_mini_conns;
};
struct eng_hist
{
struct hist_slice eh_slices[ENG_HIST_NELEMS];
unsigned eh_cur_idx,
eh_prev_idx;
};
#if ENG_HIST_ENABLED
/* Initialize engine history */
#define eng_hist_init(eh) do { \
memset(eh, 0, sizeof(*(eh))); \
(eh)->eh_cur_idx = (eh)->eh_prev_idx = \
time(NULL) & (ENG_HIST_NELEMS - 1); \
} while (0)
/* Clear slice at current index */
#define eng_hist_clear_cur(eh) do { \
memset(&(eh)->eh_slices[(eh)->eh_cur_idx], 0, \
sizeof(struct hist_slice)); \
} while (0)
void
eng_hist_log (const struct eng_hist *);
/* Switch to next slice if necessary */
#define eng_hist_tick(eh, now) do { \
if (0 == (now)) \
(eh)->eh_cur_idx = time(NULL) & (ENG_HIST_NELEMS - 1); \
else \
(eh)->eh_cur_idx = ((now) / 1000000) & (ENG_HIST_NELEMS - 1); \
if ((eh)->eh_cur_idx != (eh)->eh_prev_idx) \
{ \
eng_hist_log(eh); \
eng_hist_clear_cur(eh); \
(eh)->eh_prev_idx = (eh)->eh_cur_idx; \
} \
} while (0)
/* Increment element `what'. Slice increment is handled in this macro, too. */
#define eng_hist_inc(eh, now, what) do { \
eng_hist_tick(eh, now); \
++(eh)->eh_slices[(eh)->eh_cur_idx].what; \
} while (0)
#else /* !ENG_HIST_ENABLED */
#define eng_hist_init(eh)
#define eng_hist_clear_cur(eh)
#define eng_hist_tick(eh, now)
#define eng_hist_inc(eh, now, what)
#define eng_hist_log(eh)
#endif /* ENG_HIST_ENABLED */
#endif /* LSQUIC_ENG_HIST */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,75 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_engine_public.h -- Engine's "public interface"
*
*/
#ifndef LSQUIC_ENGINE_PUBLIC_H
#define LSQUIC_ENGINE_PUBLIC_H 1
struct lsquic_conn;
struct lsquic_engine;
struct lsquic_engine_public {
struct lsquic_mm enp_mm;
struct lsquic_engine_settings enp_settings;
const struct lsquic_packout_mem_if
*enp_pmi;
void *enp_pmi_ctx;
struct lsquic_engine *enp_engine;
enum {
ENPUB_PROC = (1 << 0), /* Being processed by one of the user-facing
* functions.
*/
} enp_flags;
unsigned char enp_ver_tags_buf[ sizeof(lsquic_ver_tag_t) * N_LSQVER ];
unsigned enp_ver_tags_len;
};
/* These values are printable ASCII characters for ease of printing the
* whole history in a single line of a log message. If connection was
* processed as result of being put onto the queue, the letter is converted
* to uppercase.
*
* The letters are assigned by first letter of the verb for most obvious
* and important actions, like "read" and "write" and other letters of
* the verb or some other letters for other actions.
*
* Each reason is either expected to produce user read from the stream
* or putting stream data into packet for sending out. This is documented
* in a separate comment column below.
*/
enum rw_reason
{
RW_REASON_EMPTY = '\0', /* No init required */
/* Expected action: */
RW_REASON_USER_WRITE = 'w', /* write */
RW_REASON_USER_WRITEV = 'v', /* write */
RW_REASON_USER_READ = 'r', /* write (WINDOW_UPDATE frame) */
RW_REASON_FLUSH = 'f', /* write */
RW_REASON_STREAM_CLOSE = 'c', /* write */
RW_REASON_RST_IN = 'n', /* read */
RW_REASON_STREAM_IN = 'd', /* read */
RW_REASON_RESET_EXT = 'e', /* write */
RW_REASON_WANTREAD = 'a', /* read */
RW_REASON_SHUTDOWN = 'u', /* write */
RW_REASON_WRITEFILE = 't', /* write */
RW_REASON_SENDFILE = 's', /* write */
};
/* Put connection onto Pending RW Queue if it is not already on it. If
* connection is being destroyed, this is a no-op.
* XXX Is the bit about "being destroyed" still true?
*/
void
lsquic_engine_add_conn_to_pend_rw (struct lsquic_engine_public *enpub,
lsquic_conn_t *conn, enum rw_reason);
/* Put connection onto Advisory Tick Time Queue if it is not already on it.
*/
void
lsquic_engine_add_conn_to_attq (struct lsquic_engine_public *enpub,
lsquic_conn_t *, lsquic_time_t);
#endif

View file

@ -0,0 +1,314 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <arpa/inet.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
#include "lsquic_parse.h"
#include "lsquic_frame_common.h"
#include "lsquic_frame_reader.h"
#include "lsquic_ev_log.h"
#define LSQUIC_LOGGER_MODULE LSQLM_EVENT
#include "lsquic_logger.h"
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
/* |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| */
/* Messages that do not include connection ID go above this point */
#define LSQUIC_LOG_CONN_ID cid
#define LCID(a...) LSQ_LOG2(LSQ_LOG_DEBUG, a) /* LCID: log with CID */
/* Messages that are to include connection ID go below this point */
/* |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| */
/* VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV */
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);
}
void
lsquic_ev_log_ack_frame_in (lsquic_cid_t cid, const struct ack_info *acki)
{
size_t sz;
char *buf;
if ((buf = acki2str(acki, &sz)))
{
LCID("ACK frame in: %.*s", (int) sz, buf);
free(buf);
}
}
void
lsquic_ev_log_stream_frame_in (lsquic_cid_t cid,
const struct stream_frame *frame)
{
LCID("STREAM frame in: stream %u; offset %"PRIu64"; size %"PRIu16
"; fin: %d", frame->stream_id, frame->data_frame.df_offset,
frame->data_frame.df_size, (int) frame->data_frame.df_fin);
}
void
lsquic_ev_log_stop_waiting_frame_in (lsquic_cid_t cid, lsquic_packno_t least)
{
LCID("STOP_WAITING frame in: least unacked packno %"PRIu64, least);
}
void
lsquic_ev_log_window_update_frame_in (lsquic_cid_t cid, uint32_t stream_id,
uint64_t offset)
{
LCID("WINDOW_UPDATE frame in: stream %"PRIu32"; offset %"PRIu64,
stream_id, offset);
}
void
lsquic_ev_log_blocked_frame_in (lsquic_cid_t cid, uint32_t stream_id)
{
LCID("BLOCKED frame in: stream %"PRIu32, stream_id);
}
void
lsquic_ev_log_connection_close_frame_in (lsquic_cid_t cid,
uint32_t error_code, int reason_len, const char *reason)
{
LCID("CONNECTION_CLOSE frame in: error code %"PRIu32", reason: %.*s",
error_code, reason_len, reason);
}
void
lsquic_ev_log_goaway_frame_in (lsquic_cid_t cid, uint32_t error_code,
uint32_t stream_id, int reason_len, const char *reason)
{
LCID("GOAWAY frame in: error code %"PRIu32", stream %"PRIu32
", reason: %.*s", error_code, stream_id, reason_len, reason);
}
void
lsquic_ev_log_rst_stream_frame_in (lsquic_cid_t cid, uint32_t stream_id,
uint64_t offset, uint32_t error_code)
{
LCID("RST_FRAME frame in: error code %"PRIu32", stream %"PRIu32
", offset: %"PRIu64, error_code, stream_id, offset);
}
void
lsquic_ev_log_padding_frame_in (lsquic_cid_t cid, size_t len)
{
LCID("PADDING frame in of %zd bytes", len);
}
void
lsquic_ev_log_ping_frame_in (lsquic_cid_t cid)
{
LCID("PING frame in");
}
void
lsquic_ev_log_packet_created (lsquic_cid_t cid,
const struct lsquic_packet_out *packet_out)
{
LCID("created packet %"PRIu64"; flags: version=%d, nonce=%d, conn_id=%d",
packet_out->po_packno,
!!(packet_out->po_flags & PO_VERSION),
!!(packet_out->po_flags & PO_NONCE),
!!(packet_out->po_flags & PO_CONN_ID));
}
void
lsquic_ev_log_packet_sent (lsquic_cid_t cid,
const struct lsquic_packet_out *packet_out)
{
char frames[lsquic_frame_types_str_sz];
if (lsquic_packet_out_verneg(packet_out))
LCID("sent version negotiation packet, size %hu",
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
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
* 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));
}
void
lsquic_ev_log_packet_not_sent (lsquic_cid_t cid,
const struct lsquic_packet_out *packet_out)
{
char frames[lsquic_frame_types_str_sz];
LCID("unsent 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 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));
}
void
lsquic_ev_log_http_headers_in (lsquic_cid_t cid, int is_server,
const struct uncompressed_headers *uh)
{
const char *cr, *p;
if (uh->uh_flags & UH_PP)
LCID("read push promise; stream %"PRIu32", promised stream %"PRIu32,
uh->uh_stream_id, uh->uh_oth_stream_id);
else
LCID("read %s headers; stream: %"PRIu32", depends on stream: %"PRIu32
", weight: %hu, exclusive: %d, fin: %d",
is_server ? "request" : "response",
uh->uh_stream_id, uh->uh_oth_stream_id, uh->uh_weight,
(int) uh->uh_exclusive, !!(uh->uh_flags & UH_FIN));
for (p = uh->uh_headers; p < uh->uh_headers + uh->uh_size; p = cr + 2)
{
cr = strchr(p, '\r');
if (cr && cr > p)
LCID(" %.*s", (int) (cr - p), p);
else
break;
}
}
void
lsquic_ev_log_generated_stream_frame (lsquic_cid_t cid,
const struct parse_funcs *pf, const unsigned char *buf, size_t bufsz)
{
struct stream_frame frame;
int len;
len = pf->pf_parse_stream_frame(buf, bufsz, &frame);
if (len > 0)
LCID("generated STREAM frame: stream %"PRIu32", offset: %"PRIu64
", size: %"PRIu16", fin: %d", frame.stream_id,
frame.data_frame.df_offset, frame.data_frame.df_size,
frame.data_frame.df_fin);
else
LSQ_LOG2(LSQ_LOG_WARN, "cannot parse STREAM frame");
}
void
lsquic_ev_log_generated_ack_frame (lsquic_cid_t cid, const struct parse_funcs *pf,
const unsigned char *ack_buf, size_t ack_buf_sz)
{
struct ack_info acki;
size_t sz;
char *buf;
int len;
len = pf->pf_parse_ack_frame(ack_buf, ack_buf_sz, &acki);
if (len < 0)
{
LSQ_LOG2(LSQ_LOG_WARN, "cannot parse ACK frame");
return;
}
if ((buf = acki2str(&acki, &sz)))
{
LCID("generated ACK frame: %.*s", (int) sz, buf);
free(buf);
}
}
void
lsquic_ev_log_generated_stop_waiting_frame (lsquic_cid_t cid,
lsquic_packno_t lunack)
{
LCID("generated STOP_WAITING frame; least unacked: %"PRIu64, lunack);
}
void
lsquic_ev_log_generated_http_headers (lsquic_cid_t cid, uint32_t stream_id,
int is_server, const struct http_prio_frame *prio_frame,
const struct lsquic_http_headers *headers)
{
uint32_t dep_stream_id;
int exclusive, i;
unsigned short weight;
if (is_server)
LCID("generated HTTP response HEADERS for stream %"PRIu32, stream_id);
else
{
memcpy(&dep_stream_id, prio_frame->hpf_stream_id, 4);
dep_stream_id = htonl(dep_stream_id);
exclusive = dep_stream_id >> 31;
dep_stream_id &= ~(1 << 31);
weight = prio_frame->hpf_weight + 1;
LCID("generated HTTP request HEADERS for stream %"PRIu32
", dep stream: %"PRIu32", weight: %hu, exclusive: %d", stream_id,
dep_stream_id, weight, exclusive);
}
for (i = 0; i < headers->count; ++i)
LCID(" %.*s: %.*s",
(int) headers->headers[i].name.iov_len,
(char *) headers->headers[i].name.iov_base,
(int) headers->headers[i].value.iov_len,
(char *) headers->headers[i].value.iov_base);
}
void
lsquic_ev_log_generated_http_push_promise (lsquic_cid_t cid,
uint32_t stream_id, uint32_t promised_stream_id,
const struct lsquic_http_headers *headers,
const struct lsquic_http_headers *extra_headers)
{
int i;
LCID("generated HTTP PUSH_PROMISE for stream %"PRIu32"; promised stream %"
PRIu32, stream_id, promised_stream_id);
for (i = 0; i < headers->count; ++i)
LCID(" %.*s: %.*s",
(int) headers->headers[i].name.iov_len,
(char *) headers->headers[i].name.iov_base,
(int) headers->headers[i].value.iov_len,
(char *) headers->headers[i].value.iov_base);
if (extra_headers)
for (i = 0; i < extra_headers->count; ++i)
LCID(" %.*s: %.*s",
(int) extra_headers->headers[i].name.iov_len,
(char *) extra_headers->headers[i].name.iov_base,
(int) extra_headers->headers[i].value.iov_len,
(char *) extra_headers->headers[i].value.iov_base);
}

View file

@ -0,0 +1,206 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_ev_log.h -- Event logger
*/
#ifndef LSQUIC_EV_LOG_H
#define LSQUIC_EV_LOG_H 1
#include "lsquic_int_types.h"
struct ack_info;
struct http_prio_frame;
struct lsquic_http_headers;
struct lsquic_packet_in;
struct lsquic_packet_out;
struct parse_funcs;
struct stream_frame;
struct uncompressed_headers;
/* Log a generic event not tied to any particular connection */
#define EV_LOG_GENERIC_EVENT(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_logger_log1(LSQ_LOG_DEBUG, LSQLM_EVENT, args); \
} while (0)
/* Log a generic event associated with connection `cid' */
#define EV_LOG_CONN_EVENT(cid, args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_logger_log2(LSQ_LOG_DEBUG, LSQLM_EVENT, cid, args); \
} while (0)
void
lsquic_ev_log_packet_in (lsquic_cid_t, const struct lsquic_packet_in *);
#define EV_LOG_PACKET_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_packet_in(args); \
} while (0)
void
lsquic_ev_log_ack_frame_in (lsquic_cid_t, const struct ack_info *);
#define EV_LOG_ACK_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_ack_frame_in(args); \
} while (0)
void
lsquic_ev_log_stream_frame_in (lsquic_cid_t, const struct stream_frame *);
#define EV_LOG_STREAM_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_stream_frame_in(args); \
} while (0)
void
lsquic_ev_log_window_update_frame_in (lsquic_cid_t, uint32_t stream_id,
uint64_t offset);
#define EV_LOG_WINDOW_UPDATE_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_window_update_frame_in(args); \
} while (0)
void
lsquic_ev_log_blocked_frame_in (lsquic_cid_t, uint32_t stream_id);
#define EV_LOG_BLOCKED_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_blocked_frame_in(args); \
} while (0)
void
lsquic_ev_log_stop_waiting_frame_in (lsquic_cid_t, lsquic_packno_t);
#define EV_LOG_STOP_WAITING_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_stop_waiting_frame_in(args); \
} while (0)
void
lsquic_ev_log_connection_close_frame_in (lsquic_cid_t, uint32_t error_code,
int reason_len, const char *reason);
#define EV_LOG_CONNECTION_CLOSE_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_connection_close_frame_in(args); \
} while (0)
void
lsquic_ev_log_goaway_frame_in (lsquic_cid_t, uint32_t error_code,
uint32_t stream_id, int reason_len, const char *reason);
#define EV_LOG_GOAWAY_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_goaway_frame_in(args); \
} while (0)
void
lsquic_ev_log_rst_stream_frame_in (lsquic_cid_t, uint32_t stream_id,
uint64_t offset, uint32_t error_code);
#define EV_LOG_RST_STREAM_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_rst_stream_frame_in(args); \
} while (0)
void
lsquic_ev_log_padding_frame_in (lsquic_cid_t, size_t len);
#define EV_LOG_PADDING_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_padding_frame_in(args); \
} while (0)
void
lsquic_ev_log_ping_frame_in (lsquic_cid_t);
#define EV_LOG_PING_FRAME_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_ping_frame_in(args); \
} while (0)
void
lsquic_ev_log_packet_created (lsquic_cid_t, const struct lsquic_packet_out *);
#define EV_LOG_PACKET_CREATED(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_packet_created(args); \
} while (0)
void
lsquic_ev_log_packet_sent (lsquic_cid_t, const struct lsquic_packet_out *);
#define EV_LOG_PACKET_SENT(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_packet_sent(args); \
} while (0)
void
lsquic_ev_log_packet_not_sent (lsquic_cid_t, const struct lsquic_packet_out *);
#define EV_LOG_PACKET_NOT_SENT(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_packet_not_sent(args); \
} while (0)
void
lsquic_ev_log_http_headers_in (lsquic_cid_t, int is_server,
const struct uncompressed_headers *);
#define EV_LOG_HTTP_HEADERS_IN(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_http_headers_in(args); \
} while (0)
void
lsquic_ev_log_generated_stream_frame (lsquic_cid_t, const struct parse_funcs *pf,
const unsigned char *, size_t len);
#define EV_LOG_GENERATED_STREAM_FRAME(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_stream_frame(args); \
} while (0)
void
lsquic_ev_log_generated_ack_frame (lsquic_cid_t, const struct parse_funcs *,
const unsigned char *, size_t len);
#define EV_LOG_GENERATED_ACK_FRAME(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_ack_frame(args); \
} while (0)
void
lsquic_ev_log_generated_stop_waiting_frame (lsquic_cid_t, lsquic_packno_t);
#define EV_LOG_GENERATED_STOP_WAITING_FRAME(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_stop_waiting_frame(args); \
} while (0)
void
lsquic_ev_log_generated_http_headers (lsquic_cid_t, uint32_t stream_id,
int is_server, const struct http_prio_frame *,
const struct lsquic_http_headers *);
#define EV_LOG_GENERATED_HTTP_HEADERS(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_http_headers(args); \
} while (0)
void
lsquic_ev_log_generated_http_push_promise (lsquic_cid_t, uint32_t stream_id,
uint32_t promised_stream_id,
const struct lsquic_http_headers *headers,
const struct lsquic_http_headers *extra_headers);
#define EV_LOG_GENERATED_HTTP_PUSH_PROMISE(args...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_http_push_promise(args); \
} while (0)
#endif

View file

@ -0,0 +1,26 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <stdint.h>
#include "lsquic_frame_common.h"
const char *
lsquic_http_setting_id2str (enum settings_param id)
{
switch (id)
{
case SETTINGS_HEADER_TABLE_SIZE:
return "SETTINGS_HEADER_TABLE_SIZE";
case SETTINGS_ENABLE_PUSH:
return "SETTINGS_ENABLE_PUSH";
case SETTINGS_MAX_CONCURRENT_STREAMS:
return "SETTINGS_MAX_CONCURRENT_STREAMS";
case SETTINGS_INITIAL_WINDOW_SIZE:
return "SETTINGS_INITIAL_WINDOW_SIZE";
case SETTINGS_MAX_FRAME_SIZE:
return "SETTINGS_MAX_FRAME_SIZE";
case SETTINGS_MAX_HEADER_LIST_SIZE:
return "SETTINGS_MAX_HEADER_LIST_SIZE";
}
return "<unknown>";
}

View file

@ -0,0 +1,81 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_frame_common.h
*/
#ifndef LSQUIC_FRAME_COMMON_H
#define LSQUIC_FRAME_COMMON_H 1
enum http_frame_type
{
HTTP_FRAME_DATA = 0x00,
HTTP_FRAME_HEADERS = 0x01,
HTTP_FRAME_PRIORITY = 0x02,
HTTP_FRAME_RST_STREAM = 0x03,
HTTP_FRAME_SETTINGS = 0x04,
HTTP_FRAME_PUSH_PROMISE = 0x05,
HTTP_FRAME_PING = 0x06,
HTTP_FRAME_GOAWAY = 0x07,
HTTP_FRAME_WINDOW_UPDATE = 0x08,
HTTP_FRAME_CONTINUATION = 0x09,
N_HTTP_FRAME_TYPES
};
enum http_frame_header_flags /* RFC 7540, Section 6.2 */
{
HFHF_END_STREAM = 0x01,
HFHF_END_HEADERS = 0x04,
HFHF_PADDED = 0x08,
HFHF_PRIORITY = 0x20,
};
struct http_frame_header /* RFC 7540, Section 4.1 */
{
unsigned char hfh_length[3];
unsigned char hfh_type; /* enum http_frame_type */
unsigned char hfh_flags;
unsigned char hfh_stream_id[4];
};
#define hfh_get_length(hfh) ( ((hfh)->hfh_length[0] << 16) | \
((hfh)->hfh_length[1] << 8) | \
(hfh)->hfh_length[2] )
enum settings_param /* RFC 7540, Section 6.5.2 */
{
SETTINGS_HEADER_TABLE_SIZE = 0x0001,
SETTINGS_ENABLE_PUSH = 0x0002,
SETTINGS_MAX_CONCURRENT_STREAMS = 0x0003,
SETTINGS_INITIAL_WINDOW_SIZE = 0x0004,
SETTINGS_MAX_FRAME_SIZE = 0x0005,
SETTINGS_MAX_HEADER_LIST_SIZE = 0x0006,
};
/* This also doubles as HEADERS frame payload prefix: */
struct http_prio_frame /* RFC 7540, Section 6.3 */
{
unsigned char hpf_stream_id[4]; /* High bit is the exclusive flag */
unsigned char hpf_weight;
};
struct http_push_promise_frame /* RFC 7540, Section 6.6 */
{
unsigned char hppf_promised_id[4]; /* High bit is reserved */
};
struct lsquic_http2_setting
{
uint16_t id;
uint32_t value;
};
const char *
lsquic_http_setting_id2str (enum settings_param id);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,120 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_frame_reader.h -- Read HTTP frames from stream
*/
#ifndef LSQUIC_FRAME_READER_H
#define LSQUIC_FRAME_READER_H 1
#include <stddef.h>
#include <stdint.h>
struct lsquic_hdec;
struct lsquic_mm;
struct lsquic_stream;
struct lsquic_frame_reader;
enum frame_reader_flags
{
FRF_SERVER = (1 << 0),
FRF_HAVE_PREV = (1 << 1),
};
/* Frame reader may hit some error conditions which are reported using
* callback fc_on_error. These codes are later mapped stream- or
* connection-level errors.
*/
enum frame_reader_error
{
FR_ERR_DUPLICATE_PSEH = 1, /* Duplicate pseudo-header */
FR_ERR_INCOMPL_REQ_PSEH, /* Not all request pseudo-headers are present */
FR_ERR_UNNEC_REQ_PSEH, /* Unnecessary request pseudo-header present in
* the response.
*/
FR_ERR_INCOMPL_RESP_PSEH, /* Not all response pseudo-headers are present */
FR_ERR_UNNEC_RESP_PSEH, /* Unnecessary response pseudo-header present in
* the response.
*/
FR_ERR_UNKNOWN_PSEH, /* Unknown pseudo-header */
FR_ERR_UPPERCASE_HEADER, /* Uppercase letter in header */
FR_ERR_MISPLACED_PSEH,
FR_ERR_MISSING_PSEH,
FR_ERR_DECOMPRESS,
FR_ERR_INVALID_FRAME_SIZE, /* E.g. a SETTINGS frame length is not a multiple
* of 6 (RFC 7540, Section 6.5.1).
*/
FR_ERR_NONZERO_STREAM_ID,
FR_ERR_ZERO_STREAM_ID,
FR_ERR_SELF_DEP_STREAM, /* A stream in priority frame cannot depend on
* itself (RFC 7540, Section 5.3.1).
*/
FR_ERR_HEADERS_TOO_LARGE,
FR_ERR_UNEXPECTED_PUSH,
FR_ERR_NOMEM, /* Cannot allocate any more memory. */
FR_ERR_EXPECTED_CONTIN, /* Expected continuation frame. */
};
/* This struct is used to return decoded HEADERS and PUSH_PROMISE frames.
* Some of the fields are only used for HEADERS frames. They are marked
* with "H" comment below.
*/
struct uncompressed_headers
{
uint32_t uh_stream_id;
uint32_t uh_oth_stream_id; /* For HEADERS frame, the ID of the
* stream that this stream depends
* on. (Zero means unset.) For
* PUSH_PROMISE, the promised stream
* ID.
*/
unsigned uh_size; /* Number of characters in uh_headers, not
* counting the NUL byte.
*/
unsigned /* H */ uh_off;
unsigned short /* H */ uh_weight; /* 1 - 256; 0 means not set */
signed char /* H */ uh_exclusive; /* 0 or 1 when set; -1 means not set */
enum {
/* H */ UH_FIN = (1 << 0),
UH_PP = (1 << 1), /* Push promise */
} uh_flags:8;
char uh_headers[ /* NUL-terminated C string */
#if FRAME_READER_TESTING
FRAME_READER_TESTING
#else
0
#endif
];
};
struct frame_reader_callbacks
{
void (*frc_on_headers) (void *frame_cb_ctx, struct uncompressed_headers *);
void (*frc_on_push_promise) (void *frame_cb_ctx, struct uncompressed_headers *);
void (*frc_on_settings) (void *frame_cb_ctx, uint16_t setting_id,
uint32_t setting_value);
void (*frc_on_priority) (void *frame_cb_ctx, uint32_t stream_id,
int exclusive, uint32_t dep_stream_id,
unsigned weight);
void (*frc_on_error) (void *frame_cb_ctx, uint32_t stream_id,
enum frame_reader_error);
};
typedef ssize_t (*fr_stream_read_f)(struct lsquic_stream *, void *, size_t);
struct lsquic_frame_reader *
lsquic_frame_reader_new (enum frame_reader_flags, unsigned max_headers_sz,
struct lsquic_mm *, struct lsquic_stream *,
fr_stream_read_f, struct lsquic_hdec *,
const struct frame_reader_callbacks *,
void *fr_cb_ctx);
int
lsquic_frame_reader_read (struct lsquic_frame_reader *);
void
lsquic_frame_reader_destroy (struct lsquic_frame_reader *);
#endif

View file

@ -0,0 +1,713 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_frame_writer.c -- write frames to HEADERS stream.
*
* The frame is first written to list of frame_buf's (frabs) and then
* out to the stream. This is done because frame's size is written out
* to the stream and we may not have enough room in the stream to fit
* the whole frame.
*/
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic_arr.h"
#include "lsquic_hpack_enc.h"
#include "lsquic_mm.h"
#include "lsquic.h"
#include "lsquic_frame_writer.h"
#include "lsquic_frame_common.h"
#include "lsquic_ev_log.h"
#define LSQUIC_LOGGER_MODULE LSQLM_FRAME_WRITER
#define LSQUIC_LOG_CONN_ID lsquic_conn_id(lsquic_stream_conn(fw->fw_stream))
#include "lsquic_logger.h"
#ifndef LSQUIC_FRAB_SZ
# define LSQUIC_FRAB_SZ 0x1000
#endif
struct frame_buf
{
TAILQ_ENTRY(frame_buf) frab_next;
unsigned short frab_size,
frab_off;
unsigned char frab_buf[
LSQUIC_FRAB_SZ
- sizeof(TAILQ_ENTRY(frame_buf))
- sizeof(unsigned short) * 2
];
};
#define frab_left_to_read(f) ((f)->frab_size - (f)->frab_off)
#define frab_left_to_write(f) ((unsigned short) sizeof((f)->frab_buf) - (f)->frab_size)
#define frab_write_to(f) ((f)->frab_buf + (f)->frab_size)
/* Make sure that frab_buf is at least five bytes long, otherwise a frame
* won't fit into two adjacent frabs.
*/
typedef char three_byte_frab_buf[(sizeof(((struct frame_buf *)0)->frab_buf) >= 5) - 1];
TAILQ_HEAD(frame_buf_head, frame_buf);
struct lsquic_frame_writer
{
struct lsquic_stream *fw_stream;
fw_write_f fw_write;
fw_wavail_f fw_wavail;
fw_flush_f fw_flush;
struct lsquic_mm *fw_mm;
struct lsquic_henc *fw_henc;
struct frame_buf_head fw_frabs;
unsigned fw_max_frame_sz;
uint32_t fw_max_header_list_sz; /* 0 means unlimited */
enum {
FW_SERVER = (1 << 0),
} fw_flags;
};
/* RFC 7540, Section 4.2 */
#define MIN_MAX_FRAME_SIZE (1 << 14)
#define MAX_MAX_FRAME_SIZE ((1 << 24) - 1)
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define SETTINGS_FRAME_SZ 6
#define ABS_MIN_FRAME_SIZE MAX(SETTINGS_FRAME_SZ, \
sizeof(struct http_prio_frame))
struct lsquic_frame_writer *
lsquic_frame_writer_new (struct lsquic_mm *mm, struct lsquic_stream *stream,
unsigned max_frame_sz, struct lsquic_henc *henc, fw_write_f write,
fw_wavail_f wavail, fw_flush_f flush, int is_server)
{
struct lsquic_frame_writer *fw;
/* When frame writer is instantiated, limit the maximum size to
* MIN_MAX_FRAME_SIZE. The reference implementation has this value
* hardcoded and QUIC does not provide a mechanism to advertise a
* different value.
*/
if (0 == max_frame_sz)
max_frame_sz = MIN_MAX_FRAME_SIZE;
else
LSQ_LOG1(LSQ_LOG_WARN, "max frame size specified to be %u bytes "
"-- this better be test code!", max_frame_sz);
if (!is_server && max_frame_sz < ABS_MIN_FRAME_SIZE)
{
LSQ_LOG1(LSQ_LOG_ERROR, "max frame size must be at least %zd bytes, "
"which is the size of priority information that client always "
"writes", ABS_MIN_FRAME_SIZE);
return NULL;
}
fw = malloc(sizeof(*fw));
if (!fw)
return NULL;
fw->fw_mm = mm;
fw->fw_henc = henc;
fw->fw_stream = stream;
fw->fw_write = write;
fw->fw_wavail = wavail;
fw->fw_flush = flush;
fw->fw_max_frame_sz = max_frame_sz;
fw->fw_max_header_list_sz = 0;
if (is_server)
fw->fw_flags = FW_SERVER;
else
fw->fw_flags = 0;
TAILQ_INIT(&fw->fw_frabs);
return fw;
}
void
lsquic_frame_writer_destroy (struct lsquic_frame_writer *fw)
{
struct frame_buf *frab;
while ((frab = TAILQ_FIRST(&fw->fw_frabs)))
{
TAILQ_REMOVE(&fw->fw_frabs, frab, frab_next);
lsquic_mm_put_4k(fw->fw_mm, frab);
}
free(fw);
}
static struct frame_buf *
fw_get_frab (struct lsquic_frame_writer *fw)
{
struct frame_buf *frab;
frab = lsquic_mm_get_4k(fw->fw_mm);
if (frab)
memset(frab, 0, offsetof(struct frame_buf, frab_buf));
return frab;
}
static void
fw_put_frab (struct lsquic_frame_writer *fw, struct frame_buf *frab)
{
TAILQ_REMOVE(&fw->fw_frabs, frab, frab_next);
lsquic_mm_put_4k(fw->fw_mm, frab);
}
static int
fw_write_to_frab (struct lsquic_frame_writer *fw, const void *buf, size_t bufsz)
{
const unsigned char *p = buf;
const unsigned char *const end = p + bufsz;
struct frame_buf *frab;
unsigned ntowrite;
while (p < end)
{
frab = TAILQ_LAST(&fw->fw_frabs, frame_buf_head);
if (!(frab && (ntowrite = frab_left_to_write(frab)) > 0))
{
frab = fw_get_frab(fw);
if (!frab)
return -1;
TAILQ_INSERT_TAIL(&fw->fw_frabs, frab, frab_next);
ntowrite = frab_left_to_write(frab);
}
if (ntowrite > bufsz)
ntowrite = bufsz;
memcpy(frab_write_to(frab), p, ntowrite);
p += ntowrite;
bufsz -= ntowrite;
frab->frab_size += ntowrite;
}
return 0;
}
int
lsquic_frame_writer_have_leftovers (const struct lsquic_frame_writer *fw)
{
return !TAILQ_EMPTY(&fw->fw_frabs);
}
int
lsquic_frame_writer_flush (struct lsquic_frame_writer *fw)
{
size_t navail = fw->fw_wavail(fw->fw_stream);
struct frame_buf *frab;
while (navail > 0 && (frab = TAILQ_FIRST(&fw->fw_frabs)))
{
size_t ntowrite = frab_left_to_read(frab);
if (navail < ntowrite)
ntowrite = navail;
ssize_t nw = fw->fw_write(fw->fw_stream,
frab->frab_buf + frab->frab_off, ntowrite);
if (nw > 0)
{
navail -= nw;
frab->frab_off += nw;
if (frab->frab_off == frab->frab_size)
{
TAILQ_REMOVE(&fw->fw_frabs, frab, frab_next);
fw_put_frab(fw, frab);
}
}
else
return -1;
}
(void) fw->fw_flush(fw->fw_stream);
return 0;
}
struct header_framer_ctx
{
struct lsquic_frame_writer
*hfc_fw;
struct {
struct frame_buf *frab;
unsigned short off;
} hfc_header_ptr; /* Points to byte *after* current frame header */
unsigned hfc_max_frame_sz; /* Maximum frame size. We always fill it. */
unsigned hfc_cur_sz; /* Number of bytes in the current frame. */
unsigned hfc_n_frames; /* Number of frames written. */
uint32_t hfc_stream_id; /* Stream ID */
enum http_frame_header_flags
hfc_first_flags;
enum http_frame_type
hfc_frame_type;
};
static void
hfc_init (struct header_framer_ctx *hfc, struct lsquic_frame_writer *fw,
unsigned max_frame_sz, enum http_frame_type frame_type,
uint32_t stream_id, enum http_frame_header_flags first_flags)
{
memset(hfc, 0, sizeof(*hfc));
hfc->hfc_fw = fw;
hfc->hfc_frame_type = frame_type;
hfc->hfc_stream_id = stream_id;
hfc->hfc_first_flags = first_flags;
hfc->hfc_max_frame_sz = max_frame_sz;
hfc->hfc_cur_sz = max_frame_sz;
}
static void
hfc_save_ptr (struct header_framer_ctx *hfc)
{
hfc->hfc_header_ptr.frab = TAILQ_LAST(&hfc->hfc_fw->fw_frabs, frame_buf_head);
hfc->hfc_header_ptr.off = hfc->hfc_header_ptr.frab->frab_size;
}
static void
hfc_terminate_frame (struct header_framer_ctx *hfc,
enum http_frame_header_flags flags)
{
union {
struct http_frame_header fh;
unsigned char buf[ sizeof(struct http_frame_header) ];
} u;
uint32_t stream_id;
struct frame_buf *frab;
/* Construct the frame */
u.fh.hfh_length[0] = hfc->hfc_cur_sz >> 16;
u.fh.hfh_length[1] = hfc->hfc_cur_sz >> 8;
u.fh.hfh_length[2] = hfc->hfc_cur_sz;
u.fh.hfh_flags = flags;
if (1 == hfc->hfc_n_frames)
{
u.fh.hfh_type = hfc->hfc_frame_type;
u.fh.hfh_flags |= hfc->hfc_first_flags;
}
else
u.fh.hfh_type = HTTP_FRAME_CONTINUATION;
stream_id = htonl(hfc->hfc_stream_id);
memcpy(u.fh.hfh_stream_id, &stream_id, sizeof(stream_id));
if (hfc->hfc_header_ptr.off >= sizeof(u.fh))
{ /* Write in a single chunk */
assert(0 == memcmp("123456789", hfc->hfc_header_ptr.frab->frab_buf +
hfc->hfc_header_ptr.off - sizeof(u.buf), sizeof(u.buf)));
memcpy(hfc->hfc_header_ptr.frab->frab_buf + hfc->hfc_header_ptr.off -
sizeof(u.buf), u.buf, sizeof(u.buf));
}
else
{ /* Write across frab boundary */
memcpy(hfc->hfc_header_ptr.frab->frab_buf,
u.buf + sizeof(u.buf) - hfc->hfc_header_ptr.off,
hfc->hfc_header_ptr.off);
frab = TAILQ_PREV(hfc->hfc_header_ptr.frab, frame_buf_head, frab_next);
memcpy(frab->frab_buf + frab->frab_size - sizeof(u.buf) +
hfc->hfc_header_ptr.off, u.buf,
sizeof(u.buf) - hfc->hfc_header_ptr.off);
}
}
static int
hfc_write (struct header_framer_ctx *hfc, const void *buf, size_t sz)
{
const unsigned char *p = buf;
unsigned avail;
int s;
while (sz > 0)
{
if (hfc->hfc_max_frame_sz == hfc->hfc_cur_sz)
{
if (hfc->hfc_n_frames > 0)
hfc_terminate_frame(hfc, 0);
s = fw_write_to_frab(hfc->hfc_fw, "123456789",
sizeof(struct http_frame_header));
if (s < 0)
return s;
++hfc->hfc_n_frames;
hfc_save_ptr(hfc);
hfc->hfc_cur_sz = 0;
}
avail = hfc->hfc_max_frame_sz - hfc->hfc_cur_sz;
if (sz < avail)
avail = sz;
if (avail)
{
s = fw_write_to_frab(hfc->hfc_fw, p, avail);
if (s < 0)
return s;
hfc->hfc_cur_sz += avail;
sz -= avail;
p += avail;
}
}
return 0;
}
static unsigned
count_uppercase (const unsigned char *buf, size_t sz)
{
static const unsigned char uppercase[0x100] = {
['A'] = 1, ['B'] = 1, ['C'] = 1, ['D'] = 1, ['E'] = 1, ['F'] = 1,
['G'] = 1, ['H'] = 1, ['I'] = 1, ['J'] = 1, ['K'] = 1, ['L'] = 1,
['M'] = 1, ['N'] = 1, ['O'] = 1, ['P'] = 1, ['Q'] = 1, ['R'] = 1,
['S'] = 1, ['T'] = 1, ['U'] = 1, ['V'] = 1, ['W'] = 1, ['X'] = 1,
['Y'] = 1, ['Z'] = 1,
};
unsigned n_uppercase, i;
n_uppercase = 0;
for (i = 0; i < sz; ++i)
n_uppercase += uppercase[ buf[i] ];
return n_uppercase;
}
static uint32_t
calc_headers_size (const struct lsquic_http_headers *headers)
{
int i;
uint32_t size = 0;
for (i = 0; i < headers->count; ++i)
size += 32 + headers->headers[i].name.iov_len +
headers->headers[i].value.iov_len;
return size;
}
static int
check_headers_size (const struct lsquic_frame_writer *fw,
const struct lsquic_http_headers *headers,
const struct lsquic_http_headers *extra_headers)
{
uint32_t headers_sz;
headers_sz = calc_headers_size(headers);
if (extra_headers)
headers_sz += calc_headers_size(extra_headers);
if (headers_sz > fw->fw_max_header_list_sz)
{
LSQ_INFO("Headers size %u is larger than max allowed (%u)",
headers_sz, fw->fw_max_header_list_sz);
errno = EMSGSIZE;
return -1;
}
return 0;
}
static int
check_headers_case (const struct lsquic_frame_writer *fw,
const struct lsquic_http_headers *headers)
{
unsigned n_uppercase;
int i;
n_uppercase = 0;
for (i = 0; i < headers->count; ++i)
n_uppercase += count_uppercase(headers->headers[i].name.iov_base,
headers->headers[i].name.iov_len);
if (n_uppercase)
{
LSQ_INFO("Uppercase letters in header names");
errno = EINVAL;
return -1;
}
return 0;
}
static int
write_headers (struct lsquic_frame_writer *fw,
const struct lsquic_http_headers *headers,
struct header_framer_ctx *hfc, unsigned char *buf4k)
{
unsigned char *end;
int i, s;
for (i = 0; i < headers->count; ++i)
{
end = lsquic_henc_encode(fw->fw_henc, buf4k, buf4k + 0x1000,
headers->headers[i].name.iov_base, headers->headers[i].name.iov_len,
headers->headers[i].value.iov_base, headers->headers[i].value.iov_len, 0);
if (!(end > buf4k))
{
LSQ_WARN("error encoding header");
errno = EBADMSG;
return -1;
}
s = hfc_write(hfc, buf4k, end - buf4k);
if (s < 0)
return s;
}
return 0;
}
int
lsquic_frame_writer_write_headers (struct lsquic_frame_writer *fw,
uint32_t stream_id,
const struct lsquic_http_headers *headers,
int eos, unsigned weight)
{
struct header_framer_ctx hfc;
int s;
struct http_prio_frame prio_frame;
enum http_frame_header_flags flags;
unsigned char *buf;
/* Internal function: weight must be valid here */
assert(weight >= 1 && weight <= 256);
if (fw->fw_max_header_list_sz && 0 != check_headers_size(fw, headers, NULL))
return -1;
if (0 != check_headers_case(fw, headers))
return -1;
if (eos)
flags = HFHF_END_STREAM;
else
flags = 0;
if (!(fw->fw_flags & FW_SERVER))
flags |= HFHF_PRIORITY;
hfc_init(&hfc, fw, fw->fw_max_frame_sz, HTTP_FRAME_HEADERS, stream_id,
flags);
if (!(fw->fw_flags & FW_SERVER))
{
memset(&prio_frame.hpf_stream_id, 0, sizeof(prio_frame.hpf_stream_id));
prio_frame.hpf_weight = weight - 1;
s = hfc_write(&hfc, &prio_frame, sizeof(struct http_prio_frame));
if (s < 0)
return s;
}
buf = lsquic_mm_get_4k(fw->fw_mm);
if (!buf)
return -1;
s = write_headers(fw, headers, &hfc, buf);
lsquic_mm_put_4k(fw->fw_mm, buf);
if (0 == s)
{
EV_LOG_GENERATED_HTTP_HEADERS(LSQUIC_LOG_CONN_ID, stream_id,
fw->fw_flags & FW_SERVER, &prio_frame, headers);
hfc_terminate_frame(&hfc, HFHF_END_HEADERS);
return lsquic_frame_writer_flush(fw);
}
else
return s;
}
int
lsquic_frame_writer_write_promise (struct lsquic_frame_writer *fw,
uint32_t stream_id, uint32_t promised_stream_id,
const struct iovec *path, const struct iovec *host,
const struct lsquic_http_headers *extra_headers)
{
struct header_framer_ctx hfc;
struct http_push_promise_frame push_frame;
lsquic_http_header_t mpas_headers[4];
struct lsquic_http_headers mpas = { /* method, path, authority, scheme */
.headers = mpas_headers,
.count = 4,
};
unsigned char *buf;
int s;
mpas_headers[0].name. iov_base = ":method";
mpas_headers[0].name. iov_len = 7;
mpas_headers[0].value.iov_base = "GET";
mpas_headers[0].value.iov_len = 3;
mpas_headers[1].name .iov_base = ":path";
mpas_headers[1].name .iov_len = 5;
mpas_headers[1].value = *path;
mpas_headers[2].name .iov_base = ":authority";
mpas_headers[2].name .iov_len = 10;
mpas_headers[2].value = *host;
mpas_headers[3].name. iov_base = ":scheme";
mpas_headers[3].name. iov_len = 7;
mpas_headers[3].value.iov_base = "https";
mpas_headers[3].value.iov_len = 5;
if (fw->fw_max_header_list_sz &&
0 != check_headers_size(fw, &mpas, extra_headers))
return -1;
if (extra_headers && 0 != check_headers_case(fw, extra_headers))
return -1;
hfc_init(&hfc, fw, fw->fw_max_frame_sz, HTTP_FRAME_PUSH_PROMISE,
stream_id, 0);
promised_stream_id = htonl(promised_stream_id);
memcpy(push_frame.hppf_promised_id, &promised_stream_id, 4);
s = hfc_write(&hfc, &push_frame, sizeof(struct http_push_promise_frame));
if (s < 0)
return s;
buf = lsquic_mm_get_4k(fw->fw_mm);
if (!buf)
return -1;
s = write_headers(fw, &mpas, &hfc, buf);
if (s != 0)
{
lsquic_mm_put_4k(fw->fw_mm, buf);
return -1;
}
if (extra_headers)
s = write_headers(fw, extra_headers, &hfc, buf);
lsquic_mm_put_4k(fw->fw_mm, buf);
if (0 == s)
{
EV_LOG_GENERATED_HTTP_PUSH_PROMISE(LSQUIC_LOG_CONN_ID, stream_id,
htonl(promised_stream_id), &mpas, extra_headers);
hfc_terminate_frame(&hfc, HFHF_END_HEADERS);
return lsquic_frame_writer_flush(fw);
}
else
return -1;
}
void
lsquic_frame_writer_max_header_list_size (struct lsquic_frame_writer *fw,
uint32_t max_size)
{
LSQ_DEBUG("set max_header_list_sz to %u", max_size);
fw->fw_max_header_list_sz = max_size;
}
static int
write_settings (struct lsquic_frame_writer *fw,
const struct lsquic_http2_setting *settings, unsigned n_settings)
{
struct http_frame_header fh;
unsigned payload_length;
uint32_t val;
uint16_t id;
int s;
payload_length = n_settings * 6;
memset(&fh, 0, sizeof(fh));
fh.hfh_type = HTTP_FRAME_SETTINGS;
fh.hfh_length[0] = payload_length >> 16;
fh.hfh_length[1] = payload_length >> 8;
fh.hfh_length[2] = payload_length;
s = fw_write_to_frab(fw, &fh, sizeof(fh));
if (s != 0)
return s;
do
{
id = htons(settings->id);
val = htonl(settings->value);
if (0 != (s = fw_write_to_frab(fw, &id, sizeof(id))) ||
0 != (s = fw_write_to_frab(fw, &val, sizeof(val))))
return s;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "wrote HTTP SETTINGS frame: "
"%s=%"PRIu32, lsquic_http_setting_id2str(settings->id),
settings->value);
++settings;
}
while (--n_settings);
return 0;
}
int
lsquic_frame_writer_write_settings (struct lsquic_frame_writer *fw,
const struct lsquic_http2_setting *settings, unsigned n_settings)
{
unsigned settings_per_frame;
unsigned n;
if (0 == n_settings)
{
errno = EINVAL;
return -1;
}
settings_per_frame = fw->fw_max_frame_sz / SETTINGS_FRAME_SZ;
n = 0;
do {
if (settings_per_frame > n_settings - n)
settings_per_frame = n_settings - n;
if (0 != write_settings(fw, &settings[n], settings_per_frame))
return -1;
n += settings_per_frame;
} while (n < n_settings);
return lsquic_frame_writer_flush(fw);
}
int
lsquic_frame_writer_write_priority (struct lsquic_frame_writer *fw,
uint32_t stream_id, int exclusive, uint32_t stream_dep_id,
unsigned weight)
{
unsigned char buf[ sizeof(struct http_frame_header) +
sizeof(struct http_prio_frame) ];
struct http_frame_header *fh = (void *) &buf[0];
struct http_prio_frame *prio_frame = (void *) &buf[sizeof(*fh)];
int s;
if (stream_dep_id & (1UL << 31))
{
LSQ_WARN("stream ID too high (%u): cannot write PRIORITY frame",
stream_dep_id);
return -1;
}
if (weight < 1 || weight > 256)
return -1;
memset(fh, 0, sizeof(*fh));
fh->hfh_type = HTTP_FRAME_PRIORITY;
fh->hfh_length[2] = sizeof(struct http_prio_frame);
stream_id = htonl(stream_id);
memcpy(fh->hfh_stream_id, &stream_id, 4);
stream_dep_id |= !!exclusive << 31;
stream_id = htonl(stream_dep_id);
memcpy(prio_frame->hpf_stream_id, &stream_id, 4);
prio_frame->hpf_weight = weight - 1;
s = fw_write_to_frab(fw, buf, sizeof(buf));
if (s != 0)
return s;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "wrote HTTP PRIORITY frame: "
"stream %"PRIu32"; weight: %u; exclusive: %d",
htonl(stream_id), weight, !!exclusive);
return lsquic_frame_writer_flush(fw);
}

View file

@ -0,0 +1,63 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_frame_writer.h -- write frames to HEADERS stream.
*/
#ifndef LSQUIC_FRAME_WRITER_H
#define LSQUIC_FRAME_WRITER_H 1
#include <stddef.h>
#include <stdint.h>
struct iovec;
struct lsquic_henc;
struct lsquic_mm;
struct lsquic_frame_writer;
struct lsquic_stream;
struct lsquic_http_headers;
struct lsquic_http2_setting;
typedef ssize_t (*fw_write_f)(struct lsquic_stream *, const void *, size_t);
typedef size_t (*fw_wavail_f)(const struct lsquic_stream *stream);
typedef int (*fw_flush_f)(struct lsquic_stream *);
struct lsquic_frame_writer *
lsquic_frame_writer_new (struct lsquic_mm *, struct lsquic_stream *,
unsigned max_frame_sz, struct lsquic_henc *,
fw_write_f, fw_wavail_f, fw_flush_f, int is_server);
void
lsquic_frame_writer_destroy (struct lsquic_frame_writer *);
int
lsquic_frame_writer_have_leftovers (const struct lsquic_frame_writer *);
int
lsquic_frame_writer_flush (struct lsquic_frame_writer *);
int
lsquic_frame_writer_write_headers (struct lsquic_frame_writer *,
uint32_t stream_id,
const struct lsquic_http_headers *,
int eos, unsigned weight);
int
lsquic_frame_writer_write_settings (struct lsquic_frame_writer *,
const struct lsquic_http2_setting *, unsigned n_settings);
int
lsquic_frame_writer_write_priority (struct lsquic_frame_writer *,
uint32_t stream_id, int exclusive, uint32_t stream_dep_id,
unsigned priority);
int
lsquic_frame_writer_write_promise (struct lsquic_frame_writer *,
uint32_t stream_id, uint32_t promised_stream_id,
const struct iovec *path, const struct iovec *host,
const struct lsquic_http_headers *headers);
void
lsquic_frame_writer_max_header_list_size (struct lsquic_frame_writer *,
uint32_t max_size);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,19 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_FULL_CONN_H
#define LSQUIC_FULL_CONN_H
struct lsquic_conn;
struct lsquic_stream_if;
struct lsquic_engine_public;
struct lsquic_conn *
full_conn_client_new (struct lsquic_engine_public *,
const struct lsquic_stream_if *,
void *stream_if_ctx,
unsigned flags /* Only FC_SERVER and FC_HTTP */,
const char *hostname, unsigned short max_packet_size);
void
full_conn_close_internal (lsquic_conn_t *, int is_user);
#endif

View file

@ -0,0 +1,22 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* Global state
*/
#include "lsquic_types.h"
#include "lsquic.h"
#include "lsquic_handshake.h"
int
lsquic_global_init (int flags)
{
return handshake_init(flags);
}
void
lsquic_global_cleanup (void)
{
handshake_cleanup();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,254 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_HANDSHAKE_H
#define LSQUIC_HANDSHAKE_H
#include <stdint.h>
#include <openssl/base.h>
#include <openssl/aead.h>
#include <time.h>
#include "lsquic_str.h"
struct lsquic_engine_public;
struct sockaddr;
#include "lsquic_qtags.h"
#define STK_LENGTH 60
#define SNO_LENGTH 56
#define SCID_LENGTH 16
#define DNONC_LENGTH 32
#define aes128_key_len 16
#define aes128_iv_len 4
/* client side, certs and hashs
*/
typedef struct cert_hash_item_st
{
struct lsquic_str* domain; /*with port, such as "xyz.com:8088" as the key */
struct lsquic_str* crts;
struct lsquic_str* hashs;
int count;
} cert_hash_item_t;
enum handshake_error /* TODO: rename this enum */
{
DATA_NOT_ENOUGH = -2,
DATA_FORMAT_ERROR = -1,
HS_ERROR = -1,
DATA_NO_ERROR = 0,
HS_SHLO = 0,
HS_1RTT = 1,
HS_2RTT = 2,
};
enum handshake_state
{
HSK_CHLO_REJ = 0,
HSK_SHLO,
HSK_COMPLETED,
N_HSK_STATES
};
typedef struct tag_value_st
{
uint32_t tag;
const char * value;
int len;
} tag_value_t;
typedef struct hs_ctx_st
{
enum {
HSET_TCID = (1 << 0), /* tcid is set */
HSET_SMHL = (1 << 1), /* smhl is set */
HSET_SCID = (1 << 2),
} set;
enum {
HOPT_NSTP = (1 << 0), /* NSTP option present in COPT */
HOPT_SREJ = (1 << 1), /* SREJ option present in COPT */
} opts;
uint32_t pdmd;
uint32_t aead;
uint32_t kexs;
uint32_t mids;
uint32_t scls;
uint32_t cfcw;
uint32_t sfcw;
uint32_t srbf;
uint32_t icsl;
uint32_t irtt;
uint64_t rcid;
uint32_t tcid;
uint32_t smhl;
uint64_t ctim; /* any usage? */
uint64_t sttl;
unsigned char scid[SCID_LENGTH];
//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];
uint32_t rrej;
struct lsquic_str ccs;
struct lsquic_str sni; /* 0 rtt */
struct lsquic_str ccrt;
struct lsquic_str stk;
struct lsquic_str sno;
struct lsquic_str prof;
struct lsquic_str csct;
struct lsquic_str crt; /* compressed certs buffer */
} hs_ctx_t;
/* client side need to store 0rtt info per STK */
typedef struct lsquic_session_cache_info_st
{
unsigned char sscid[SCID_LENGTH];
unsigned char spubs[32]; /* server pub key for next time 0rtt */
uint32_t ver; /* one VERSION */
uint32_t aead;
uint32_t kexs;
uint32_t pdmd;
uint64_t orbt;
uint64_t expy;
int scfg_flag; /* 0, no-init, 1, no parse, 2, parsed */
struct lsquic_str sstk;
struct lsquic_str scfg;
struct lsquic_str sni_key; /* This is only used as key */
} lsquic_session_cache_info_t;
#ifndef LSQUIC_KEEP_ENC_SESS_HISTORY
# ifndef NDEBUG
# define LSQUIC_KEEP_ENC_SESS_HISTORY 1
# else
# define LSQUIC_KEEP_ENC_SESS_HISTORY 0
# endif
#endif
#if LSQUIC_KEEP_ENC_SESS_HISTORY
#define ESHIST_BITS 7
#define ESHIST_MASK ((1 << ESHIST_BITS) - 1)
#define ESHIST_STR_SIZE ((1 << ESHIST_BITS) + 1)
typedef unsigned char eshist_idx_t;
enum enc_sess_history_event
{
ESHE_EMPTY = '\0',
ESHE_SET_SNI = 'I',
ESHE_SET_SNO = 'O',
ESHE_SET_STK = 'K',
ESHE_SET_SCID = 'D',
ESHE_SET_PROF = 'P',
};
#endif
typedef struct lsquic_enc_session
{
enum handshake_state hsk_state;
uint8_t have_key; /* 0, no 1, I, 2, D, 3, F */
uint8_t peer_have_final_key;
uint8_t server_start_use_final_key;
lsquic_cid_t cid;
unsigned char priv_key[32];
EVP_AEAD_CTX *enc_ctx_i;
EVP_AEAD_CTX *dec_ctx_i;
/* Have to save the initial key for diversification need */
unsigned char enc_key_i[aes128_key_len];
unsigned char dec_key_i[aes128_key_len];
unsigned char enc_key_nonce_i[aes128_iv_len];
unsigned char dec_key_nonce_i[aes128_iv_len];
EVP_AEAD_CTX *enc_ctx_f;
EVP_AEAD_CTX *dec_ctx_f;
unsigned char enc_key_nonce_f[aes128_iv_len];
unsigned char dec_key_nonce_f[aes128_iv_len];
hs_ctx_t hs_ctx;
lsquic_session_cache_info_t *info;
SSL_CTX * ssl_ctx;
const struct lsquic_engine_public *enpub;
struct lsquic_str * cert_ptr; /* pointer to the leaf cert of the server, not real copy */
struct lsquic_str chlo; /* real copy of CHLO message */
struct lsquic_str sstk;
struct lsquic_str ssno;
#if LSQUIC_KEEP_ENC_SESS_HISTORY
eshist_idx_t es_hist_idx;
unsigned char es_hist_buf[1 << ESHIST_BITS];
#endif
} lsquic_enc_session_t;
#if LSQUIC_KEEP_ENC_SESS_HISTORY
void
lsquic_get_enc_hist (const lsquic_enc_session_t *, char buf[ESHIST_STR_SIZE]);
#endif
int handshake_init(int flags);
void handshake_cleanup();
lsquic_enc_session_t *
new_enc_session_c(const char *domain, lsquic_cid_t cid,
const struct lsquic_engine_public *);
void free_enc_session(lsquic_enc_session_t *enc_session);
void free_info(lsquic_session_cache_info_t *info);
lsquic_cid_t generate_cid(void);
/* save to hash table */
lsquic_session_cache_info_t *retrieve_session_info_entry(const char *key);
void remove_expire_session_info_entry();
void remove_session_info_entry(struct lsquic_str *key);
cert_hash_item_t *make_cert_hash_item(struct lsquic_str *domain, struct lsquic_str **certs, int count);
void c_free_cert_hash_item(cert_hash_item_t *item);
cert_hash_item_t* c_find_certs(struct lsquic_str *domain);
int c_insert_certs(cert_hash_item_t *item);
/* -1 error, 0, OK, response in `buf' */
int gen_chlo(lsquic_enc_session_t *enc_session, enum lsquic_version,
uint8_t *buf, size_t *len);
int handle_chlo_reply(lsquic_enc_session_t *enc_session, const uint8_t *data,
int len);
int is_hs_done(lsquic_enc_session_t *enc_session);
/**
* The belows are global functions
*/
int lsquic_enc(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,
int is_hello);
int lsquic_dec(lsquic_enc_session_t *enc_session, enum lsquic_version,
uint8_t path_id, uint64_t pack_num,
unsigned char *buf, size_t *header_len, size_t data_len,
unsigned char *diversification_nonce,
unsigned char *buf_out, size_t max_out_len, size_t *out_len);
int
get_peer_setting (const lsquic_enc_session_t *, uint32_t tag, uint32_t *val);
int
get_peer_option (const lsquic_enc_session_t *enc_session, uint32_t tag);
#ifdef NDEBUG
#define lsquic_enc_session_have_key_gt_one(e) ((e) && (e)->have_key > 1)
#else
int
lsquic_enc_session_have_key_gt_one (const lsquic_enc_session_t *enc_session);
#endif
#endif

228
src/liblsquic/lsquic_hash.c Normal file
View file

@ -0,0 +1,228 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hash.c
*/
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic_malo.h"
#include "lsquic_hash.h"
#include "lsquic_xxhash.h"
struct lsquic_hash_elem
{
TAILQ_ENTRY(lsquic_hash_elem)
qhe_next_bucket,
qhe_next_all;
const void *qhe_key_data;
unsigned qhe_key_len;
void *qhe_value;
unsigned qhe_hash_val;
};
TAILQ_HEAD(hels_head, lsquic_hash_elem);
#define N_BUCKETS(n_bits) (1U << (n_bits))
#define BUCKNO(n_bits, hash) ((hash) & (N_BUCKETS(n_bits) - 1))
struct lsquic_hash
{
struct hels_head *qh_buckets,
qh_all;
struct malo *qh_malo_els;
struct lsquic_hash_elem *qh_iter_next;
unsigned qh_count;
unsigned qh_nbits;
};
struct lsquic_hash *
lsquic_hash_create (void)
{
struct hels_head *buckets;
struct lsquic_hash *hash;
struct malo *malo;
unsigned nbits = 2;
unsigned i;
buckets = malloc(sizeof(buckets[0]) * N_BUCKETS(nbits));
if (!buckets)
return NULL;
hash = malloc(sizeof(*hash));
if (!hash)
{
free(buckets);
return NULL;
}
malo = lsquic_malo_create(sizeof(struct lsquic_hash_elem));
if (!malo)
{
free(hash);
free(buckets);
return NULL;
}
for (i = 0; i < N_BUCKETS(nbits); ++i)
TAILQ_INIT(&buckets[i]);
TAILQ_INIT(&hash->qh_all);
hash->qh_buckets = buckets;
hash->qh_nbits = nbits;
hash->qh_malo_els = malo;
hash->qh_iter_next = NULL;
hash->qh_count = 0;
return hash;
}
void
lsquic_hash_destroy (struct lsquic_hash *hash)
{
lsquic_malo_destroy(hash->qh_malo_els);
free(hash->qh_buckets);
free(hash);
}
static int
lsquic_hash_grow (struct lsquic_hash *hash)
{
struct hels_head *new_buckets, *new[2];
struct lsquic_hash_elem *el;
unsigned n, old_nbits;
int idx;
old_nbits = hash->qh_nbits;
new_buckets = malloc(sizeof(hash->qh_buckets[0])
* N_BUCKETS(old_nbits + 1));
if (!new_buckets)
return -1;
for (n = 0; n < N_BUCKETS(old_nbits); ++n)
{
new[0] = &new_buckets[n];
new[1] = &new_buckets[n + N_BUCKETS(old_nbits)];
TAILQ_INIT(new[0]);
TAILQ_INIT(new[1]);
while ((el = TAILQ_FIRST(&hash->qh_buckets[n])))
{
TAILQ_REMOVE(&hash->qh_buckets[n], el, qhe_next_bucket);
idx = (BUCKNO(old_nbits + 1, el->qhe_hash_val) >> old_nbits) & 1;
TAILQ_INSERT_TAIL(new[idx], el, qhe_next_bucket);
}
}
free(hash->qh_buckets);
hash->qh_nbits = old_nbits + 1;
hash->qh_buckets = new_buckets;
return 0;
}
struct lsquic_hash_elem *
lsquic_hash_insert (struct lsquic_hash *hash, const void *key,
unsigned key_sz, void *data)
{
unsigned buckno, hash_val;
struct lsquic_hash_elem *el;
el = lsquic_malo_get(hash->qh_malo_els);
if (!el)
return NULL;
if (hash->qh_count >= N_BUCKETS(hash->qh_nbits) / 2 &&
0 != lsquic_hash_grow(hash))
{
lsquic_malo_put(el);
return NULL;
}
hash_val = XXH64(key, key_sz, (uintptr_t) hash);
buckno = BUCKNO(hash->qh_nbits, hash_val);
TAILQ_INSERT_TAIL(&hash->qh_all, el, qhe_next_all);
TAILQ_INSERT_TAIL(&hash->qh_buckets[buckno], el, qhe_next_bucket);
el->qhe_key_data = key;
el->qhe_key_len = key_sz;
el->qhe_value = data;
el->qhe_hash_val = hash_val;
++hash->qh_count;
return el;
}
struct lsquic_hash_elem *
lsquic_hash_find (struct lsquic_hash *hash, const void *key, unsigned key_sz)
{
unsigned buckno, hash_val;
struct lsquic_hash_elem *el;
hash_val = XXH64(key, key_sz, (uintptr_t) hash);
buckno = BUCKNO(hash->qh_nbits, hash_val);
TAILQ_FOREACH(el, &hash->qh_buckets[buckno], qhe_next_bucket)
if (hash_val == el->qhe_hash_val &&
key_sz == el->qhe_key_len &&
0 == memcmp(key, el->qhe_key_data, key_sz))
{
return el;
}
return NULL;
}
void *
lsquic_hashelem_getdata (const struct lsquic_hash_elem *el)
{
return el->qhe_value;
}
void
lsquic_hash_erase (struct lsquic_hash *hash, struct lsquic_hash_elem *el)
{
unsigned buckno;
buckno = BUCKNO(hash->qh_nbits, el->qhe_hash_val);
TAILQ_REMOVE(&hash->qh_buckets[buckno], el, qhe_next_bucket);
TAILQ_REMOVE(&hash->qh_all, el, qhe_next_all);
lsquic_malo_put(el);
--hash->qh_count;
}
void
lsquic_hash_reset_iter (struct lsquic_hash *hash)
{
hash->qh_iter_next = TAILQ_FIRST(&hash->qh_all);
}
struct lsquic_hash_elem *
lsquic_hash_first (struct lsquic_hash *hash)
{
lsquic_hash_reset_iter(hash);
return lsquic_hash_next(hash);
}
struct lsquic_hash_elem *
lsquic_hash_next (struct lsquic_hash *hash)
{
struct lsquic_hash_elem *el;
el = hash->qh_iter_next;
if (el)
hash->qh_iter_next = TAILQ_NEXT(el, qhe_next_all);
return el;
}
unsigned
lsquic_hash_count (struct lsquic_hash *hash)
{
return hash->qh_count;
}

View file

@ -0,0 +1,43 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hash.c -- A generic hash
*/
#ifndef LSQUIC_HASH_H
#define LSQUIC_HASH_H
struct lsquic_hash;
struct lsquic_hash_elem;
struct lsquic_hash *
lsquic_hash_create (void);
void
lsquic_hash_destroy (struct lsquic_hash *);
struct lsquic_hash_elem *
lsquic_hash_insert (struct lsquic_hash *, const void *key, unsigned key_sz,
void *data);
struct lsquic_hash_elem *
lsquic_hash_find (struct lsquic_hash *, const void *key, unsigned key_sz);
void *
lsquic_hashelem_getdata (const struct lsquic_hash_elem *);
void
lsquic_hash_erase (struct lsquic_hash *, struct lsquic_hash_elem *);
void
lsquic_hash_reset_iter (struct lsquic_hash *);
struct lsquic_hash_elem *
lsquic_hash_first (struct lsquic_hash *);
struct lsquic_hash_elem *
lsquic_hash_next (struct lsquic_hash *);
unsigned
lsquic_hash_count (struct lsquic_hash *);
#endif

View file

@ -0,0 +1,388 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* HEADERS stream logic
*/
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic_types.h"
#include "lsquic_frame_common.h"
#include "lsquic_frame_reader.h"
#include "lsquic_frame_writer.h"
#include "lsquic_arr.h"
#include "lsquic_hpack_enc.h"
#include "lsquic_hpack_dec.h"
#include "lsquic.h"
#include "lsquic_headers_stream.h"
#define MAX_HEADERS_SIZE (64 * 1024)
#define MAX_HEADER_TABLE_SIZE (512 * 1024)
#define LSQUIC_LOGGER_MODULE LSQLM_HEADERS
#define LSQUIC_LOG_CONN_ID lsquic_conn_id(lsquic_stream_conn(hs->hs_stream))
#include "lsquic_logger.h"
static const struct frame_reader_callbacks frame_callbacks;
struct headers_stream
{
struct lsquic_stream *hs_stream;
struct lsquic_frame_reader *hs_fr;
struct lsquic_frame_writer *hs_fw;
const struct headers_stream_callbacks
*hs_callbacks;
const struct lsquic_engine_settings
*hs_settings;
void *hs_cb_ctx;
struct lsquic_mm *hs_mm;
struct lsquic_henc hs_henc;
struct lsquic_hdec hs_hdec;
enum {
HS_IS_SERVER = (1 << 0),
HS_HENC_INITED = (1 << 1),
} hs_flags;
};
int
lsquic_headers_stream_send_settings (struct headers_stream *hs,
const struct lsquic_http2_setting *settings, unsigned n_settings)
{
if (0 == lsquic_frame_writer_write_settings(hs->hs_fw, settings,
n_settings))
{
lsquic_stream_wantwrite(hs->hs_stream,
lsquic_frame_writer_have_leftovers(hs->hs_fw));
return 0;
}
else
{
LSQ_WARN("Could not write settings to stream: %s",
strerror(errno));
return -1;
}
}
static lsquic_stream_ctx_t *
headers_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
{
struct headers_stream *hs = stream_if_ctx;
lsquic_hdec_init(&hs->hs_hdec);
if (0 != lsquic_henc_init(&hs->hs_henc))
{
LSQ_WARN("could not initialize HPACK encoder: %s", strerror(errno));
return NULL;
}
hs->hs_flags |= HS_HENC_INITED;
hs->hs_stream = stream;
LSQ_DEBUG("stream created");
hs->hs_fr = lsquic_frame_reader_new((hs->hs_flags & HS_IS_SERVER) ? FRF_SERVER : 0,
MAX_HEADERS_SIZE, hs->hs_mm,
stream, lsquic_stream_read, &hs->hs_hdec,
&frame_callbacks, hs);
if (!hs->hs_fr)
{
LSQ_WARN("could not create frame reader: %s", strerror(errno));
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
return NULL;
}
hs->hs_fw = lsquic_frame_writer_new(hs->hs_mm, stream, 0, &hs->hs_henc,
lsquic_stream_write, lsquic_stream_write_avail,
lsquic_stream_flush, (hs->hs_flags & HS_IS_SERVER));
if (!hs->hs_fw)
{
LSQ_WARN("could not create frame writer: %s", strerror(errno));
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
return NULL;
}
lsquic_stream_wantread(stream, 1);
return (lsquic_stream_ctx_t *) hs;
}
static void
headers_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *ctx)
{
struct headers_stream *hs = (struct headers_stream *) ctx;
if (0 != lsquic_frame_reader_read(hs->hs_fr))
{
LSQ_ERROR("frame reader failed");
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
}
}
static void
headers_on_write (lsquic_stream_t *stream, struct lsquic_stream_ctx *ctx)
{
struct headers_stream *hs = (struct headers_stream *) ctx;
assert(lsquic_frame_writer_have_leftovers(hs->hs_fw));
int s = lsquic_frame_writer_flush(hs->hs_fw);
if (0 == s)
{
lsquic_stream_wantwrite(stream,
lsquic_frame_writer_have_leftovers(hs->hs_fw));
}
else
{
LSQ_WARN("Error writing to stream: %s", strerror(errno));
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
}
}
static void
headers_on_close (lsquic_stream_t *stream, struct lsquic_stream_ctx *ctx)
{
struct headers_stream *hs = (struct headers_stream *) ctx;
LSQ_DEBUG("stream closed");
}
int
lsquic_headers_stream_send_headers (struct headers_stream *hs,
uint32_t stream_id, const struct lsquic_http_headers *headers, int eos,
unsigned weight)
{
LSQ_DEBUG("received compressed headers to send");
int s;
s = lsquic_frame_writer_write_headers(hs->hs_fw, stream_id, headers, eos,
weight);
if (0 == s)
{
lsquic_stream_wantwrite(hs->hs_stream,
lsquic_frame_writer_have_leftovers(hs->hs_fw));
}
else
LSQ_INFO("Error writing headers: %s", strerror(errno));
return s;
}
int
lsquic_headers_stream_send_priority (struct headers_stream *hs,
uint32_t stream_id, int exclusive, uint32_t dep_stream_id, unsigned weight)
{
LSQ_DEBUG("received priority to send");
int s;
if (stream_id == dep_stream_id)
{
LSQ_INFO("stream cannot depend on itself"); /* RFC 7540, Sec. 5.3.1. */
return -1;
}
s = lsquic_frame_writer_write_priority(hs->hs_fw, stream_id, exclusive,
dep_stream_id, weight);
if (0 == s)
{
lsquic_stream_wantwrite(hs->hs_stream,
lsquic_frame_writer_have_leftovers(hs->hs_fw));
}
else
LSQ_INFO("Error writing priority frame: %s", strerror(errno));
return s;
}
struct headers_stream *
lsquic_headers_stream_new (int is_server, struct lsquic_mm *mm,
const struct lsquic_engine_settings *settings,
const struct headers_stream_callbacks *callbacks,
void *cb_ctx)
{
struct headers_stream *hs = calloc(1, sizeof(*hs));
if (!hs)
return NULL;
hs->hs_callbacks = callbacks;
hs->hs_cb_ctx = cb_ctx;
if (is_server)
hs->hs_flags = HS_IS_SERVER;
else
hs->hs_flags = 0;
hs->hs_settings = settings;
hs->hs_mm = mm;
return hs;
}
void
lsquic_headers_stream_destroy (struct headers_stream *hs)
{
if (hs->hs_fr)
lsquic_frame_reader_destroy(hs->hs_fr);
if (hs->hs_fw)
lsquic_frame_writer_destroy(hs->hs_fw);
if (hs->hs_flags & HS_HENC_INITED)
lsquic_henc_cleanup(&hs->hs_henc);
lsquic_hdec_cleanup(&hs->hs_hdec);
free(hs);
}
static const struct lsquic_stream_if headers_stream_if =
{
.on_new_stream = headers_on_new_stream,
.on_read = headers_on_read,
.on_write = headers_on_write,
.on_close = headers_on_close,
};
const struct lsquic_stream_if *const lsquic_headers_stream_if =
&headers_stream_if;
static void
headers_on_incoming_headers (void *ctx, struct uncompressed_headers *uh)
{
struct headers_stream *hs = ctx;
hs->hs_callbacks->hsc_on_headers(hs->hs_cb_ctx, uh);
}
static void
headers_on_push_promise (void *ctx, struct uncompressed_headers *uh)
{
struct headers_stream *hs = ctx;
hs->hs_callbacks->hsc_on_push_promise(hs->hs_cb_ctx, uh);
}
static void
headers_on_priority (void *ctx, uint32_t stream_id, int exclusive,
uint32_t dep_stream_id, unsigned weight)
{
struct headers_stream *hs = ctx;
hs->hs_callbacks->hsc_on_priority(hs->hs_cb_ctx, stream_id, exclusive,
dep_stream_id, weight);
}
static void
headers_on_error (void *ctx, uint32_t stream_id, enum frame_reader_error err)
{
struct headers_stream *hs = ctx;
switch (err)
{
case FR_ERR_DUPLICATE_PSEH:
case FR_ERR_INCOMPL_REQ_PSEH:
case FR_ERR_UNNEC_REQ_PSEH:
case FR_ERR_INCOMPL_RESP_PSEH:
case FR_ERR_UNNEC_RESP_PSEH:
case FR_ERR_UNKNOWN_PSEH:
case FR_ERR_UPPERCASE_HEADER:
case FR_ERR_MISPLACED_PSEH:
case FR_ERR_MISSING_PSEH:
case FR_ERR_DECOMPRESS:
case FR_ERR_HEADERS_TOO_LARGE:
case FR_ERR_SELF_DEP_STREAM:
LSQ_INFO("error %u is a stream error (stream %u)", err, stream_id);
hs->hs_callbacks->hsc_on_stream_error(hs->hs_cb_ctx, stream_id);
break;
case FR_ERR_INVALID_FRAME_SIZE:
case FR_ERR_NONZERO_STREAM_ID:
case FR_ERR_UNEXPECTED_PUSH:
case FR_ERR_ZERO_STREAM_ID:
case FR_ERR_NOMEM:
case FR_ERR_EXPECTED_CONTIN:
LSQ_INFO("error %u is a connection error (stream %u)", err, stream_id);
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
break;
}
}
static void
headers_on_settings (void *ctx, uint16_t setting_id, uint32_t setting_value)
{
struct headers_stream *hs = ctx;
switch (setting_id)
{
case SETTINGS_HEADER_TABLE_SIZE:
if (setting_value > MAX_HEADER_TABLE_SIZE)
{
LSQ_INFO("tried to update table size to %u, which is larger than "
"allowed maximum of %u bytes", setting_value,
MAX_HEADER_TABLE_SIZE);
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
}
else
{
LSQ_INFO("update hpack table size to %u", setting_value);
lsquic_henc_set_max_capacity(&hs->hs_henc, setting_value);
}
break;
case SETTINGS_MAX_HEADER_LIST_SIZE:
LSQ_INFO("set max header list size to %u", setting_value);
lsquic_frame_writer_max_header_list_size(hs->hs_fw, setting_value);
break;
case SETTINGS_ENABLE_PUSH:
LSQ_INFO("got setting enable_push: %u", setting_value);
if (hs->hs_flags & HS_IS_SERVER)
{
if (setting_value <= 1)
hs->hs_callbacks->hsc_on_enable_push(hs->hs_cb_ctx,
setting_value);
else
{
LSQ_INFO("invalid value of enable_push");
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
}
}
else
{
LSQ_INFO("it is an error to receive enable_push setting in "
"client mode");
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
}
break;
case SETTINGS_MAX_CONCURRENT_STREAMS:
case SETTINGS_INITIAL_WINDOW_SIZE:
case SETTINGS_MAX_FRAME_SIZE:
/* [draft-ietf-quic-http-00], Section 3 */
LSQ_INFO("Specifying setting 0x%X is a QUIC error", setting_id);
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
break;
default:
LSQ_INFO("Ignoring unknown setting 0x%X; value 0x%X", setting_id,
setting_value);
break;
}
}
int
lsquic_headers_stream_push_promise (struct headers_stream *hs,
uint32_t stream_id, uint32_t promised_stream_id,
const struct iovec *path, const struct iovec *host,
const struct lsquic_http_headers *headers)
{
int s;
LSQ_DEBUG("promising stream %u in response to stream %u",
promised_stream_id, stream_id);
s = lsquic_frame_writer_write_promise(hs->hs_fw, stream_id,
promised_stream_id, path, host, headers);
if (0 == s)
{
lsquic_stream_wantwrite(hs->hs_stream,
lsquic_frame_writer_have_leftovers(hs->hs_fw));
}
else
LSQ_INFO("Error writing push promise: %s", strerror(errno));
return s;
}
static const struct frame_reader_callbacks frame_callbacks = {
.frc_on_headers = headers_on_incoming_headers,
.frc_on_push_promise = headers_on_push_promise,
.frc_on_error = headers_on_error,
.frc_on_settings = headers_on_settings,
.frc_on_priority = headers_on_priority,
};

View file

@ -0,0 +1,71 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_headers_stream.h -- HEADERS stream interface
*/
#ifndef LSQUIC_HEADERS_STREAM_H
#define LSQUIC_HEADERS_STREAM_H 1
#include <stdint.h>
struct iovec;
struct lsquic_stream_if;
struct lsquic_stream;
struct lsquic_mm;
struct lsquic_http_headers;
struct lsquic_frame_reader;
struct lsquic_frame_writer;
struct uncompressed_headers;
struct lsquic_engine_settings;
struct lsquic_http2_setting;
/* Incoming frames result in new objects or events. Callbacks in this
* struct are used to dispatch them.
*/
struct headers_stream_callbacks
{
void (*hsc_on_headers)
(void *frame_cb_ctx, struct uncompressed_headers *);
void (*hsc_on_enable_push) (void *hs_cb_ctx, int enable_push);
void (*hsc_on_push_promise)
(void *frame_cb_ctx, struct uncompressed_headers *);
void (*hsc_on_priority) (void *hs_cb_ctx, uint32_t stream_id,
int exclusive, uint32_t dep_stream_id, unsigned weight);
void (*hsc_on_stream_error) (void *hs_cb_ctx, uint32_t stream_id);
void (*hsc_on_conn_error) (void *hs_cb_ctx);
};
struct headers_stream *
lsquic_headers_stream_new (int is_server, struct lsquic_mm *,
const struct lsquic_engine_settings *,
const struct headers_stream_callbacks *,
void *hs_cb_ctx);
void
lsquic_headers_stream_destroy (struct headers_stream *);
int
lsquic_headers_stream_send_headers (struct headers_stream *hs,
uint32_t stream_id,
const struct lsquic_http_headers *, int eos,
unsigned weight);
int
lsquic_headers_stream_push_promise (struct headers_stream *hs,
uint32_t stream_id, uint32_t promised_stream_id,
const struct iovec *path, const struct iovec *host,
const struct lsquic_http_headers *);
int
lsquic_headers_stream_send_priority (struct headers_stream *hs,
uint32_t stream_id, int exclusive, uint32_t dep_stream_id, unsigned weight);
int
lsquic_headers_stream_send_settings (struct headers_stream *hs,
const struct lsquic_http2_setting *, unsigned count);
extern const struct lsquic_stream_if *const lsquic_headers_stream_if;
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,55 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_HPACK_COMMON_H
#define LSQUIC_HPACK_COMMON_H
#define HPACK_STATIC_TABLE_SIZE 61
#define INITIAL_DYNAMIC_TABLE_SIZE 4096
/* RFC 7541, Section 4.1:
*
* " The size of the dynamic table is the sum of the size of its entries.
* "
* " The size of an entry is the sum of its name's length in octets (as
* " defined in Section 5.2), its value's length in octets, and 32.
*/
#define DYNAMIC_ENTRY_OVERHEAD 32
/**
* @typedef hpack_hdr_tbl_t
* @brief A struct for the static table (name - value)
*/
typedef struct hpack_hdr_tbl_s
{
const char *name;
uint16_t name_len;
const char *val;
uint16_t val_len;
} hpack_hdr_tbl_t;
/**
* @typedef hpack_huff_encode_t
* @brief Huffman encode struct
*/
typedef struct hpack_huff_encode_s
{
uint32_t code;
int bits;
} hpack_huff_encode_t;
/**
* @typedef hpack_huff_decode_t
* @brief Huffman decode struct
*/
typedef struct hpack_huff_decode_s
{
uint8_t state;
uint8_t flags;
uint8_t sym;
} hpack_huff_decode_t;
extern const hpack_huff_decode_t lsquic_hpack_huff_decode_tables[256][16];
extern const hpack_huff_encode_t lsquic_hpack_huff_encode_tables[257];
extern const hpack_hdr_tbl_t lsquic_hpack_stx_tab[HPACK_STATIC_TABLE_SIZE];
#endif

View file

@ -0,0 +1,419 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hdec.c - HPACK decoder
*/
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "lsquic_arr.h"
#include "lsquic_hpack_common.h"
#include "lsquic_hpack_dec.h"
/* Dynamic table entry: */
struct dec_table_entry
{
uint16_t dte_name_len;
uint16_t dte_val_len;
char dte_buf[0]; /* Contains both name and value */
};
#define DTE_NAME(dte) ((dte)->dte_buf)
#define DTE_VALUE(dte) (&(dte)->dte_buf[(dte)->dte_name_len])
enum
{
HPACK_HUFFMAN_FLAG_ACCEPTED = 0x01,
HPACK_HUFFMAN_FLAG_SYM = 0x02,
HPACK_HUFFMAN_FLAG_FAIL = 0x04,
};
typedef struct hpack_huff_decode_status_s
{
uint8_t state;
uint8_t eos;
} hpack_huff_decode_status_t;
void
lsquic_hdec_init (struct lsquic_hdec *dec)
{
memset(dec, 0, sizeof(*dec));
dec->hpd_max_capacity = INITIAL_DYNAMIC_TABLE_SIZE;
dec->hpd_cur_max_capacity = INITIAL_DYNAMIC_TABLE_SIZE;
lsquic_arr_init(&dec->hpd_dyn_table);
}
void
lsquic_hdec_cleanup (struct lsquic_hdec *dec)
{
uintptr_t val;
while (lsquic_arr_count(&dec->hpd_dyn_table) > 0)
{
val = lsquic_arr_pop(&dec->hpd_dyn_table);
free((struct dec_table_entry *) val);
}
lsquic_arr_cleanup(&dec->hpd_dyn_table);
}
//https://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-5.1
#ifdef NDEBUG
static
#endif
int
lsquic_hdec_dec_int (const unsigned char **src, const unsigned char *src_end,
uint8_t prefix_bits, uint32_t *value)
{
uint32_t B, M;
uint8_t prefix_max = (1 << prefix_bits) - 1;
*value = (*(*src)++ & prefix_max);
if (*value < prefix_max)
return 0;
/* To optimize the loop for the normal case, the overflow is checked
* outside the loop. The decoder is limited to 28-bit integer values,
* which is far above limitations imposed by the APIs (16-bit integers).
*/
M = 0;
do
{
if ((*src) >= src_end)
return -1;
B = *(*src)++;
*value = *value + ((B & 0x7f) << M);
M += 7;
}
while (B & 0x80);
return -(M > sizeof(*value) * 8);
}
static void
hdec_drop_oldest_entry (struct lsquic_hdec *dec)
{
struct dec_table_entry *entry;
entry = (void *) lsquic_arr_shift(&dec->hpd_dyn_table);
dec->hpd_cur_capacity -= DYNAMIC_ENTRY_OVERHEAD + entry->dte_name_len
+ entry->dte_val_len;
free(entry);
}
static void
hdec_remove_overflow_entries (struct lsquic_hdec *dec)
{
while (dec->hpd_cur_capacity > dec->hpd_cur_max_capacity)
hdec_drop_oldest_entry(dec);
}
static void
hdec_update_max_capacity (struct lsquic_hdec *dec, uint32_t new_capacity)
{
dec->hpd_cur_max_capacity = new_capacity;
hdec_remove_overflow_entries(dec);
}
void
lsquic_hdec_set_max_capacity (struct lsquic_hdec *dec, unsigned max_capacity)
{
dec->hpd_max_capacity = max_capacity;
hdec_update_max_capacity(dec, max_capacity);
}
static unsigned char *
hdec_huff_dec4bits (uint8_t src_4bits, unsigned char *dst,
hpack_huff_decode_status_t *status)
{
const hpack_huff_decode_t cur_dec_code =
lsquic_hpack_huff_decode_tables[status->state][src_4bits];
if (cur_dec_code.flags & HPACK_HUFFMAN_FLAG_FAIL) {
return NULL; //failed
}
if (cur_dec_code.flags & HPACK_HUFFMAN_FLAG_SYM)
{
*dst = cur_dec_code.sym;
dst++;
}
status->state = cur_dec_code.state;
status->eos = ((cur_dec_code.flags & HPACK_HUFFMAN_FLAG_ACCEPTED) != 0);
return dst;
}
static int
hdec_huff_decode (const unsigned char *src, int src_len,
unsigned char *dst, int dst_len)
{
const unsigned char *p_src = src;
const unsigned char *src_end = src + src_len;
unsigned char *p_dst = dst;
unsigned char *dst_end = dst + dst_len;
hpack_huff_decode_status_t status = { 0, 1 };
while (p_src != src_end)
{
if (p_dst == dst_end)
return -2;
if ((p_dst = hdec_huff_dec4bits(*p_src >> 4, p_dst, &status))
== NULL)
return -1;
if (p_dst == dst_end)
return -2;
if ((p_dst = hdec_huff_dec4bits(*p_src & 0xf, p_dst, &status))
== NULL)
return -1;
++p_src;
}
if (!status.eos)
return -1;
return p_dst - dst;
}
//reutrn the length in the dst, also update the src
static int
hdec_dec_str (unsigned char *dst, size_t dst_len, const unsigned char **src,
const unsigned char *src_end)
{
if ((*src) == src_end)
return 0;
int is_huffman = (*(*src) & 0x80);
uint32_t len;
if (0 != lsquic_hdec_dec_int(src, src_end, 7, &len))
return -2; //wrong int
int ret = 0;
if ((uint32_t)(src_end - (*src)) < len) {
return -2; //wrong int
}
if (is_huffman)
{
ret = hdec_huff_decode(*src, len, dst, dst_len);
if (ret < 0)
return -3; //Wrong code
(*src) += len;
}
else
{
if (dst_len < (size_t)(src_end - (*src)))
ret = -3; //dst not enough space
else
{
memcpy(dst, (*src), len);
(*src) += len;
ret = len;
}
}
return ret;
}
/* hpd_dyn_table is a dynamic array. New entries are pushed onto it,
* while old entries are shifted from it.
*/
static struct dec_table_entry *
hdec_get_table_entry (struct lsquic_hdec *dec, uint32_t index)
{
uintptr_t val;
index -= HPACK_STATIC_TABLE_SIZE;
if (index == 0 || index > lsquic_arr_count(&dec->hpd_dyn_table))
return NULL;
index = lsquic_arr_count(&dec->hpd_dyn_table) - index;
val = lsquic_arr_get(&dec->hpd_dyn_table, index);
return (struct dec_table_entry *) val;
}
#ifdef NDEBUG
static
#endif
int
lsquic_hdec_push_entry (struct lsquic_hdec *dec, const char *name,
uint16_t name_len, const char *val, uint16_t val_len)
{
struct dec_table_entry *entry;
size_t size;
size = sizeof(*entry) + name_len + val_len;
entry = malloc(size);
if (!entry)
return -1;
if (0 != lsquic_arr_push(&dec->hpd_dyn_table, (uintptr_t) entry))
{
free(entry);
return -1;
}
dec->hpd_cur_capacity += DYNAMIC_ENTRY_OVERHEAD + name_len + val_len;
entry->dte_name_len = name_len;
entry->dte_val_len = val_len;
memcpy(DTE_NAME(entry), name, name_len);
memcpy(DTE_VALUE(entry), val, val_len);
return 0;
}
int
lsquic_hdec_decode (struct lsquic_hdec *dec,
const unsigned char **src, const unsigned char *src_end,
char *dst, char *const dst_end, uint16_t *name_len, uint16_t *val_len)
{
struct dec_table_entry *entry;
uint32_t index, new_capacity;
int indexed_type, len;
if ((*src) == src_end)
return 0;
while ((*(*src) & 0xe0) == 0x20) //001 xxxxx
{
if (0 != lsquic_hdec_dec_int(src, src_end, 5, &new_capacity))
return -1;
if (new_capacity > dec->hpd_max_capacity)
return -1;
hdec_update_max_capacity(dec, new_capacity);
if (*src == src_end)
return 0;
}
/* lsquic_hdec_dec_int() sets `index' and advances `src'. If we do not call
* it, we set `index' and advance `src' ourselves:
*/
if (*(*src) & 0x80) //1 xxxxxxx
{
if (0 != lsquic_hdec_dec_int(src, src_end, 7, &index))
return -1;
indexed_type = 3; //need to parse value
}
else if (*(*src) > 0x40) //01 xxxxxx
{
if (0 != lsquic_hdec_dec_int(src, src_end, 6, &index))
return -1;
indexed_type = 0;
}
else if (*(*src) == 0x40) //custmized //0100 0000
{
indexed_type = 0;
index = 0;
++(*src);
}
//Never indexed
else if (*(*src) == 0x10) //00010000
{
indexed_type = 2;
index = 0;
++(*src);
}
else if ((*(*src) & 0xf0) == 0x10) //0001 xxxx
{
if (0 != lsquic_hdec_dec_int(src, src_end, 4, &index))
return -1;
indexed_type = 2;
}
//without indexed
else if (*(*src) == 0x00) //0000 0000
{
indexed_type = 1;
index = 0;
++(*src);
}
else // 0000 xxxx
{
if (0 != lsquic_hdec_dec_int(src, src_end, 4, &index))
return -1;
indexed_type = 1;
}
char *const name = dst;
if (index > 0)
{
if (index <= HPACK_STATIC_TABLE_SIZE) //static table
{
if (lsquic_hpack_stx_tab[index - 1].name_len > dst_end - dst)
return -1;
*name_len = lsquic_hpack_stx_tab[index - 1].name_len;
memcpy(name, lsquic_hpack_stx_tab[index - 1].name, *name_len);
if (indexed_type == 3)
{
if (lsquic_hpack_stx_tab[index - 1].name_len +
lsquic_hpack_stx_tab[index - 1].val_len > dst_end - dst)
return -1;
*val_len = lsquic_hpack_stx_tab[index - 1].val_len;
memcpy(name + *name_len, lsquic_hpack_stx_tab[index - 1].val, *val_len);
return 1;
}
}
else
{
entry = hdec_get_table_entry(dec, index);
if (entry == NULL)
return -1;
if (entry->dte_name_len > dst_end - dst)
return -1;
*name_len = entry->dte_name_len;
memcpy(name, DTE_NAME(entry), *name_len);
if (indexed_type == 3)
{
if (entry->dte_name_len + entry->dte_val_len > dst_end - dst)
return -1;
*val_len = entry->dte_val_len;
memcpy(name + *name_len, DTE_VALUE(entry), *val_len);
return 1;
}
}
}
else
{
len = hdec_dec_str((unsigned char *)name, dst_end - dst, src, src_end);
if (len < 0)
return len; //error
if (len > UINT16_MAX)
return -2;
*name_len = len;
}
len = hdec_dec_str((unsigned char *)name + *name_len,
dst_end - dst - *name_len, src, src_end);
if (len < 0)
return len; //error
if (len > UINT16_MAX)
return -2;
*val_len = len;
if (indexed_type == 0)
{
if (0 != lsquic_hdec_push_entry(dec, name, *name_len,
name + *name_len, *val_len))
return -1; //error
}
return 1;
}

View file

@ -0,0 +1,52 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hdec.h - HPACK decoder
*/
#ifndef LSQUIC_HPACK_DEC_H
#define LSQUIC_HPACK_DEC_H
struct lsquic_hdec
{
unsigned hpd_max_capacity; /* Maximum set by caller */
unsigned hpd_cur_max_capacity; /* Adjusted at runtime */
unsigned hpd_cur_capacity;
struct lsquic_arr hpd_dyn_table;
};
void
lsquic_hdec_init (struct lsquic_hdec *);
void
lsquic_hdec_cleanup (struct lsquic_hdec *);
/** @lsquic_hdecode
* @brief HPACK decode one name/value item
* @param[in,out] dec - A pointer to a valid HPACK API struct
* @param[in,out] src - Address of pointer to source buffer
* @param[in] src_end - A pointer to end of source buffer
* @param[out] dst - A pointer to destination buffer
* @param[out] dst_end - A pointer to end of destination buffer
* @param[out] name_len - The item name's length
* @param[out] value_len - The item value's length
* @return 1: OK, 0: end, -1: FAIL
*/
int
lsquic_hdec_decode (struct lsquic_hdec *dec,
const unsigned char **src, const unsigned char *src_end,
char *dst, char *const dst_end, uint16_t *name_len, uint16_t *val_len);
void
lsquic_hdec_set_max_capacity (struct lsquic_hdec *, unsigned);
#ifndef NDEBUG
int
lsquic_hdec_dec_int (const unsigned char **src, const unsigned char *src_end,
uint8_t prefix_bits, uint32_t *value);
int
lsquic_hdec_push_entry (struct lsquic_hdec *dec, const char *name,
uint16_t name_len, const char *val, uint16_t val_len);
#endif
#endif

View file

@ -0,0 +1,855 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hpack_enc.c - HPACK encoder
*/
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic_hpack_common.h"
#include "lsquic_hpack_enc.h"
#include "lsquic_xxhash.h"
struct double_enc_head
{
struct enc_head by_name;
struct enc_head by_nameval;
};
struct enc_table_entry
{
/* An entry always lives on all three lists */
STAILQ_ENTRY(enc_table_entry) ete_next_nameval,
ete_next_name,
ete_next_all;
unsigned ete_id;
unsigned ete_nameval_hash;
unsigned ete_name_hash;
uint16_t ete_name_len;
uint16_t ete_val_len;
char ete_buf[0];
};
#define ETE_NAME(ete) ((ete)->ete_buf)
#define ETE_VALUE(ete) (&(ete)->ete_buf[(ete)->ete_name_len])
#define N_BUCKETS(n_bits) (1U << (n_bits))
#define BUCKNO(n_bits, hash) ((hash) & (N_BUCKETS(n_bits) - 1))
int
lsquic_henc_init (struct lsquic_henc *enc)
{
struct double_enc_head *buckets;
unsigned nbits = 2;
unsigned i;
buckets = malloc(sizeof(buckets[0]) * N_BUCKETS(nbits));
if (!buckets)
return -1;
for (i = 0; i < N_BUCKETS(nbits); ++i)
{
STAILQ_INIT(&buckets[i].by_name);
STAILQ_INIT(&buckets[i].by_nameval);
}
memset(enc, 0, sizeof(*enc));
STAILQ_INIT(&enc->hpe_all_entries);
enc->hpe_max_capacity = INITIAL_DYNAMIC_TABLE_SIZE;
enc->hpe_buckets = buckets;
/* The initial value of the entry ID is completely arbitrary. As long as
* there are fewer than 2^32 dynamic table entries, the math to calculate
* the entry ID works. To prove to ourselves that the wraparound works
* and to have the unit tests cover it, we initialize the next ID so that
* it is just about to wrap around.
*/
enc->hpe_next_id = ~0 - 3;
enc->hpe_nbits = nbits;
enc->hpe_nelem = 0;
return 0;
}
void
lsquic_henc_cleanup (struct lsquic_henc *enc)
{
struct enc_table_entry *entry, *next;
for (entry = STAILQ_FIRST(&enc->hpe_all_entries); entry; entry = next)
{
next = STAILQ_NEXT(entry, ete_next_all);
free(entry);
}
free(enc->hpe_buckets);
}
//not find return 0, otherwise return the index
#ifdef NDEBUG
static
#endif
unsigned
lsquic_henc_get_stx_tab_id (const char *name, uint16_t name_len,
const char *val, uint16_t val_len, int *val_matched)
{
if (name_len < 3)
return 0;
*val_matched = 0;
//check value first
int i = -1;
switch (*val)
{
case 'G':
i = 1;
break;
case 'P':
i = 2;
break;
case '/':
if (val_len == 1)
i = 3;
else if (val_len == 11)
i = 4;
break;
case 'h':
if (val_len == 4)
i = 5;
else if (val_len == 5)
i = 6;
break;
case '2':
if (val_len == 3)
{
switch (*(val + 2))
{
case '0':
i = 7;
break;
case '4':
i = 8;
break;
case '6':
i = 9;
break;
default:
break;
}
}
break;
case '3':
i = 10;
break;
case '4':
if (val_len == 3)
{
switch (*(val + 2))
{
case '0':
i = 11;
break;
case '4':
i = 12;
default:
break;
}
}
break;
case '5':
i = 13;
break;
case 'g':
i = 15;
break;
default:
break;
}
if (i > 0 && lsquic_hpack_stx_tab[i].val_len == val_len
&& lsquic_hpack_stx_tab[i].name_len == name_len
&& memcmp(val, lsquic_hpack_stx_tab[i].val, val_len) == 0
&& memcmp(name, lsquic_hpack_stx_tab[i].name, name_len) == 0)
{
*val_matched = 1;
return i + 1;
}
//macth name only checking
i = -1;
switch (*name)
{
case ':':
switch (*(name + 1))
{
case 'a':
i = 0;
break;
case 'm':
i = 1;
break;
case 'p':
i = 3;
break;
case 's':
if (*(name + 2) == 'c') //:scheme
i = 5;
else
i = 7;
break;
default:
break;
}
break;
case 'a':
switch (name_len)
{
case 3:
i = 20; //age
break;
case 5:
i = 21; //allow
break;
case 6:
i = 18; //accept
break;
case 13:
if (*(name + 1) == 'u')
i = 22; //authorization
else
i = 17; //accept-ranges
break;
case 14:
i = 14; //accept-charset
break;
case 15:
if (*(name + 7) == 'l')
i = 16; //accept-language,
else
i = 15;// accept-encoding
break;
case 27:
i = 19;//access-control-allow-origin
break;
default:
break;
}
break;
case 'c':
switch (name_len)
{
case 6:
i = 31; //cookie
break;
case 12:
i = 30; //content-type
break;
case 13:
if (*(name + 1) == 'a')
i = 23; //cache-control
else
i = 29; //content-range
break;
case 14:
i = 27; //content-length
break;
case 16:
switch (*(name + 9))
{
case 'n':
i = 25 ;//content-encoding
break;
case 'a':
i = 26; //content-language
break;
case 'o':
i = 28; //content-location
default:
break;
}
break;
case 19:
i = 24; //content-disposition
break;
}
break;
case 'd':
i = 32 ;//date
break;
case 'e':
switch (name_len)
{
case 4:
i = 33; //etag
break;
case 6:
i = 34;
break;
case 7:
i = 35;
break;
default:
break;
}
break;
case 'f':
i = 36; //from
break;
case 'h':
i = 37; //host
break;
case 'i':
switch (name_len)
{
case 8:
if (*(name + 3) == 'm')
i = 38; //if-match
else
i = 41; //if-range
break;
case 13:
i = 40; //if-none-match
break;
case 17:
i = 39; //if-modified-since
break;
case 19:
i = 42; //if-unmodified-since
break;
default:
break;
}
break;
case 'l':
switch (name_len)
{
case 4:
i = 44; //link
break;
case 8:
i = 45; //location
break;
case 13:
i = 43; //last-modified
break;
default:
break;
}
break;
case 'm':
i = 46; //max-forwards
break;
case 'p':
if (name_len == 18)
i = 47; //proxy-authenticate
else
i = 48; //proxy-authorization
break;
case 'r':
if (name_len >= 5)
{
switch (*(name + 4))
{
case 'e':
if (name_len == 5)
i = 49; //range
else
i = 51; //refresh
break;
case 'r':
i = 50; //referer
break;
case 'y':
i = 52; //retry-after
break;
default:
break;
}
}
break;
case 's':
switch (name_len)
{
case 6:
i = 53; //server
break;
case 10:
i = 54; //set-cookie
break;
case 25:
i = 55; //strict-transport-security
break;
default:
break;
}
break;
case 't':
i = 56;//transfer-encoding
break;
case 'u':
i = 57; //user-agent
break;
case 'v':
if (name_len == 4)
i = 58;
else
i = 59;
break;
case 'w':
i = 60;
break;
default:
break;
}
if (i >= 0
&& lsquic_hpack_stx_tab[i].name_len == name_len
&& memcmp(name, lsquic_hpack_stx_tab[i].name, name_len) == 0)
return i + 1;
return 0;
}
/* Given a dynamic entry, return its table ID */
static unsigned
henc_calc_table_id (const struct lsquic_henc *enc,
const struct enc_table_entry *entry)
{
return HPACK_STATIC_TABLE_SIZE
+ (enc->hpe_next_id - entry->ete_id)
;
}
static unsigned
henc_find_table_id (struct lsquic_henc *enc, const char *name,
uint16_t name_len, const char *value, uint16_t value_len,
int *val_matched)
{
struct enc_table_entry *entry;
unsigned name_hash, nameval_hash, buckno, static_table_id;
XXH32_state_t hash_state;
/* First, look for a match in the static table: */
static_table_id = lsquic_henc_get_stx_tab_id(name, name_len, value,
value_len, val_matched);
if (static_table_id > 0 && *val_matched)
return static_table_id;
/* Search by name and value: */
XXH32_reset(&hash_state, (uintptr_t) enc);
XXH32_update(&hash_state, &name_len, sizeof(name_len));
XXH32_update(&hash_state, name, name_len);
name_hash = XXH32_digest(&hash_state);
XXH32_update(&hash_state, &value_len, sizeof(value_len));
XXH32_update(&hash_state, value, value_len);
nameval_hash = XXH32_digest(&hash_state);
buckno = BUCKNO(enc->hpe_nbits, nameval_hash);
STAILQ_FOREACH(entry, &enc->hpe_buckets[buckno].by_nameval, ete_next_nameval)
if (nameval_hash == entry->ete_nameval_hash &&
name_len == entry->ete_name_len &&
value_len == entry->ete_val_len &&
0 == memcmp(name, ETE_NAME(entry), name_len) &&
0 == memcmp(value, ETE_VALUE(entry), value_len))
{
*val_matched = 1;
return henc_calc_table_id(enc, entry);
}
/* Name/value match is not found, but if the caller found a matching
* static table entry, no need to continue to search:
*/
if (static_table_id > 0)
return static_table_id;
/* Search by name only: */
buckno = BUCKNO(enc->hpe_nbits, name_hash);
STAILQ_FOREACH(entry, &enc->hpe_buckets[buckno].by_name, ete_next_name)
if (name_hash == entry->ete_name_hash &&
name_len == entry->ete_name_len &&
0 == memcmp(name, ETE_NAME(entry), name_len))
{
*val_matched = 0;
return henc_calc_table_id(enc, entry);
}
return 0;
}
////https://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-5.1
static unsigned char *
henc_enc_int (unsigned char *dst, unsigned char *const end, uint32_t value,
uint8_t prefix_bits)
{
unsigned char *const dst_orig = dst;
/* This function assumes that at least one byte is available */
assert(dst < end);
if (value < (uint32_t)(1 << prefix_bits) - 1)
*dst++ |= value;
else
{
*dst++ |= (1 << prefix_bits) - 1;
value -= (1 << prefix_bits) - 1;
while (value >= 128)
{
if (dst < end)
{
*dst++ = (0x80 | value);
value >>= 7;
}
else
return dst_orig;
}
if (dst < end)
*dst++ = value;
else
return dst_orig;
}
return dst;
}
static int
henc_huffman_enc (const unsigned char *src, const unsigned char *const src_end,
unsigned char *dst, int dst_len)
{
const unsigned char *p_src = src;
unsigned char *p_dst = dst;
unsigned char *dst_end = p_dst + dst_len;
uint64_t bits = 0;
int bits_left = 40;
hpack_huff_encode_t cur_enc_code;
assert(dst_len > 0);
while (p_src != src_end)
{
cur_enc_code = lsquic_hpack_huff_encode_tables[(int) *p_src++];
assert(bits_left >= cur_enc_code.bits); // (possible negative shift, undefined behavior)
bits |= (uint64_t)cur_enc_code.code << (bits_left - cur_enc_code.bits);
bits_left -= cur_enc_code.bits;
while (bits_left <= 32)
{
*p_dst++ = bits >> 32;
bits <<= 8;
bits_left += 8;
if (p_dst == dst_end)
return -1; //dst does not have enough space
}
}
if (bits_left != 40)
{
assert(bits_left < 40 && bits_left > 0);
bits |= ((uint64_t)1 << bits_left) - 1;
*p_dst++ = bits >> 32;
}
return p_dst - dst;
}
#ifdef NDEBUG
static
#endif
int
lsquic_henc_enc_str (unsigned char *const dst, size_t dst_len,
const unsigned char *str, uint16_t str_len)
{
unsigned char size_buf[4];
unsigned char *p;
unsigned size_len;
int rc;
if (dst_len > 1)
/* We guess that the string size fits into a single byte -- meaning
* compressed string of size 126 and smaller -- which is the normal
* case. Thus, we immediately write compressed string to the output
* buffer. If our guess is not correct, we fix it later.
*/
rc = henc_huffman_enc(str, str + str_len, dst + 1, dst_len - 1);
else if (dst_len == 1)
/* Here, the call can only succeed if the string to encode is empty. */
rc = 0;
else
return -1;
/*
* Check if need huffman encoding or not
* Comment: (size_t)rc <= str_len = means if same length, still use Huffman
* ^
*/
if (rc > 0 && (size_t)rc <= str_len)
{
if (rc < 127)
{
*dst = 0x80 | rc;
return 1 + rc;
}
size_buf[0] = 0x80;
str_len = rc;
str = dst + 1;
}
else if (str_len <= dst_len - 1)
{
if (str_len < 127)
{
*dst = str_len;
memcpy(dst + 1, str, str_len);
return 1 + str_len;
}
size_buf[0] = 0x00;
}
else
return -1;
/* The guess of one-byte size was incorrect. Perform necessary
* adjustments.
*/
p = henc_enc_int(size_buf, size_buf + sizeof(size_buf), str_len, 7);
if (p == size_buf)
return -1;
size_len = p - size_buf;
assert(size_len > 1);
/* Check if there is enough room in the output buffer for both
* encoded size and the string.
*/
if (size_len + str_len > dst_len)
return -1;
memmove(dst + size_len, str, str_len);
memcpy(dst, size_buf, size_len);
return size_len + str_len;
}
static void
henc_drop_oldest_entry (struct lsquic_henc *enc)
{
struct enc_table_entry *entry;
unsigned buckno;
entry = STAILQ_FIRST(&enc->hpe_all_entries);
assert(entry);
STAILQ_REMOVE_HEAD(&enc->hpe_all_entries, ete_next_all);
buckno = BUCKNO(enc->hpe_nbits, entry->ete_nameval_hash);
assert(entry == STAILQ_FIRST(&enc->hpe_buckets[buckno].by_nameval));
STAILQ_REMOVE_HEAD(&enc->hpe_buckets[buckno].by_nameval, ete_next_nameval);
buckno = BUCKNO(enc->hpe_nbits, entry->ete_name_hash);
assert(entry == STAILQ_FIRST(&enc->hpe_buckets[buckno].by_name));
STAILQ_REMOVE_HEAD(&enc->hpe_buckets[buckno].by_name, ete_next_name);
enc->hpe_cur_capacity -= DYNAMIC_ENTRY_OVERHEAD + entry->ete_name_len
+ entry->ete_val_len;
--enc->hpe_nelem;
free(entry);
}
static void
henc_remove_overflow_entries (struct lsquic_henc *enc)
{
while (enc->hpe_cur_capacity > enc->hpe_max_capacity)
henc_drop_oldest_entry(enc);
}
static int
henc_grow_tables (struct lsquic_henc *enc)
{
struct double_enc_head *new_buckets, *new[2];
struct enc_table_entry *entry;
unsigned n, old_nbits;
int idx;
old_nbits = enc->hpe_nbits;
new_buckets = malloc(sizeof(enc->hpe_buckets[0])
* N_BUCKETS(old_nbits + 1));
if (!new_buckets)
return -1;
for (n = 0; n < N_BUCKETS(old_nbits); ++n)
{
new[0] = &new_buckets[n];
new[1] = &new_buckets[n + N_BUCKETS(old_nbits)];
STAILQ_INIT(&new[0]->by_name);
STAILQ_INIT(&new[1]->by_name);
STAILQ_INIT(&new[0]->by_nameval);
STAILQ_INIT(&new[1]->by_nameval);
while ((entry = STAILQ_FIRST(&enc->hpe_buckets[n].by_name)))
{
STAILQ_REMOVE_HEAD(&enc->hpe_buckets[n].by_name, ete_next_name);
idx = (BUCKNO(old_nbits + 1, entry->ete_name_hash) >> old_nbits) & 1;
STAILQ_INSERT_TAIL(&new[idx]->by_name, entry, ete_next_name);
}
while ((entry = STAILQ_FIRST(&enc->hpe_buckets[n].by_nameval)))
{
STAILQ_REMOVE_HEAD(&enc->hpe_buckets[n].by_nameval, ete_next_nameval);
idx = (BUCKNO(old_nbits + 1, entry->ete_nameval_hash) >> old_nbits) & 1;
STAILQ_INSERT_TAIL(&new[idx]->by_nameval, entry, ete_next_nameval);
}
}
free(enc->hpe_buckets);
enc->hpe_nbits = old_nbits + 1;
enc->hpe_buckets = new_buckets;
return 0;
}
#ifdef NDEBUG
static
#endif
int
lsquic_henc_push_entry (struct lsquic_henc *enc, const char *name,
uint16_t name_len, const char *value, uint16_t value_len)
{
unsigned name_hash, nameval_hash, buckno;
struct enc_table_entry *entry;
XXH32_state_t hash_state;
size_t size;
if (enc->hpe_nelem >= N_BUCKETS(enc->hpe_nbits) / 2 &&
0 != henc_grow_tables(enc))
return -1;
size = sizeof(*entry) + name_len + value_len;
entry = malloc(size);
if (!entry)
return -1;
XXH32_reset(&hash_state, (uintptr_t) enc);
XXH32_update(&hash_state, &name_len, sizeof(name_len));
XXH32_update(&hash_state, name, name_len);
name_hash = XXH32_digest(&hash_state);
XXH32_update(&hash_state, &value_len, sizeof(value_len));
XXH32_update(&hash_state, value, value_len);
nameval_hash = XXH32_digest(&hash_state);
entry->ete_name_hash = name_hash;
entry->ete_nameval_hash = nameval_hash;
entry->ete_name_len = name_len;
entry->ete_val_len = value_len;
entry->ete_id = enc->hpe_next_id++;
memcpy(ETE_NAME(entry), name, name_len);
memcpy(ETE_VALUE(entry), value, value_len);
STAILQ_INSERT_TAIL(&enc->hpe_all_entries, entry, ete_next_all);
buckno = BUCKNO(enc->hpe_nbits, nameval_hash);
STAILQ_INSERT_TAIL(&enc->hpe_buckets[buckno].by_nameval, entry, ete_next_nameval);
buckno = BUCKNO(enc->hpe_nbits, name_hash);
STAILQ_INSERT_TAIL(&enc->hpe_buckets[buckno].by_name, entry, ete_next_name);
enc->hpe_cur_capacity += DYNAMIC_ENTRY_OVERHEAD + name_len + value_len;
++enc->hpe_nelem;
henc_remove_overflow_entries(enc);
return 0;
}
unsigned char *
lsquic_henc_encode (struct lsquic_henc *enc, unsigned char *dst,
unsigned char *dst_end, const char *name, uint16_t name_len,
const char *value, uint16_t value_len, int indexed_type)
{
//indexed_type: 0, Add, 1,: without, 2: never
static const char indexed_prefix_number[] = {0x40, 0x00, 0x10};
unsigned char *const dst_org = dst;
int val_matched, rc;
unsigned table_id;
assert(indexed_type >= 0 && indexed_type <= 2);
if (dst_end <= dst)
return dst_org;
table_id = henc_find_table_id(enc, name, name_len, value, value_len,
&val_matched);
if (table_id > 0)
{
if (val_matched)
{
*dst = 0x80;
dst = henc_enc_int(dst, dst_end, table_id, 7);
/* No need to check return value: we pass it up as-is because
* the behavior is the same.
*/
return dst;
}
else
{
*dst = indexed_prefix_number[indexed_type];
dst = henc_enc_int(dst, dst_end, table_id, ((indexed_type == 0) ? 6 : 4));
if (dst == dst_org)
return dst_org;
}
}
else
{
*dst++ = indexed_prefix_number[indexed_type];
rc = lsquic_henc_enc_str(dst, dst_end - dst, (const unsigned char *)name, name_len);
if (rc < 0)
return dst_org; //Failed to enc this header, return unchanged ptr.
dst += rc;
}
rc = lsquic_henc_enc_str(dst, dst_end - dst, (const unsigned char *)value, value_len);
if (rc < 0)
return dst_org; //Failed to enc this header, return unchanged ptr.
dst += rc;
if (indexed_type == 0)
{
rc = lsquic_henc_push_entry(enc, name, name_len, value, value_len);
if (rc != 0)
return dst_org; //Failed to enc this header, return unchanged ptr.
}
return dst;
}
void
lsquic_henc_set_max_capacity (struct lsquic_henc *enc, unsigned max_capacity)
{
enc->hpe_max_capacity = max_capacity;
henc_remove_overflow_entries(enc);
}
#ifndef NDEBUG
void
lsquic_henc_iter_reset (struct lsquic_henc *enc)
{
enc->hpe_iter = STAILQ_FIRST(&enc->hpe_all_entries);
}
/* Returns 0 if entry is found */
int
lsquic_henc_iter_next (struct lsquic_henc *enc,
struct enc_dyn_table_entry *retval)
{
const struct enc_table_entry *entry;
entry = enc->hpe_iter;
if (!entry)
return -1;
enc->hpe_iter = STAILQ_NEXT(entry, ete_next_all);
retval->name = ETE_NAME(entry);
retval->value = ETE_VALUE(entry);
retval->name_len = entry->ete_name_len;
retval->value_len = entry->ete_val_len;
retval->entry_id = henc_calc_table_id(enc, entry);
return 0;
}
#endif

View file

@ -0,0 +1,102 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hpack_enc.h - HPACK encoder
*/
#ifndef LSQUIC_HPACK_ENC_H
#define LSQUIC_HPACK_ENC_H 1
struct enc_table_entry;
#ifndef NDEBUG
struct enc_dyn_table_entry
{
const char *name, /* Not NUL-terminated */
*value; /* Not NUL-terminated */
unsigned name_len,
value_len;
unsigned entry_id;
};
#endif
STAILQ_HEAD(enc_head, enc_table_entry);
struct double_enc_head;
struct lsquic_henc
{
unsigned hpe_cur_capacity;
unsigned hpe_max_capacity;
/* Each new dynamic table entry gets the next number. It is used to
* calculate the entry's position in the decoder table without having
* to maintain an actual array.
*/
unsigned hpe_next_id;
/* Dynamic table entries (struct enc_table_entry) live in two hash
* tables: name/value hash table and name hash table. These tables
* are the same size.
*/
unsigned hpe_nelem;
unsigned hpe_nbits;
struct enc_head hpe_all_entries;
struct double_enc_head
*hpe_buckets;
#ifndef NDEBUG
const struct enc_table_entry
*hpe_iter;
#endif
};
/* Initialization routine allocates memory. -1 is returned if memory
* could not be allocated. 0 is returned on success.
*/
int
lsquic_henc_init (struct lsquic_henc *);
void
lsquic_henc_cleanup (struct lsquic_henc *);
/** @lsquic_hpack_encode
* @brief HPACK encode one name/value item
* @param[in,out] henc - A pointer to a valid HPACK API struct
* @param[out] dst - A pointer to destination buffer
* @param[out] dst_end - A pointer to end of destination buffer
* @param[in] name - A pointer to the item name
* @param[in] name_len - The item name's length
* @param[in] value - A pointer to the item value
* @param[in] value_len - The item value's length
* @param[in] indexed_type - 0, Add, 1,: without, 2: never
* @return The (possibly advanced) dst pointer
*/
unsigned char *
lsquic_henc_encode (struct lsquic_henc *henc, unsigned char *dst,
unsigned char *dst_end, const char *name, uint16_t name_len,
const char *value, uint16_t value_len, int indexed_type);
void
lsquic_henc_set_max_capacity (struct lsquic_henc *, unsigned);
#ifndef NDEBUG
unsigned
lsquic_henc_get_stx_tab_id (const char *name, uint16_t name_len,
const char *val, uint16_t val_len, int *val_matched);
int
lsquic_henc_push_entry (struct lsquic_henc *enc, const char *name,
uint16_t name_len, const char *value, uint16_t value_len);
int
lsquic_henc_enc_str (unsigned char *const dst, size_t dst_len,
const unsigned char *str, uint16_t str_len);
void
lsquic_henc_iter_reset (struct lsquic_henc *enc);
/* Returns 0 if entry is found */
int
lsquic_henc_iter_next (struct lsquic_henc *enc, struct enc_dyn_table_entry *);
#endif
#endif

View file

@ -0,0 +1,22 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_INT_TYPES_H
#define LSQUIC_INT_TYPES_H 1
/* Types included in this file are only used internally. Types used in
* include/lsquic.h should be listed in include/lsquic_types.h
*/
#include <stdint.h>
typedef uint64_t lsquic_time_t; /* Microseconds since the epoch */
typedef uint64_t lsquic_packno_t;
typedef uint32_t lsquic_ver_tag_t; /* Opaque 4-byte value */
/* The `low' and `high' members are inclusive: if the range only has one
* member, low == high.
*/
struct lsquic_packno_range {
lsquic_packno_t low, high;
};
#endif

View file

@ -0,0 +1,328 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* LSQUIC Logger implementation.
*/
#include <errno.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#define LSQUIC_LOGGER_MODULE LSQLM_LOGGER /* Quis custodiet ipsos custodes? */
#include "lsquic_logger.h"
#include "lsquic.h"
static enum lsquic_logger_timestamp_style g_llts = LLTS_NONE;
static int
null_vprintf (void *ctx, const char *fmt, va_list ap)
{
return 0;
}
static int
file_vprintf (void *ctx, const char *fmt, va_list ap)
{
return vfprintf((FILE *) ctx, fmt, ap);
}
static const struct lsquic_logger_if file_logger_if = {
.vprintf = file_vprintf,
};
static const struct lsquic_logger_if null_logger_if = {
.vprintf = null_vprintf,
};
static void *logger_ctx = NULL;
static const struct lsquic_logger_if *logger_if = &null_logger_if;
enum lsq_log_level lsq_log_levels[N_LSQUIC_LOGGER_MODULES] = {
[LSQLM_NOMODULE] = LSQ_LOG_WARN,
[LSQLM_LOGGER] = LSQ_LOG_WARN,
[LSQLM_EVENT] = LSQ_LOG_WARN,
[LSQLM_ENGINE] = LSQ_LOG_WARN,
[LSQLM_CONN] = LSQ_LOG_WARN,
[LSQLM_RECHIST] = LSQ_LOG_WARN,
[LSQLM_STREAM] = LSQ_LOG_WARN,
[LSQLM_PARSE] = LSQ_LOG_WARN,
[LSQLM_CFCW] = LSQ_LOG_WARN,
[LSQLM_SFCW] = LSQ_LOG_WARN,
[LSQLM_SENDCTL] = LSQ_LOG_WARN,
[LSQLM_ALARMSET] = LSQ_LOG_WARN,
[LSQLM_CRYPTO] = LSQ_LOG_WARN,
[LSQLM_HANDSHAKE] = LSQ_LOG_WARN,
[LSQLM_HSK_ADAPTER] = LSQ_LOG_WARN,
[LSQLM_CUBIC] = LSQ_LOG_WARN,
[LSQLM_HEADERS] = LSQ_LOG_WARN,
[LSQLM_FRAME_READER]= LSQ_LOG_WARN,
[LSQLM_FRAME_WRITER]= LSQ_LOG_WARN,
[LSQLM_CONN_HASH] = LSQ_LOG_WARN,
[LSQLM_ENG_HIST] = LSQ_LOG_WARN,
[LSQLM_SPI] = LSQ_LOG_WARN,
[LSQLM_DI] = LSQ_LOG_WARN,
[LSQLM_PACER] = LSQ_LOG_WARN,
};
const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = {
[LSQLM_NOMODULE] = "",
[LSQLM_LOGGER] = "logger",
[LSQLM_EVENT] = "event",
[LSQLM_ENGINE] = "engine",
[LSQLM_CONN] = "conn",
[LSQLM_RECHIST] = "rechist",
[LSQLM_STREAM] = "stream",
[LSQLM_PARSE] = "parse",
[LSQLM_CFCW] = "cfcw",
[LSQLM_SFCW] = "sfcw",
[LSQLM_SENDCTL] = "sendctl",
[LSQLM_ALARMSET] = "alarmset",
[LSQLM_CRYPTO] = "crypto",
[LSQLM_HANDSHAKE] = "handshake",
[LSQLM_HSK_ADAPTER] = "hsk-adapter",
[LSQLM_CUBIC] = "cubic",
[LSQLM_HEADERS] = "headers",
[LSQLM_FRAME_READER]= "frame-reader",
[LSQLM_FRAME_WRITER]= "frame-writer",
[LSQLM_CONN_HASH] = "conn-hash",
[LSQLM_ENG_HIST] = "eng-hist",
[LSQLM_SPI] = "spi",
[LSQLM_DI] = "di",
[LSQLM_PACER] = "pacer",
};
const char *const lsq_loglevel2str[N_LSQUIC_LOG_LEVELS] = {
[LSQ_LOG_ALERT] = "ALERT",
[LSQ_LOG_CRIT] = "CRIT",
[LSQ_LOG_DEBUG] = "DEBUG",
[LSQ_LOG_EMERG] = "EMERG",
[LSQ_LOG_ERROR] = "ERROR",
[LSQ_LOG_INFO] = "INFO",
[LSQ_LOG_NOTICE] = "NOTICE",
[LSQ_LOG_WARN] = "WARN",
};
static void
lsquic_printf (const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
logger_if->vprintf(logger_ctx, fmt, ap);
va_end(ap);
}
static void
print_timestamp (void)
{
struct tm tm;
struct timeval tv;
gettimeofday(&tv, NULL);
localtime_r(&tv.tv_sec, &tm);
if (g_llts == LLTS_YYYYMMDD_HHMMSSMS)
lsquic_printf("%04d-%02d-%02d %02d:%02d:%02d.%03d ",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, (int) (tv.tv_usec / 1000));
else if (g_llts == LLTS_HHMMSSMS)
lsquic_printf("%02d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min,
tm.tm_sec, (int) (tv.tv_usec / 1000));
else if (g_llts == LLTS_HHMMSSUS)
lsquic_printf("%02d:%02d:%02d.%06d ", tm.tm_hour, tm.tm_min,
tm.tm_sec, (int) tv.tv_usec);
else if (g_llts == LLTS_CHROMELIKE)
lsquic_printf("%02d%02d/%02d%02d%02d.%06d ", tm.tm_mon + 1,
tm.tm_mday,tm.tm_hour, tm.tm_min, tm.tm_sec, (int) tv.tv_usec);
}
void
lsquic_logger_log3 (enum lsq_log_level log_level,
enum lsquic_logger_module module,
uint64_t conn_id, uint32_t stream_id, const char *fmt, ...)
{
const int saved_errno = errno;
if (g_llts != LLTS_NONE)
print_timestamp();
lsquic_printf("[%s] [QUIC:%"PRIu64"-%"PRIu32"] %s: ",
lsq_loglevel2str[log_level], conn_id, stream_id, lsqlm_to_str[module]);
va_list ap;
va_start(ap, fmt);
logger_if->vprintf(logger_ctx, fmt, ap);
va_end(ap);
lsquic_printf("\n");
errno = saved_errno;
}
void
lsquic_logger_log2 (enum lsq_log_level log_level,
enum lsquic_logger_module module,
uint64_t conn_id, const char *fmt, ...)
{
const int saved_errno = errno;
if (g_llts != LLTS_NONE)
print_timestamp();
lsquic_printf("[%s] [QUIC:%"PRIu64"] %s: ",
lsq_loglevel2str[log_level], conn_id, lsqlm_to_str[module]);
va_list ap;
va_start(ap, fmt);
logger_if->vprintf(logger_ctx, fmt, ap);
va_end(ap);
lsquic_printf("\n");
errno = saved_errno;
}
void
lsquic_logger_log1 (enum lsq_log_level log_level,
enum lsquic_logger_module module,
const char *fmt, ...)
{
const int saved_errno = errno;
if (g_llts != LLTS_NONE)
print_timestamp();
lsquic_printf("[%s] %s: ", lsq_loglevel2str[log_level],
lsqlm_to_str[module]);
va_list ap;
va_start(ap, fmt);
logger_if->vprintf(logger_ctx, fmt, ap);
va_end(ap);
lsquic_printf("\n");
errno = saved_errno;
}
void
lsquic_logger_log0 (enum lsq_log_level log_level, const char *fmt, ...)
{
const int saved_errno = errno;
if (g_llts != LLTS_NONE)
print_timestamp();
lsquic_printf("[%s] ", lsq_loglevel2str[log_level]);
va_list ap;
va_start(ap, fmt);
logger_if->vprintf(logger_ctx, fmt, ap);
va_end(ap);
lsquic_printf("\n");
errno = saved_errno;
}
void
lsquic_logger_init (const struct lsquic_logger_if *lif, void *lctx,
unsigned llts)
{
logger_if = lif;
logger_ctx = lctx;
if (llts < N_LLTS)
g_llts = llts;
LSQ_DEBUG("%s called", __func__);
}
enum lsquic_logger_module
lsquic_str_to_logger_module (const char *str)
{
enum lsquic_logger_module i;
for (i = 0; i < sizeof(lsqlm_to_str) / sizeof(lsqlm_to_str[0]); ++i)
if (0 == strcasecmp(lsqlm_to_str[i], str))
return i;
return -1;
}
enum lsq_log_level
lsquic_str_to_log_level (const char *str)
{
if (0 == strcasecmp(str, "emerg"))
return LSQ_LOG_EMERG;
if (0 == strcasecmp(str, "alert"))
return LSQ_LOG_ALERT;
if (0 == strcasecmp(str, "crit"))
return LSQ_LOG_CRIT;
if (0 == strcasecmp(str, "error"))
return LSQ_LOG_ERROR;
if (0 == strcasecmp(str, "warn"))
return LSQ_LOG_WARN;
if (0 == strcasecmp(str, "notice"))
return LSQ_LOG_NOTICE;
if (0 == strcasecmp(str, "info"))
return LSQ_LOG_INFO;
if (0 == strcasecmp(str, "debug"))
return LSQ_LOG_DEBUG;
return -1;
}
void
lsquic_log_to_fstream (FILE *file, unsigned llts)
{
lsquic_logger_init(&file_logger_if, file, llts);
}
int
lsquic_logger_lopt (const char *optarg_orig)
{
char *const optarg = strdup(optarg_orig);
char *mod_str;
int i;
for (i = 0; (mod_str = strtok(i ? NULL : optarg, ",")); ++i) {
char *level_str = strchr(mod_str, '=');
if (!level_str) {
fprintf(stderr, "Invalid module specification `%s'\n", mod_str);
break;
}
*level_str = '\0';
++level_str;
enum lsquic_logger_module mod = lsquic_str_to_logger_module(mod_str);
if (-1 == (int) mod) {
fprintf(stderr, "`%s' is not a valid module name\n", mod_str);
break;
}
enum lsq_log_level level = lsquic_str_to_log_level(level_str);
if (-1 == (int) level) {
fprintf(stderr, "`%s' is not a valid level\n", level_str);
break;
}
lsq_log_levels[mod] = level;
LSQ_INFO("set %s to %s", mod_str, level_str);
}
free(optarg);
return mod_str == NULL ? 0 : -1;
}
int
lsquic_set_log_level (const char *level_str)
{
enum lsq_log_level level;
unsigned i;
level = lsquic_str_to_log_level(level_str);
if ((int) level >= 0)
{
for (i = 0; i < sizeof(lsq_log_levels) / sizeof(lsq_log_levels[0]); ++i)
lsq_log_levels[i] = level;
return 0;
}
else
return -1;
}

View file

@ -0,0 +1,201 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_logger.h -- logging functions and macros.
*
* Usage (this assumes MY_MODULE) is a part of enum lsquic_logger_module):
* #define LSQUIC_LOGGER_MODULE MY_MODULE
* #include "lsquic_logger.h"
* LSQ_INFO("info message");
*
* If you want log messages from your module to contain connection ID, #define
* LSQUIC_LOG_CONN_ID so that it evaluates to connection ID. If, in addition,
* you want stream ID to be logged, #define LSQUIC_LOG_STREAM_ID similarly.
* See existing code for examples.
*
* To add a module:
* 1. Add entry to enum lsquic_logger_module.
* 2. Update lsqlm_to_str.
* 3. Update lsq_log_levels.
*/
#ifndef LSQUIC_LOGGER_H
#define LSQUIC_LOGGER_H
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef LSQUIC_LOWEST_LOG_LEVEL
# define LSQUIC_LOWEST_LOG_LEVEL LSQ_LOG_DEBUG
#endif
/* Same levels as in sys/syslog.h: */
enum lsq_log_level {
LSQ_LOG_EMERG,
LSQ_LOG_ALERT,
LSQ_LOG_CRIT,
LSQ_LOG_ERROR,
LSQ_LOG_WARN,
LSQ_LOG_NOTICE,
LSQ_LOG_INFO,
LSQ_LOG_DEBUG,
N_LSQUIC_LOG_LEVELS
};
enum lsquic_logger_module {
LSQLM_NOMODULE,
LSQLM_LOGGER,
LSQLM_EVENT,
LSQLM_ENGINE,
LSQLM_CONN,
LSQLM_RECHIST,
LSQLM_STREAM,
LSQLM_PARSE,
LSQLM_CFCW,
LSQLM_SFCW,
LSQLM_SENDCTL,
LSQLM_ALARMSET,
LSQLM_CRYPTO,
LSQLM_HANDSHAKE,
LSQLM_HSK_ADAPTER,
LSQLM_CUBIC,
LSQLM_HEADERS,
LSQLM_FRAME_WRITER,
LSQLM_FRAME_READER,
LSQLM_CONN_HASH,
LSQLM_ENG_HIST,
LSQLM_SPI,
LSQLM_DI,
LSQLM_PACER,
N_LSQUIC_LOGGER_MODULES
};
/* Each module has its own log level.
*/
extern enum lsq_log_level lsq_log_levels[N_LSQUIC_LOGGER_MODULES];
extern const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES];
extern const char *const lsq_loglevel2str[N_LSQUIC_LOG_LEVELS];
#define LSQ_LOG_ENABLED_EXT(level, module) ( \
level <= LSQUIC_LOWEST_LOG_LEVEL && level <= lsq_log_levels[module])
#define LSQ_LOG_ENABLED(level) LSQ_LOG_ENABLED_EXT(level, LSQUIC_LOGGER_MODULE)
/* The functions that perform actual logging are void. This is an
* optimization. In majority of cases the calls will succeed; even if
* they fail, there is nothing (at least, nothing simple) to be done to
* handle logging failure.
*/
/* There are four levels of log functions, depending on whether they take
* the following arguments:
* 1. Logger module
* 2. Connection ID
* 3. Stream ID
*
* Each level of logging function supports one additional argument, as seen
* below. LSQ_LOG is set to one of LSQ_LOG0, LSQ_LOG1, LSQ_LOG2, or LSQ_LOG3.
* You can still use LSQ_LOG{0..3} directly.
*/
void
lsquic_logger_log3 (enum lsq_log_level, enum lsquic_logger_module,
uint64_t conn_id, uint32_t stream_id,
const char *format, ...)
#if __GNUC__
__attribute__((format(printf, 5, 6)))
#endif
;
# define LSQ_LOG3(level, ...) do { \
if (LSQ_LOG_ENABLED(level)) \
lsquic_logger_log3(level, LSQUIC_LOGGER_MODULE, \
LSQUIC_LOG_CONN_ID, LSQUIC_LOG_STREAM_ID, __VA_ARGS__); \
} while (0)
void
lsquic_logger_log2 (enum lsq_log_level, enum lsquic_logger_module,
uint64_t conn_id, const char *format, ...)
#if __GNUC__
__attribute__((format(printf, 4, 5)))
#endif
;
# define LSQ_LOG2(level, ...) do { \
if (LSQ_LOG_ENABLED(level)) \
lsquic_logger_log2(level, LSQUIC_LOGGER_MODULE, \
LSQUIC_LOG_CONN_ID, __VA_ARGS__); \
} while (0)
void
lsquic_logger_log1 (enum lsq_log_level, enum lsquic_logger_module,
const char *format, ...)
#if __GNUC__
__attribute__((format(printf, 3, 4)))
#endif
;
# define LSQ_LOG1(level, ...) do { \
if (LSQ_LOG_ENABLED(level)) \
lsquic_logger_log1(level, LSQUIC_LOGGER_MODULE, __VA_ARGS__); \
} while (0)
void
lsquic_logger_log0 (enum lsq_log_level, const char *format, ...)
#if __GNUC__
__attribute__((format(printf, 2, 3)))
#endif
;
# define LSQ_LOG0(level, ...) do { \
if (LSQ_LOG_ENABLED(level)) \
lsquic_logger_log0(level, __VA_ARGS__); \
} while (0)
#if defined(LSQUIC_LOGGER_MODULE)
#if defined(LSQUIC_LOG_CONN_ID)
#if defined(LSQUIC_LOG_STREAM_ID)
# define LSQ_LOG LSQ_LOG3
#else
# define LSQ_LOG LSQ_LOG2
#endif
#else
# define LSQ_LOG LSQ_LOG1
#endif
#else
# define LSQ_LOG LSQ_LOG0
# define LSQUIC_LOGGER_MODULE LSQLM_NOMODULE
#endif
#define LSQ_DEBUG(...) LSQ_LOG(LSQ_LOG_DEBUG, __VA_ARGS__)
#define LSQ_WARN(...) LSQ_LOG(LSQ_LOG_WARN, __VA_ARGS__)
#define LSQ_ALERT(...) LSQ_LOG(LSQ_LOG_ALERT, __VA_ARGS__)
#define LSQ_CRIT(...) LSQ_LOG(LSQ_LOG_CRIT, __VA_ARGS__)
#define LSQ_ERROR(...) LSQ_LOG(LSQ_LOG_ERROR, __VA_ARGS__)
#define LSQ_NOTICE(...) LSQ_LOG(LSQ_LOG_NOTICE, __VA_ARGS__)
#define LSQ_INFO(...) LSQ_LOG(LSQ_LOG_INFO, __VA_ARGS__)
#define LSQ_EMERG(...) LSQ_LOG(LSQ_LOG_EMERG, __VA_ARGS__)
/* Shorthand for printing to file streams using internal lsquic_logger_if
*/
void
lsquic_log_to_fstream (FILE *, unsigned llts);
enum lsquic_logger_module
lsquic_str_to_logger_module (const char *);
enum lsq_log_level
lsquic_str_to_log_level (const char *);
/* Parse and set log levels passed via -l flag. If an error is encountered,
* an error message is printed to stderr and negative value is returned.
*/
int
lsquic_logger_lopt (const char *optarg);
#ifdef __cplusplus
}
#endif
#endif

289
src/liblsquic/lsquic_malo.c Normal file
View file

@ -0,0 +1,289 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_malo.c -- malo allocator implementation.
*
* The malo allocator is a pool of objects of fixed size. It tries to
* allocate and deallocate objects as fast as possible. To do so, it
* does the following:
*
* 1. Allocations occur 4 KB at a time.
* 2. No division or multiplication operations are performed.
*
* (In recent testing, malo was about 2.7 times faster than malloc for
* 64-byte objects.)
*
* Besides speed, two other important characteristics distinguish it
* from other pool allocators:
*
* 1. To free (put) an object, one does not need a pointer to the malo
* object. This makes this allocator easy to use.
* 2. A built-in iterator is provided to iterate over all allocated
* objects (with ability to safely release objects while iterator
* is active). This may be useful in some circumstances.
*
* To gain all these advantages, there are trade-offs:
*
* 1. There are two memory penalties:
* a. Per object overhead. To avoid division and multiplication,
* the object sizes is rounded up to the nearest power or two,
* starting with 64 bytes (minumum) and up to 2 KB (maximum).
* Thus, a 104-byte object will have a 24-byte overhead; a
* 130-byte object will have 126-byte overhead. This is
* something to keep in mind.
* b. Per page overhead. Page links occupy some bytes in the
* page. To keep things fast, at least one slot per page is
* always occupied, independent of object size. Thus, for a
* 1 KB object size, 25% of the page is used for the page
* header.
* 2. 4 KB pages are not freed until the malo allocator is destroyed.
* This is something to keep in mind.
*
* P.S. In Russian, "malo" (мало) means "little" or "few". Thus, the
* malo allocator aims to perform its job in as few CPU cycles as
* possible.
*/
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/queue.h>
#include "fiu-local.h"
#include "lsquic_malo.h"
/* 64 slots in a 4KB page means that the smallest object is 64 bytes.
* The largest object is 2KB.
*/
#define MALO_MIN_NBITS 6
#define MALO_MAX_NBITS 11
/* A "free page" is a page with free slots available.
*/
static unsigned find_free_slot (uint64_t slots);
static unsigned size_in_bits (size_t sz);
struct malo_page {
SLIST_ENTRY(malo_page) next_page;
LIST_ENTRY(malo_page) next_free_page;
struct malo *malo;
uint64_t slots,
full_slot_mask;
unsigned nbits;
unsigned initial_slot;
};
typedef char malo_header_fits_in_one_slot
[0 - (sizeof(struct malo_page) > (1 << MALO_MAX_NBITS))];
struct malo {
struct malo_page page_header;
SLIST_HEAD(, malo_page) all_pages;
LIST_HEAD(, malo_page) free_pages;
struct {
struct malo_page *cur_page;
unsigned next_slot;
} iter;
};
struct malo *
lsquic_malo_create (size_t obj_size)
{
unsigned nbits = size_in_bits(obj_size);
if (nbits < MALO_MIN_NBITS)
nbits = MALO_MIN_NBITS;
else if (nbits > MALO_MAX_NBITS)
{
errno = EOVERFLOW;
return NULL;
}
struct malo *malo;
if (0 != posix_memalign((void **) &malo, 0x1000, 0x1000))
return NULL;
SLIST_INIT(&malo->all_pages);
LIST_INIT(&malo->free_pages);
malo->iter.cur_page = &malo->page_header;
malo->iter.next_slot = 0;
int n_slots = sizeof(*malo) / (1 << nbits)
+ ((sizeof(*malo) % (1 << nbits)) > 0);
struct malo_page *const page = &malo->page_header;
SLIST_INSERT_HEAD(&malo->all_pages, page, next_page);
LIST_INSERT_HEAD(&malo->free_pages, page, next_free_page);
page->malo = malo;
if (nbits == MALO_MIN_NBITS)
page->full_slot_mask = ~0ULL;
else
page->full_slot_mask = (1ULL << (1 << (12 - nbits))) - 1;
page->slots = (1ULL << n_slots) - 1;
page->nbits = nbits;
page->initial_slot = n_slots;
return malo;
}
static struct malo_page *
allocate_page (struct malo *malo)
{
struct malo_page *page;
if (0 != posix_memalign((void **) &page, 0x1000, 0x1000))
return NULL;
SLIST_INSERT_HEAD(&malo->all_pages, page, next_page);
LIST_INSERT_HEAD(&malo->free_pages, page, next_free_page);
page->slots = 1;
page->full_slot_mask = malo->page_header.full_slot_mask;
page->nbits = malo->page_header.nbits;
page->malo = malo;
page->initial_slot = 1;
return page;
}
#define FAIL_NOMEM do { errno = ENOMEM; return NULL; } while (0)
/* Get a new object. */
void *
lsquic_malo_get (struct malo *malo)
{
fiu_do_on("malo/get", FAIL_NOMEM);
struct malo_page *page = LIST_FIRST(&malo->free_pages);
if (!page)
{
page = allocate_page(malo);
if (!page)
return NULL;
}
unsigned slot = find_free_slot(page->slots);
page->slots |= (1ULL << slot);
if (page->full_slot_mask == page->slots)
LIST_REMOVE(page, next_free_page);
return (char *) page + (slot << page->nbits);
}
/* Return obj to the pool */
void
lsquic_malo_put (void *obj)
{
uintptr_t page_addr = (uintptr_t) obj & ~((1 << 12) - 1);
struct malo_page *page = (void *) page_addr;
unsigned slot = ((uintptr_t) obj - page_addr) >> page->nbits;
if (page->full_slot_mask == page->slots)
LIST_INSERT_HEAD(&page->malo->free_pages, page, next_free_page);
page->slots &= ~(1ULL << slot);
}
void
lsquic_malo_destroy (struct malo *malo)
{
struct malo_page *page, *next;
page = SLIST_FIRST(&malo->all_pages);
while (page != &malo->page_header)
{
next = SLIST_NEXT(page, next_page);
free(page);
page = next;
}
free(page);
}
/* The iterator is built-in. Usage:
* void *obj;
* for (obj = lsquic_malo_first(malo); obj; lsquic_malo_next(malo))
* do_stuff(obj);
*/
void *
lsquic_malo_first (struct malo *malo)
{
malo->iter.cur_page = SLIST_FIRST(&malo->all_pages);
malo->iter.next_slot = malo->iter.cur_page->initial_slot;
return lsquic_malo_next(malo);
}
void *
lsquic_malo_next (struct malo *malo)
{
struct malo_page *page;
unsigned max_slot, slot;
page = malo->iter.cur_page;
if (page)
{
max_slot = 1 << (12 - page->nbits); /* Same for all pages */
slot = malo->iter.next_slot;
while (1)
{
for (; slot < max_slot; ++slot)
{
if (page->slots & (1ULL << slot))
{
malo->iter.cur_page = page;
malo->iter.next_slot = slot + 1;
return (char *) page + (slot << page->nbits);
}
}
page = SLIST_NEXT(page, next_page);
if (page)
slot = page->initial_slot;
else
{
malo->iter.cur_page = NULL; /* Stop iterator */
return NULL;
}
}
}
return NULL;
}
static unsigned
size_in_bits (size_t sz)
{
#if __GNUC__
unsigned clz = __builtin_clz(sz - 1);
return 32 - clz;
#else
unsigned clz;
size_t y;
--sz;
clz = 32;
y = sz >> 16; if (y) { clz -= 16; sz = y; }
y = sz >> 8; if (y) { clz -= 8; sz = y; }
y = sz >> 4; if (y) { clz -= 4; sz = y; }
y = sz >> 2; if (y) { clz -= 2; sz = y; }
y = sz >> 1; if (y) return 32 - clz + 1;
return 32 - clz + sz;
#endif
}
static unsigned
find_free_slot (uint64_t slots)
{
#if __GNUC__
return __builtin_ffsll(~slots) - 1;
#else
unsigned n;
slots =~ slots;
n = 0;
if (0 == (slots & ((1ULL << 32) - 1))) { n += 32; slots >>= 32; }
if (0 == (slots & ((1ULL << 16) - 1))) { n += 16; slots >>= 16; }
if (0 == (slots & ((1ULL << 8) - 1))) { n += 8; slots >>= 8; }
if (0 == (slots & ((1ULL << 4) - 1))) { n += 4; slots >>= 4; }
if (0 == (slots & ((1ULL << 2) - 1))) { n += 2; slots >>= 2; }
if (0 == (slots & ((1ULL << 1) - 1))) { n += 1; slots >>= 1; }
return n;
#endif
}

View file

@ -0,0 +1,38 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_malo.h -- Fast allocator for fixed-sized objects.
*/
#ifndef LSQUIC_MALO_H
#define LSQUIC_MALO_H 1
struct malo;
/* Create a malo allocator for objects of size `obj_size'. */
struct malo *
lsquic_malo_create (size_t obj_size);
/* Get a new object. */
void *
lsquic_malo_get (struct malo *);
/* Return obj to the pool */
void
lsquic_malo_put (void *obj);
/* This deallocates all remaining objects. */
void
lsquic_malo_destroy (struct malo *);
/* The iterator is built-in. Usage:
* void *obj;
* for (obj = lsquic_malo_first(obj); obj; lsquic_malo_next(obj))
* do_stuff(obj);
*/
void *
lsquic_malo_first (struct malo *);
void *
lsquic_malo_next (struct malo *);
#endif

293
src/liblsquic/lsquic_mm.c Normal file
View file

@ -0,0 +1,293 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_mm.c -- Memory manager.
*/
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "fiu-local.h"
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_malo.h"
#include "lsquic_conn.h"
#include "lsquic_rtt.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
#include "lsquic_parse.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#define FAIL_NOMEM do { errno = ENOMEM; return NULL; } while (0)
struct payload_buf
{
SLIST_ENTRY(payload_buf) next_pb;
};
struct packet_out_buf
{
SLIST_ENTRY(packet_out_buf) next_pob;
};
struct four_k_page
{
SLIST_ENTRY(four_k_page) next_fkp;
};
struct sixteen_k_page
{
SLIST_ENTRY(sixteen_k_page) next_skp;
};
int
lsquic_mm_init (struct lsquic_mm *mm)
{
int i;
mm->acki = malloc(sizeof(*mm->acki));
mm->malo.stream_frame = lsquic_malo_create(sizeof(struct stream_frame));
mm->malo.stream_rec_arr = lsquic_malo_create(sizeof(struct stream_rec_arr));
mm->malo.packet_in = lsquic_malo_create(sizeof(struct lsquic_packet_in));
mm->malo.packet_out = lsquic_malo_create(sizeof(struct lsquic_packet_out));
TAILQ_INIT(&mm->free_packets_in);
for (i = 0; i < MM_N_OUT_BUCKETS; ++i)
SLIST_INIT(&mm->packet_out_bufs[i]);
SLIST_INIT(&mm->payload_bufs);
SLIST_INIT(&mm->four_k_pages);
SLIST_INIT(&mm->sixteen_k_pages);
if (mm->acki && mm->malo.stream_frame && mm->malo.stream_rec_arr &&
mm->malo.packet_in)
{
return 0;
}
else
return -1;
}
void
lsquic_mm_cleanup (struct lsquic_mm *mm)
{
int i;
struct packet_out_buf *pob;
struct payload_buf *pb;
struct four_k_page *fkp;
struct sixteen_k_page *skp;
free(mm->acki);
lsquic_malo_destroy(mm->malo.packet_in);
lsquic_malo_destroy(mm->malo.packet_out);
lsquic_malo_destroy(mm->malo.stream_frame);
lsquic_malo_destroy(mm->malo.stream_rec_arr);
for (i = 0; i < MM_N_OUT_BUCKETS; ++i)
while ((pob = SLIST_FIRST(&mm->packet_out_bufs[i])))
{
SLIST_REMOVE_HEAD(&mm->packet_out_bufs[i], next_pob);
free(pob);
}
while ((pb = SLIST_FIRST(&mm->payload_bufs)))
{
SLIST_REMOVE_HEAD(&mm->payload_bufs, next_pb);
free(pb);
}
while ((fkp = SLIST_FIRST(&mm->four_k_pages)))
{
SLIST_REMOVE_HEAD(&mm->four_k_pages, next_fkp);
free(fkp);
}
while ((skp = SLIST_FIRST(&mm->sixteen_k_pages)))
{
SLIST_REMOVE_HEAD(&mm->sixteen_k_pages, next_skp);
free(skp);
}
}
struct lsquic_packet_in *
lsquic_mm_get_packet_in (struct lsquic_mm *mm)
{
struct lsquic_packet_in *packet_in;
fiu_do_on("mm/packet_in", FAIL_NOMEM);
packet_in = TAILQ_FIRST(&mm->free_packets_in);
if (packet_in)
{
assert(0 == packet_in->pi_refcnt);
TAILQ_REMOVE(&mm->free_packets_in, packet_in, pi_next);
}
else
packet_in = lsquic_malo_get(mm->malo.packet_in);
if (packet_in)
memset(packet_in, 0, sizeof(*packet_in));
return packet_in;
}
/* Based on commonly used MTUs, ordered from small to large: */
enum {
PACKET_OUT_PAYLOAD_0 = 1280 - QUIC_MIN_PACKET_OVERHEAD,
PACKET_OUT_PAYLOAD_1 = QUIC_MAX_IPv6_PACKET_SZ - QUIC_MIN_PACKET_OVERHEAD,
PACKET_OUT_PAYLOAD_2 = QUIC_MAX_IPv4_PACKET_SZ - QUIC_MIN_PACKET_OVERHEAD,
};
static const unsigned packet_out_sizes[] = {
PACKET_OUT_PAYLOAD_0,
PACKET_OUT_PAYLOAD_1,
PACKET_OUT_PAYLOAD_2,
};
static unsigned
packet_out_index (unsigned size)
{
unsigned idx = (size > PACKET_OUT_PAYLOAD_0)
+ (size > PACKET_OUT_PAYLOAD_1);
return idx;
}
void
lsquic_mm_put_packet_out (struct lsquic_mm *mm,
struct lsquic_packet_out *packet_out)
{
struct packet_out_buf *pob;
unsigned idx;
assert(packet_out->po_data);
pob = (struct packet_out_buf *) packet_out->po_data;
idx = packet_out_index(packet_out->po_n_alloc);
SLIST_INSERT_HEAD(&mm->packet_out_bufs[idx], pob, next_pob);
lsquic_malo_put(packet_out);
}
struct lsquic_packet_out *
lsquic_mm_get_packet_out (struct lsquic_mm *mm, struct malo *malo,
unsigned short size)
{
struct lsquic_packet_out *packet_out;
struct packet_out_buf *pob;
unsigned idx;
assert(size <= QUIC_MAX_PAYLOAD_SZ);
fiu_do_on("mm/packet_out", FAIL_NOMEM);
packet_out = lsquic_malo_get(malo ? malo : mm->malo.packet_out);
if (!packet_out)
return NULL;
idx = packet_out_index(size);
pob = SLIST_FIRST(&mm->packet_out_bufs[idx]);
if (pob)
SLIST_REMOVE_HEAD(&mm->packet_out_bufs[idx], next_pob);
else
{
pob = malloc(packet_out_sizes[idx]);
if (!pob)
{
lsquic_malo_put(packet_out);
return NULL;
}
}
memset(packet_out, 0, sizeof(*packet_out));
STAILQ_INIT(&packet_out->po_srec_arrs);
packet_out->po_n_alloc = size;
packet_out->po_data = (unsigned char *) pob;
return packet_out;
}
void *
lsquic_mm_get_1370 (struct lsquic_mm *mm)
{
struct payload_buf *pb = SLIST_FIRST(&mm->payload_bufs);
fiu_do_on("mm/1370", FAIL_NOMEM);
if (pb)
SLIST_REMOVE_HEAD(&mm->payload_bufs, next_pb);
else
pb = malloc(1370);
return pb;
}
void
lsquic_mm_put_1370 (struct lsquic_mm *mm, void *mem)
{
struct payload_buf *pb = mem;
SLIST_INSERT_HEAD(&mm->payload_bufs, pb, next_pb);
}
void *
lsquic_mm_get_4k (struct lsquic_mm *mm)
{
struct four_k_page *fkp = SLIST_FIRST(&mm->four_k_pages);
fiu_do_on("mm/4k", FAIL_NOMEM);
if (fkp)
SLIST_REMOVE_HEAD(&mm->four_k_pages, next_fkp);
else
fkp = malloc(0x1000);
return fkp;
}
void
lsquic_mm_put_4k (struct lsquic_mm *mm, void *mem)
{
struct four_k_page *fkp = mem;
SLIST_INSERT_HEAD(&mm->four_k_pages, fkp, next_fkp);
}
void *
lsquic_mm_get_16k (struct lsquic_mm *mm)
{
struct sixteen_k_page *skp = SLIST_FIRST(&mm->sixteen_k_pages);
fiu_do_on("mm/16k", FAIL_NOMEM);
if (skp)
SLIST_REMOVE_HEAD(&mm->sixteen_k_pages, next_skp);
else
skp = malloc(16 * 1024);
return skp;
}
void
lsquic_mm_put_16k (struct lsquic_mm *mm, void *mem)
{
struct sixteen_k_page *skp = mem;
SLIST_INSERT_HEAD(&mm->sixteen_k_pages, skp, next_skp);
}
void
lsquic_mm_put_packet_in (struct lsquic_mm *mm,
struct lsquic_packet_in *packet_in)
{
assert(0 == packet_in->pi_refcnt);
if (packet_in->pi_flags & PI_OWN_DATA)
lsquic_mm_put_1370(mm, packet_in->pi_data);
TAILQ_INSERT_HEAD(&mm->free_packets_in, packet_in, pi_next);
}

78
src/liblsquic/lsquic_mm.h Normal file
View file

@ -0,0 +1,78 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_mm.h -- Memory manager.
*
* Allocators and in this class are meant to be used for the lifetime of
* QUIC engine.
*/
#ifndef LSQUIC_MM_H
#define LSQUIC_MM_H 1
struct lsquic_engine_public;
struct lsquic_packet_in;
struct lsquic_packet_out;
struct ack_info;
struct malo;
#define MM_N_OUT_BUCKETS 3
struct lsquic_mm {
struct ack_info *acki;
struct {
struct malo *stream_frame; /* For struct stream_frame */
struct malo *stream_rec_arr;/* For struct stream_rec_arr */
struct malo *packet_in; /* For struct lsquic_packet_in */
struct malo *packet_out; /* For struct lsquic_packet_out */
} malo;
TAILQ_HEAD(, lsquic_packet_in) free_packets_in;
SLIST_HEAD(, packet_out_buf) packet_out_bufs[MM_N_OUT_BUCKETS];
SLIST_HEAD(, payload_buf) payload_bufs;
SLIST_HEAD(, four_k_page) four_k_pages;
SLIST_HEAD(, sixteen_k_page) sixteen_k_pages;
};
int
lsquic_mm_init (struct lsquic_mm *);
void
lsquic_mm_cleanup (struct lsquic_mm *);
struct lsquic_packet_in *
lsquic_mm_get_packet_in (struct lsquic_mm *);
void
lsquic_mm_put_packet_in (struct lsquic_mm *, struct lsquic_packet_in *);
#define lsquic_packet_in_put(mm, p) do { \
assert((p)->pi_refcnt != 0); \
if (--(p)->pi_refcnt == 0) \
lsquic_mm_put_packet_in(mm, p); \
} while (0)
struct lsquic_packet_out *
lsquic_mm_get_packet_out (struct lsquic_mm *, struct malo *,
unsigned short size);
void
lsquic_mm_put_packet_out (struct lsquic_mm *, struct lsquic_packet_out *);
void *
lsquic_mm_get_1370 (struct lsquic_mm *);
void
lsquic_mm_put_1370 (struct lsquic_mm *, void *);
void *
lsquic_mm_get_4k (struct lsquic_mm *);
void
lsquic_mm_put_4k (struct lsquic_mm *, void *);
void *
lsquic_mm_get_16k (struct lsquic_mm *);
void
lsquic_mm_put_16k (struct lsquic_mm *, void *);
#endif

View file

@ -0,0 +1,180 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
#ifndef NDEBUG
#include <stdlib.h> /* getenv */
#endif
#include <string.h>
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_pacer.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_out.h"
#include "lsquic_util.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PACER
#define LSQUIC_LOG_CONN_ID pacer->pa_cid
#include "lsquic_logger.h"
#ifndef MAX
# define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
void
pacer_init (struct pacer *pacer, lsquic_cid_t cid, unsigned max_intertick)
{
memset(pacer, 0, sizeof(*pacer));
pacer->pa_burst_tokens = 10;
pacer->pa_cid = cid;
pacer->pa_max_intertick = max_intertick;
#ifndef NDEBUG
const char *val;
if ((val = getenv("LSQUIC_PACER_INTERTICK")))
{
pacer->pa_flags |= PA_CONSTANT_INTERTICK;
pacer->pa_intertick_avg = atoi(val);
}
#endif
}
void
pacer_packet_scheduled (struct pacer *pacer, unsigned n_in_flight,
int in_recovery, tx_time_f tx_time, void *tx_ctx)
{
lsquic_time_t delay, sched_time;
int app_limited, making_up;
if (n_in_flight == 0 && !in_recovery)
{
pacer->pa_burst_tokens = 10;
LSQ_DEBUG("%s: replenish tokens: %u", __func__, pacer->pa_burst_tokens);
}
if (pacer->pa_burst_tokens > 0)
{
--pacer->pa_burst_tokens;
pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
pacer->pa_next_sched = 0;
pacer->pa_last_delayed = 0;
LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens);
return;
}
sched_time = pacer->pa_now;
delay = tx_time(tx_ctx);
if (pacer->pa_flags & PA_LAST_SCHED_DELAYED)
{
pacer->pa_next_sched += delay;
app_limited = pacer->pa_last_delayed != 0
&& pacer->pa_last_delayed + delay <= sched_time;
making_up = pacer->pa_next_sched <= sched_time;
LSQ_DEBUG("making up: %d; app limited; %d", making_up, app_limited);
if (making_up && !app_limited)
pacer->pa_last_delayed = sched_time;
else
{
pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
pacer->pa_last_delayed = 0;
}
}
else
pacer->pa_next_sched = MAX(pacer->pa_next_sched + delay,
sched_time + delay);
LSQ_DEBUG("next_sched is set to %"PRIu64" usec from now",
pacer->pa_next_sched - lsquic_time_now());
}
void
pacer_loss_event (struct pacer *pacer)
{
pacer->pa_burst_tokens = 0;
LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens);
}
static unsigned
clock_granularity (const struct pacer *pacer)
{
#ifndef NDEBUG
if (pacer->pa_flags & PA_CONSTANT_INTERTICK)
return pacer->pa_max_intertick;
#endif
return pacer->pa_intertick_var;
}
int
pacer_can_schedule (struct pacer *pacer, unsigned n_in_flight)
{
int can;
if (pacer->pa_burst_tokens > 0 || n_in_flight == 0)
can = 1;
else if (pacer->pa_next_sched > pacer->pa_now + clock_granularity(pacer))
{
pacer->pa_flags |= PA_LAST_SCHED_DELAYED;
can = 0;
}
else
can = 1;
LSQ_DEBUG("%s: %d", __func__, can);
return can;
}
#define ALPHA_SHIFT 3
#define BETA_SHIFT 2
static void
update_avg_intertick (struct pacer *pacer, unsigned intertick)
{
unsigned diff;
#ifndef NDEBUG
if (pacer->pa_flags & PA_CONSTANT_INTERTICK)
return;
#endif
if (pacer->pa_intertick_avg)
{
if (intertick > pacer->pa_intertick_avg)
diff = intertick - pacer->pa_intertick_avg;
else
diff = pacer->pa_intertick_avg - intertick;
pacer->pa_intertick_var -= pacer->pa_intertick_var >> BETA_SHIFT;
pacer->pa_intertick_var += diff >> BETA_SHIFT;
pacer->pa_intertick_avg -= pacer->pa_intertick_avg >> ALPHA_SHIFT;
pacer->pa_intertick_avg += intertick >> ALPHA_SHIFT;
}
else
{
pacer->pa_intertick_avg = intertick;
pacer->pa_intertick_var = intertick >> 1;
}
}
void
pacer_tick (struct pacer *pacer, lsquic_time_t now)
{
unsigned intertick;
assert(now > pacer->pa_now);
if (pacer->pa_now)
{
assert(now - pacer->pa_now < (1ULL << sizeof(unsigned) * 8));
intertick = now - pacer->pa_now;
LSQ_DEBUG("intertick estimate: %u; real value: %u; error: %d",
clock_granularity(pacer), intertick,
(int) clock_granularity(pacer) - (int) intertick);
update_avg_intertick(pacer, intertick);
}
pacer->pa_now = now;
}

View file

@ -0,0 +1,54 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_PACER_H
#define LSQUIC_PACER_H 1
struct pacer
{
lsquic_cid_t pa_cid; /* Used for logging */
lsquic_time_t pa_next_sched;
lsquic_time_t pa_last_delayed;
lsquic_time_t pa_now;
/* All tick times are in microseconds */
unsigned pa_max_intertick; /* Maximum intertick time */
/* We keep an average of intertick times, which is our best estimate
* for the time when the connection ticks next. This estimate is used
* to see whether a packet can be scheduled or not.
*/
unsigned pa_intertick_avg; /* Smoothed average */
unsigned pa_intertick_var; /* Variance */
unsigned short pa_packet_size;
unsigned char pa_burst_tokens;
enum {
PA_LAST_SCHED_DELAYED = (1 << 0),
#ifndef NDEBUG
PA_CONSTANT_INTERTICK = (1 << 1), /* Use fake intertick time for testing */
#endif
} pa_flags:8;
};
typedef lsquic_time_t (*tx_time_f)(void *ctx);
void
pacer_init (struct pacer *, lsquic_cid_t, unsigned max_intertick);
void
pacer_tick (struct pacer *, lsquic_time_t);
int
pacer_can_schedule (struct pacer *, unsigned n_in_flight);
void
pacer_packet_scheduled (struct pacer *pacer, unsigned n_in_flight,
int in_recovery, tx_time_f tx_time, void *tx_ctx);
void
pacer_loss_event (struct pacer *);
#define pacer_next_sched(pacer) (+(pacer)->pa_next_sched)
#endif

View file

@ -0,0 +1,123 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_packet_common.c -- some common packet-related routines
*/
#include <stdio.h>
#include <stdlib.h>
#include "lsquic_logger.h"
#include "lsquic_packet_common.h"
static const char * const frame_type_2_str[N_QUIC_FRAMES] = {
[QUIC_FRAME_INVALID] = "QUIC_FRAME_INVALID",
[QUIC_FRAME_STREAM] = "QUIC_FRAME_STREAM",
[QUIC_FRAME_ACK] = "QUIC_FRAME_ACK",
[QUIC_FRAME_PADDING] = "QUIC_FRAME_PADDING",
[QUIC_FRAME_RST_STREAM] = "QUIC_FRAME_RST_STREAM",
[QUIC_FRAME_CONNECTION_CLOSE] = "QUIC_FRAME_CONNECTION_CLOSE",
[QUIC_FRAME_GOAWAY] = "QUIC_FRAME_GOAWAY",
[QUIC_FRAME_WINDOW_UPDATE] = "QUIC_FRAME_WINDOW_UPDATE",
[QUIC_FRAME_BLOCKED] = "QUIC_FRAME_BLOCKED",
[QUIC_FRAME_STOP_WAITING] = "QUIC_FRAME_STOP_WAITING",
[QUIC_FRAME_PING] = "QUIC_FRAME_PING",
};
#define SLEN(x) (sizeof(#x) - sizeof("QUIC_FRAME_"))
const size_t lsquic_frame_types_str_sz =
/* We don't need to include INVALID frame in this list because it is
* never a part of any frame list bitmask (e.g. po_frame_types).
*/
SLEN(QUIC_FRAME_STREAM) + 1 +
SLEN(QUIC_FRAME_ACK) + 1 +
SLEN(QUIC_FRAME_PADDING) + 1 +
SLEN(QUIC_FRAME_RST_STREAM) + 1 +
SLEN(QUIC_FRAME_CONNECTION_CLOSE) + 1 +
SLEN(QUIC_FRAME_GOAWAY) + 1 +
SLEN(QUIC_FRAME_WINDOW_UPDATE) + 1 +
SLEN(QUIC_FRAME_BLOCKED) + 1 +
SLEN(QUIC_FRAME_STOP_WAITING) + 1 +
SLEN(QUIC_FRAME_PING) + 1;
const char *
lsquic_frame_types_to_str (char *buf, size_t bufsz, short frame_types)
{
char *p;
int i, w;
size_t sz;
if (bufsz > 0)
buf[0] = '\0';
p = buf;
for (i = 0; i < N_QUIC_FRAMES; ++i)
{
if (frame_types & (1 << i))
{
sz = bufsz - (p - buf);
w = snprintf(p, sz, "%.*s%s", p > buf, " ",
frame_type_2_str[i] + sizeof("QUIC_FRAME_") - 1);
if (w > (int) sz)
{
LSQ_WARN("not enough room for all frame types");
break;
}
p += w;
}
frame_types &= ~(1 << i);
}
return buf;
}
enum lsquic_packno_bits
calc_packno_bits (lsquic_packno_t packno, lsquic_packno_t least_unacked,
uint64_t n_in_flight)
{
uint64_t delta;
unsigned bits;
delta = packno - least_unacked;
if (n_in_flight > delta)
delta = n_in_flight;
delta *= 4;
bits = (delta > (1ULL << 8))
+ (delta > (1ULL << 16))
+ (delta > (1ULL << 32));
return bits;
}
lsquic_packno_t
restore_packno (lsquic_packno_t cur_packno,
enum lsquic_packno_bits cur_packno_bits,
lsquic_packno_t max_packno)
{
lsquic_packno_t candidates[3], epoch_delta;
int64_t diffs[3];
unsigned min, len;
len = packno_bits2len(cur_packno_bits);
epoch_delta = 1ULL << (len << 3);
candidates[1] = (max_packno & ~(epoch_delta - 1)) + cur_packno;
candidates[0] = candidates[1] - epoch_delta;
candidates[2] = candidates[1] + epoch_delta;
diffs[0] = llabs((int64_t) candidates[0] - (int64_t) max_packno);
diffs[1] = llabs((int64_t) candidates[1] - (int64_t) max_packno);
diffs[2] = llabs((int64_t) candidates[2] - (int64_t) max_packno);
min = diffs[1] < diffs[0];
if (diffs[2] < diffs[min])
min = 2;
return candidates[min];
}

View file

@ -0,0 +1,118 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_PACKET_COMMON_H
#define LSQUIC_PACKET_COMMON_H 1
#include <stdint.h>
#include "lsquic_int_types.h"
enum PACKET_PUBLIC_FLAGS
{
PACKET_PUBLIC_FLAGS_VERSION = 1,
PACKET_PUBLIC_FLAGS_RST = 2,
PACKET_PUBLIC_FLAGS_NONCE = 4,
PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID = 8,
PACKET_PUBLIC_FLAGS_MULTIPATH = 1 << 6,
PACKET_PUBLIC_FLAGS_TWO_OR_MORE_BYTES = 1 << 7,
};
enum QUIC_FRAME_TYPE
{
QUIC_FRAME_INVALID,
/*Special*/
QUIC_FRAME_STREAM,
QUIC_FRAME_ACK,
/*Regular*/
QUIC_FRAME_PADDING,
QUIC_FRAME_RST_STREAM,
QUIC_FRAME_CONNECTION_CLOSE,
QUIC_FRAME_GOAWAY,
QUIC_FRAME_WINDOW_UPDATE,
QUIC_FRAME_BLOCKED,
QUIC_FRAME_STOP_WAITING,
QUIC_FRAME_PING,
N_QUIC_FRAMES
};
extern const size_t lsquic_frame_types_str_sz;
const char *
lsquic_frame_types_to_str (char *buf, size_t bufsz, short frame_types);
#define QFRAME_REGEN_MASK ((1 << QUIC_FRAME_ACK) \
| (1 << QUIC_FRAME_STOP_WAITING))
#define QFRAME_REGENERATE(frame_type) ((1 << (frame_type)) & QFRAME_REGEN_MASK)
#define QFRAME_ACKABLE_MASK ( \
(1 << QUIC_FRAME_STREAM) \
| (1 << QUIC_FRAME_RST_STREAM) \
| (1 << QUIC_FRAME_GOAWAY) \
| (1 << QUIC_FRAME_WINDOW_UPDATE) \
| (1 << QUIC_FRAME_PING) \
| (1 << QUIC_FRAME_BLOCKED) \
)
#define QFRAME_ACKABLE(frame_type) ((1 << (frame_type)) & QFRAME_ACKABLE_MASK)
#define QFRAME_RETRANSMITTABLE_MASK ( \
(1 << QUIC_FRAME_STREAM) \
| (1 << QUIC_FRAME_RST_STREAM) \
| (1 << QUIC_FRAME_GOAWAY) \
| (1 << QUIC_FRAME_WINDOW_UPDATE) \
| (1 << QUIC_FRAME_BLOCKED) \
| (1 << QUIC_FRAME_CONNECTION_CLOSE) \
)
#define QFRAME_RETRANSMITTABLE(frame_type) \
((1 << (frame_type)) & QFRAME_RETRANSMITTABLE_MASK)
#define QUIC_MAX_PUBHDR_SZ (1 /* Type */ + 8 /* CID */ + 4 /* Version */ \
+ 32 /* Nonce */ + 6 /* Packet Number */ )
#define QUIC_MIN_PUBHDR_SZ (1 /* Type */ + 1 /* Packet number */)
/* 12 bytes of FNV hash or encryption IV */
#define QUIC_PACKET_HASH_SZ 12
/* [draft-hamilton-quic-transport-protocol-01], Section 7 */
#define QUIC_MAX_IPv4_PACKET_SZ 1370
#define QUIC_MAX_IPv6_PACKET_SZ 1350
#define QUIC_MAX_PACKET_SZ (QUIC_MAX_IPv4_PACKET_SZ > \
QUIC_MAX_IPv6_PACKET_SZ ? QUIC_MAX_IPv4_PACKET_SZ : QUIC_MAX_IPv6_PACKET_SZ)
#define QUIC_MIN_PACKET_OVERHEAD (QUIC_PACKET_HASH_SZ + QUIC_MIN_PUBHDR_SZ)
#define QUIC_MAX_PAYLOAD_SZ (QUIC_MAX_PACKET_SZ - QUIC_MIN_PACKET_OVERHEAD)
#define QUIC_WUF_SZ 13 /* Type (1) + Stream ID (4) + Offset (8) */
#define QUIC_BLOCKED_FRAME_SZ 5 /* Type (1) + Stream ID (4) */
#define QUIC_RST_STREAM_SZ 17 /* Type (1) + Stream ID (4) + Offset (8) +
Error code (4) */
#define QUIC_GOAWAY_FRAME_SZ 11 /* Type (1) + Error code (4) + Stream ID (4) +
Reason phrase length (2) */
/* Bitmask to be used as bits 4 and 5 (0x30) in common header's flag field: */
enum lsquic_packno_bits
{
PACKNO_LEN_1 = 0,
PACKNO_LEN_2 = 1,
PACKNO_LEN_4 = 2,
PACKNO_LEN_6 = 3,
};
enum lsquic_packno_bits
calc_packno_bits (lsquic_packno_t packno, lsquic_packno_t least_unacked,
uint64_t n_in_flight);
#define packno_bits2len(b) (((b) << 1) + !(b))
lsquic_packno_t
restore_packno (lsquic_packno_t cur_packno,
enum lsquic_packno_bits cur_packno_bits,
lsquic_packno_t max_packno);
#endif

View file

@ -0,0 +1,35 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_packet_in.h"
int
packet_in_ver_first (const lsquic_packet_in_t *packet_in, struct ver_iter *vi,
lsquic_ver_tag_t *ver_tag)
{
vi->packet_in = packet_in;
vi->off = packet_in->pi_quic_ver;
return packet_in_ver_next(vi, ver_tag);
}
int
packet_in_ver_next (struct ver_iter *vi, lsquic_ver_tag_t *ver_tag)
{
if (vi->off + 4 <= vi->packet_in->pi_header_sz)
{
memcpy(ver_tag, vi->packet_in->pi_data + vi->off, 4);
vi->off += 4;
return 1;
}
else
{
assert(vi->packet_in->pi_header_sz == vi->off);
return 0;
}
}

View file

@ -0,0 +1,101 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_packet_in.h
*/
#ifndef LSQUIC_PACKET_IN_H
#define LSQUIC_PACKET_IN_H 1
#include <sys/queue.h>
struct lsquic_packet_in;
struct data_frame
{
const unsigned char *df_data; /* Pointer to data */
uint64_t df_offset; /* Stream offset */
uint16_t df_read_off; /* Read offset */
uint16_t df_size; /* Size of df_data */
signed char df_fin; /* FIN? */
};
typedef struct stream_frame
{
/* Stream frames are stored in a list inside stream. */
TAILQ_ENTRY(stream_frame) next_frame;
/* `data' points somewhere into the packet payload. The packet object
* is reference-counted. When the frame is freed, the packet is released
* via lsquic_packet_put(). If data_length is zero, the frame does not
* keep a reference to the incoming packet and this pointer is not set.
*/
struct lsquic_packet_in *packet_in;
struct data_frame data_frame;
uint32_t stream_id; /* Parsed from packet */
} stream_frame_t;
typedef struct lsquic_packet_in
{
TAILQ_ENTRY(lsquic_packet_in) pi_next;
lsquic_time_t pi_received; /* Time received */
lsquic_cid_t pi_conn_id;
lsquic_packno_t pi_packno;
unsigned short pi_header_sz; /* Points to payload */
unsigned short pi_data_sz; /* Data plus header */
/* A packet may be referred to by one or more frames and packets_in
* list.
*/
unsigned short pi_refcnt;
short pi_frame_types;
unsigned short pi_hsk_stream; /* Offset to handshake stream
* frame, only valid if
* PI_HSK_STREAM is set.
*/
unsigned char pi_quic_ver; /* Offset to QUIC version */
unsigned char pi_nonce; /* Offset to nonce */
enum {
PI_DECRYPTED = (1 << 0),
PI_OWN_DATA = (1 << 1), /* We own pi_data */
PI_CONN_ID = (1 << 2), /* pi_conn_id is set */
} pi_flags:8;
/* If PI_OWN_DATA flag is not set, `pi_data' points to user-supplied
* packet data, which is NOT TO BE MODIFIED.
*/
unsigned char *pi_data;
} lsquic_packet_in_t;
#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_packno_bits(p) \
((lsquic_packet_in_public_flags(p) >> 4) & 3)
#define lsquic_packet_in_upref(p) (++(p)->pi_refcnt)
#define lsquic_packet_in_get(p) (lsquic_packet_in_upref(p), (p))
#define lsquic_packet_in_nonce(p) \
((p)->pi_nonce ? (p)->pi_data + (p)->pi_nonce : NULL)
/* The version iterator is used on a version negotiation packet only.
* The iterator functions return 1 when next version is returned and
* 0 when there are no more versions.
*/
struct ver_iter
{
const struct lsquic_packet_in *packet_in;
unsigned off;
};
int
packet_in_ver_first (const lsquic_packet_in_t *packet_in, struct ver_iter *,
lsquic_ver_tag_t *ver_tag);
int
packet_in_ver_next (struct ver_iter *, lsquic_ver_tag_t *ver_tag);
#endif

View file

@ -0,0 +1,303 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_packet_out.c
*/
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_malo.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
#include "lsquic_parse.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_logger.h"
typedef char _stream_rec_arr_is_at_most_64bytes[
(sizeof(struct stream_rec_arr) <= 64) - 1];
struct stream_rec *
posi_first (struct packet_out_srec_iter *posi,
lsquic_packet_out_t *packet_out)
{
posi->packet_out = packet_out;
posi->past_srec = 0;
return posi_next(posi);
}
struct stream_rec *
posi_next (struct packet_out_srec_iter *posi)
{
if (posi->past_srec)
{
while (posi->cur_srec_arr)
{
for (; posi->srec_idx < sizeof(posi->cur_srec_arr->srecs) / sizeof(posi->cur_srec_arr->srecs[0]);
++posi->srec_idx)
{
if (posi->cur_srec_arr->srecs[ posi->srec_idx ].sr_frame_types)
return &posi->cur_srec_arr->srecs[ posi->srec_idx++ ];
}
posi->cur_srec_arr = STAILQ_NEXT(posi->cur_srec_arr, next_stream_rec_arr);
posi->srec_idx = 0;
}
return NULL;
}
else
{
++posi->past_srec;
posi->cur_srec_arr = STAILQ_FIRST(&posi->packet_out->po_srec_arrs);
posi->srec_idx = 0;
if (posi->packet_out->po_srec.sr_frame_types)
return &posi->packet_out->po_srec;
return posi_next(posi);
}
}
/* Assumption: there can only be one STREAM and only one RST_STREAM frame
* for a particular stream per packet. The latter is true because a stream
* will only send out one of them. The former is true due the way packets
* are filled: stream will write out STREAM frame as large as it can.
*
* Assumption: frames are added to the packet_out in order of their placement
* in packet_out->po_data. There is an assertion in this function that guards
* for this.
*/
int
lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,
struct lsquic_mm *mm,
struct lsquic_stream *new_stream,
enum QUIC_FRAME_TYPE frame_type,
unsigned short off)
{
struct packet_out_srec_iter posi;
struct stream_rec_arr *srec_arr;
struct stream_rec *srec;
for (srec = posi_first(&posi, packet_out); srec; srec = posi_next(&posi))
if (srec->sr_stream == new_stream)
{
switch (frame_type)
{
case QUIC_FRAME_STREAM:
assert(!(srec->sr_frame_types & (1 << QUIC_FRAME_STREAM)));
srec->sr_frame_types |= (1 << QUIC_FRAME_STREAM);
srec->sr_off = off;
break;
default:
assert(QUIC_FRAME_RST_STREAM == frame_type);
assert(!(srec->sr_frame_types & (1 << QUIC_FRAME_RST_STREAM)));
srec->sr_frame_types |= (1 << QUIC_FRAME_RST_STREAM);
break;
}
return 0; /* Update existing record */
}
else if (srec->sr_frame_types & (1 << QUIC_FRAME_STREAM) & (1 << frame_type))
assert(srec->sr_off < off); /* Check that STREAM frames are added in order */
++new_stream->n_unacked;
if (!srec_taken(&packet_out->po_srec))
{
packet_out->po_srec.sr_frame_types = (1 << frame_type);
packet_out->po_srec.sr_stream = new_stream;
packet_out->po_srec.sr_off = off;
return 0; /* Insert in first slot */
}
STAILQ_FOREACH(srec_arr, &packet_out->po_srec_arrs, next_stream_rec_arr)
{
unsigned i;
for (i = 0; i < sizeof(srec_arr->srecs) / sizeof(srec_arr->srecs[0]); ++i)
if (!srec_taken(&srec_arr->srecs[i]))
{
srec_arr->srecs[i].sr_frame_types = (1 << frame_type);
srec_arr->srecs[i].sr_stream = new_stream;
srec_arr->srecs[i].sr_off = off;
return 0; /* Insert in existing srec */
}
}
srec_arr = lsquic_malo_get(mm->malo.stream_rec_arr);
if (!srec_arr)
return -1;
memset(srec_arr, 0, sizeof(*srec_arr));
srec_arr->srecs[0].sr_frame_types = (1 << frame_type);
srec_arr->srecs[0].sr_stream = new_stream;
srec_arr->srecs[0].sr_off = off;
STAILQ_INSERT_TAIL(&packet_out->po_srec_arrs, srec_arr, next_stream_rec_arr);
return 0; /* Insert in new srec */
}
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 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;
flags = bits << POBIT_SHIFT;
if (ver_tag)
flags |= PO_VERSION;
if (nonce)
flags |= PO_NONCE;
if (use_cid)
flags |= PO_CONN_ID;
header_size = lsquic_po_header_length(flags);
if (header_size + QUIC_PACKET_HASH_SZ >= max_size)
{
errno = EINVAL;
return NULL;
}
packet_out = lsquic_mm_get_packet_out(mm, malo, max_size - header_size
- QUIC_PACKET_HASH_SZ);
if (!packet_out)
return NULL;
packet_out->po_flags = PO_WRITEABLE | flags;
if (ver_tag)
packet_out->po_ver_tag = *ver_tag;
if (nonce)
{
/* Nonces are allocated for a very small number of packets. This
* memory is too expensive to carry in every packet.
*/
packet_out->po_nonce = malloc(32);
if (!packet_out->po_nonce)
{
lsquic_mm_put_packet_out(mm, packet_out);
return NULL;
}
memcpy(packet_out->po_nonce, nonce, 32);
}
return packet_out;
}
void
lsquic_packet_out_destroy (lsquic_packet_out_t *packet_out,
struct lsquic_engine_public *enpub)
{
struct stream_rec_arr *srec_arr;
while ((srec_arr = STAILQ_FIRST(&packet_out->po_srec_arrs)))
{
STAILQ_REMOVE_HEAD(&packet_out->po_srec_arrs, next_stream_rec_arr);
lsquic_malo_put(srec_arr);
}
if (packet_out->po_flags & PO_ENCRYPTED)
enpub->enp_pmi->pmi_release(enpub->enp_pmi_ctx,
packet_out->po_enc_data);
if (packet_out->po_nonce)
free(packet_out->po_nonce);
lsquic_mm_put_packet_out(&enpub->enp_mm, packet_out);
}
/* If `stream_id' is zero, stream frames from all reset streams are elided.
* Otherwise, elision is limited to the specified stream.
*/
void
lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_t *packet_out,
const struct parse_funcs *pf,
uint32_t stream_id)
{
struct packet_out_srec_iter posi;
struct stream_rec *srec;
struct stream_frame frame;
unsigned short adj = 0;
int n_stream_frames = 0, n_elided = 0;
int victim;
for (srec = posi_first(&posi, packet_out); srec; srec = posi_next(&posi))
{
if (srec->sr_frame_types & (1 << QUIC_FRAME_STREAM))
{
++n_stream_frames;
/* Offsets of all STREAM frames should be adjusted */
srec->sr_off -= adj;
if (stream_id)
{
victim = srec->sr_stream->id == stream_id;
if (victim)
{
assert(lsquic_stream_is_reset(srec->sr_stream));
}
}
else
victim = lsquic_stream_is_reset(srec->sr_stream);
if (victim)
{
++n_elided;
const int len =
pf->pf_parse_stream_frame(packet_out->po_data + srec->sr_off,
packet_out->po_data_sz - srec->sr_off, &frame);
if (len < 0)
{ /* This is pretty severe: we should be able to parse our own
* frames. Should this abort the connection?
*/
LSQ_ERROR("can't parse our own stream frame");
return;
}
assert(frame.stream_id == srec->sr_stream->id);
/* Move the data and adjust sizes */
adj += len;
memmove(packet_out->po_data + srec->sr_off,
packet_out->po_data + srec->sr_off + len,
packet_out->po_data_sz - srec->sr_off - len);
packet_out->po_data_sz -= len;
/* See what we can do with the stream */
srec->sr_frame_types &= ~(1 << QUIC_FRAME_STREAM);
if (!srec_taken(srec))
lsquic_stream_acked(srec->sr_stream);
}
}
}
assert(n_stream_frames);
if (n_elided == n_stream_frames)
packet_out->po_frame_types &= ~(1 << QUIC_FRAME_STREAM);
}
void
lsquic_packet_out_chop_regen (lsquic_packet_out_t *packet_out)
{
struct packet_out_srec_iter posi;
struct stream_rec *srec;
unsigned delta;
delta = packet_out->po_regen_sz;
packet_out->po_data_sz -= delta;
memmove(packet_out->po_data, packet_out->po_data + delta,
packet_out->po_data_sz);
packet_out->po_regen_sz = 0;
for (srec = posi_first(&posi, packet_out); srec; srec = posi_next(&posi))
if (srec->sr_frame_types & (1 << QUIC_FRAME_STREAM))
srec->sr_off -= delta;
}

View file

@ -0,0 +1,162 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_packet_out.h -- Structure and routines dealing with packet_out
*/
#ifndef LSQUIC_PACKET_OUT_H
#define LSQUIC_PACKET_OUT_H 1
#include <sys/queue.h>
struct malo;
struct lsquic_engine_public;
struct lsquic_mm;
struct lsquic_stream;
struct parse_funcs;
/* Each stream_rec is associated with one packet_out. packet_out can have
* zero or more stream_rec structures. stream_rec keeps a pointer to a stream
* that has STREAM or RST_STREAM frames inside packet_out. `sr_frame_types'
* is a bitmask that records which of these two frames are in the packet.
* If this value is zero, `sr_stream' and `sr_off' values are not valid.
* `sr_off' indicates where inside packet_out->po_data STREAM frame begins.
*
* We need this information for two reasons:
* 1. A stream is not destroyed until all of its STREAM and RST_STREAM
* frames are acknowledged. This is to make sure that we do not exceed
* maximum allowed number of streams.
* 2. When a packet is resubmitted, STREAM frames for a stream that has
* been reset are not to be resubmitted.
*/
struct stream_rec {
struct lsquic_stream *sr_stream;
unsigned short sr_off;
short sr_frame_types;
};
#define srec_taken(srec) ((srec)->sr_frame_types)
struct stream_rec_arr {
STAILQ_ENTRY(stream_rec_arr) next_stream_rec_arr;
struct stream_rec srecs[
( 64 /* Efficient size for malo allocator */
- sizeof(SLIST_ENTRY(stream_rec)) /* next_stream_rec */
) / sizeof(struct stream_rec)
];
};
typedef struct lsquic_packet_out
{
/* `po_next' is used for packets_out, unacked_packets and expired_packets
* lists.
*/
TAILQ_ENTRY(lsquic_packet_out)
po_next;
lsquic_time_t po_sent; /* Time sent */
lsquic_packno_t po_packno;
/* A lot of packets contain data belonging to only one stream. Thus,
* `srec' is used first. If this is not enough, any number of
* stream_rec_arr structures can be allocated to handle more stream
* records.
*/
struct stream_rec po_srec;
STAILQ_HEAD(, stream_rec_arr)
po_srec_arrs;
/* If PO_ENCRYPTED is set, this points to the buffer that holds encrypted
* data.
*/
unsigned char *po_enc_data;
lsquic_ver_tag_t po_ver_tag; /* Set if PO_VERSION is set */
short po_frame_types; /* 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 */
unsigned short po_regen_sz; /* Number of bytes at the beginning
* of data containing bytes that are
* not to be retransmitted, e.g. ACK
* frames.
*/
unsigned short po_n_alloc; /* Total number of bytes allocated in po_data */
enum packet_out_flags {
PO_HELLO = (1 << 1), /* Packet contains SHLO or CHLO data */
PO_ENCRYPTED= (1 << 3), /* po_enc_data has encrypted data */
PO_WRITEABLE= (1 << 4), /* Packet is writeable */
#define POBIT_SHIFT 5
PO_BITS_0 = (1 << 5), /* PO_BITS_0 and PO_BITS_1 encode the */
PO_BITS_1 = (1 << 6), /* packet number length. See macros below. */
PO_NONCE = (1 << 7), /* Use value in `po_nonce' to generate header */
PO_VERSION = (1 << 8), /* Use value in `po_ver_tag' to generate header */
PO_CONN_ID = (1 << 9), /* Include connection ID in public header */
PO_REPACKNO = (1 <<10), /* Regenerate packet number */
PO_NOENCRYPT= (1 <<11), /* Do not encrypt data in po_data */
PO_VERNEG = (1 <<12), /* Version negotiation packet. */
} po_flags:16;
unsigned char *po_nonce; /* Use to generate header if PO_NONCE is set */
unsigned char *po_data;
} lsquic_packet_out_t;
/* The size of lsquic_packet_out_t could be further reduced:
*
* po_ver_tag could be encoded as a few bits representing enum lsquic_version
* in po_flags. The cost is a bit of complexity. This will save us four bytes.
*/
#define lsquic_packet_out_avail(p) ((unsigned short) \
((p)->po_n_alloc - (p)->po_data_sz))
#define lsquic_packet_out_packno_bits(p) (((p)->po_flags >> POBIT_SHIFT) & 0x3)
/* XXX This will need to be made into a method for Q041 */
#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_packet_out_verneg(p) \
(((p)->po_flags & (PO_NOENCRYPT|PO_VERNEG)) == (PO_NOENCRYPT|PO_VERNEG))
#define lsquic_packet_out_pubres(p) \
(((p)->po_flags & (PO_NOENCRYPT|PO_VERNEG)) == PO_NOENCRYPT )
struct packet_out_srec_iter {
lsquic_packet_out_t *packet_out;
struct stream_rec_arr *cur_srec_arr;
unsigned srec_idx;
int past_srec;
};
struct stream_rec *
posi_first (struct packet_out_srec_iter *posi, lsquic_packet_out_t *);
struct stream_rec *
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 lsquic_ver_tag_t *, const unsigned char *nonce);
void
lsquic_packet_out_destroy (lsquic_packet_out_t *,
struct lsquic_engine_public *);
int
lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,
struct lsquic_mm *mm,
struct lsquic_stream *new_stream,
enum QUIC_FRAME_TYPE,
unsigned short off);
void
lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_t *,
const struct parse_funcs *, uint32_t);
void
lsquic_packet_out_chop_regen (lsquic_packet_out_t *);
#endif

View file

@ -0,0 +1,138 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_packints.c -- Packet intervals implementation.
*/
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/queue.h>
#include "lsquic_int_types.h"
#include "lsquic_packints.h"
void
lsquic_packints_init (struct packints *pints)
{
TAILQ_INIT(&pints->pk_intervals);
pints->pk_cur = NULL;
}
void
lsquic_packints_cleanup (struct packints *pints)
{
struct packet_interval *pi, *next;
for (pi = TAILQ_FIRST(&pints->pk_intervals); pi; pi = next)
{
next = TAILQ_NEXT(pi, next_pi);
free(pi);
}
}
static int
grow_pi (struct packet_interval *pi, lsquic_packno_t packno)
{
if (pi->range.low - 1 == packno) {
--pi->range.low;
return 1;
}
if (pi->range.high + 1 == packno) {
++pi->range.high;
return 1;
}
return 0;
}
#if LSQUIC_PACKINTS_SANITY_CHECK
void
lsquic_packints_sanity_check (const struct packints *packints)
{
struct packet_interval *pi;
uint64_t prev_high;
prev_high = 0;
TAILQ_FOREACH(pi, &packints->pk_intervals, next_pi)
{
if (prev_high)
{
assert(pi->range.high + 1 < prev_high);
assert(pi->range.high >= pi->range.low);
}
else
prev_high = pi->range.high;
}
}
#endif
enum packints_status
lsquic_packints_add (struct packints *pints, lsquic_packno_t packno)
{
struct packet_interval *pi, *prev;
prev = NULL;
TAILQ_FOREACH(pi, &pints->pk_intervals, next_pi)
{
if (packno <= pi->range.high)
{
if (packno >= pi->range.low)
return PACKINTS_DUP;
} else {
if (packno > pi->range.high)
break;
}
prev = pi;
}
if ((prev && grow_pi(prev, packno)) || (pi && grow_pi(pi, packno)))
{
if (prev && pi && (prev->range.low - 1 == pi->range.high)) {
prev->range.low = pi->range.low;
TAILQ_REMOVE(&pints->pk_intervals, pi, next_pi);
free(pi);
}
}
else
{
struct packet_interval *newpi = malloc(sizeof(*newpi));
if (!newpi)
return PACKINTS_ERR;
newpi->range.low = newpi->range.high = packno;
if (pi)
TAILQ_INSERT_BEFORE(pi, newpi, next_pi);
else
TAILQ_INSERT_TAIL(&pints->pk_intervals, newpi, next_pi);
}
lsquic_packints_sanity_check(pints);
return PACKINTS_OK;
}
const struct lsquic_packno_range *
lsquic_packints_first (struct packints *pints)
{
pints->pk_cur = TAILQ_FIRST(&pints->pk_intervals);
return lsquic_packints_next(pints);
}
const struct lsquic_packno_range *
lsquic_packints_next (struct packints *pints)
{
const struct lsquic_packno_range *range;
if (pints->pk_cur)
{
range = &pints->pk_cur->range;
pints->pk_cur = TAILQ_NEXT(pints->pk_cur, next_pi);
return range;
}
else
return NULL;
}

View file

@ -0,0 +1,47 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_packints.h -- Ordered (high to low) list of packet intervals.
*/
#ifndef LSQUIC_PACKINTS_H
#define LSQUIC_PACKINTS_H 1
#define LSQUIC_PACKINTS_SANITY_CHECK 0
#include <sys/queue.h>
struct packet_interval {
TAILQ_ENTRY(packet_interval) next_pi;
struct lsquic_packno_range range;
};
struct packints {
TAILQ_HEAD(, packet_interval) pk_intervals;
struct packet_interval *pk_cur;
};
void
lsquic_packints_init (struct packints *);
void
lsquic_packints_cleanup (struct packints *);
enum packints_status { PACKINTS_OK, PACKINTS_DUP, PACKINTS_ERR, };
enum packints_status
lsquic_packints_add (struct packints *, lsquic_packno_t);
const struct lsquic_packno_range *
lsquic_packints_first (struct packints *);
const struct lsquic_packno_range *
lsquic_packints_next (struct packints *);
#if LSQUIC_PACKINTS_SANITY_CHECK
void
lsquic_packints_sanity_check (const struct packints *);
#else
# define lsquic_packints_sanity_check(pints)
#endif
#endif

View file

@ -0,0 +1,211 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_PARSE_H
#define LSQUIC_PARSE_H 1
#include <stdint.h>
#include "lsquic_packet_common.h"
struct lsquic_packet_in;
struct stream_frame;
#define LSQUIC_PARSE_ACK_TIMESTAMPS 0
typedef struct ack_info
{
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;
struct {
lsquic_packno_t high, low;
} 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
} ack_info_t;
#define largest_acked(acki) (+(acki)->ranges[0].high)
#define smallest_acked(acki) (+(acki)->ranges[(acki)->n_ranges - 1].low)
/* gaf_: generate ACK frame */
struct lsquic_packno_range;
typedef const struct lsquic_packno_range *
(*gaf_rechist_first_f) (void *rechist);
typedef const struct lsquic_packno_range *
(*gaf_rechist_next_f) (void *rechist);
typedef lsquic_time_t
(*gaf_rechist_largest_recv_f) (void *rechist);
/* gsf_: generate stream frame */
typedef int (*gsf_fin_f) (void *stream);
typedef size_t (*gsf_size_f) (void *stream);
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).
*/
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);
void
(*pf_parse_packet_in_finish) (struct lsquic_packet_in *packet_in,
struct packin_parse_state *);
enum QUIC_FRAME_TYPE
(*pf_parse_frame_type) (unsigned char);
/* Return used buffer length */
int
(*pf_gen_stream_frame) (unsigned char *buf, size_t bufsz,
uint32_t stream_id, uint64_t offset,
gsf_fin_f, gsf_size_f, gsf_read_f, void *stream);
unsigned
(*pf_parse_stream_frame_header_sz) (unsigned char type);
int
(*pf_parse_stream_frame) (const unsigned char *buf, size_t rem_packet_sz,
struct stream_frame *);
int
(*pf_parse_ack_frame) (const unsigned char *buf, size_t buf_len,
ack_info_t *ack_info);
lsquic_packno_t
(*pf_parse_ack_high) (const unsigned char *buf, size_t buf_len);
int
(*pf_gen_ack_frame) (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f, gaf_rechist_next_f,
gaf_rechist_largest_recv_f, void *rechist, lsquic_time_t now,
int *has_missing);
int
(*pf_gen_stop_waiting_frame) (unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits,
lsquic_packno_t least_unacked_packno);
int
(*pf_parse_stop_waiting_frame) (const unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits,
lsquic_packno_t *least_unacked);
int
(*pf_skip_stop_waiting_frame) (size_t buf_len, enum lsquic_packno_bits);
int
(*pf_gen_window_update_frame) (unsigned char *buf, int buf_len,
uint32_t stream_id, uint64_t offset);
int
(*pf_parse_window_update_frame) (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id, uint64_t *offset);
int
(*pf_gen_blocked_frame) (unsigned char *buf, size_t buf_len,
uint32_t stream_id);
int
(*pf_parse_blocked_frame) (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id);
int
(*pf_gen_rst_frame) (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, uint32_t error_code);
int
(*pf_parse_rst_frame) (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id, uint64_t *offset, uint32_t *error_code);
int
(*pf_gen_connect_close_frame) (unsigned char *buf, int buf_len,
uint32_t error_code, const char *reason, int reason_len);
int
(*pf_parse_connect_close_frame) (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint16_t *reason_length,
uint8_t *reason_offset);
int
(*pf_gen_goaway_frame) (unsigned char *buf, size_t buf_len,
uint32_t error_code, uint32_t last_good_stream_id,
const char *reason, size_t reason_len);
int
(*pf_parse_goaway_frame) (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint32_t *last_good_stream_id,
uint16_t *reason_length, const char **reason);
int
(*pf_gen_ping_frame) (unsigned char *buf, int buf_len);
#ifndef NDEBUG
/* These float reading and writing functions assume `mem' has at least
* 2 bytes.
*/
void
(*pf_write_float_time16) (lsquic_time_t time_us, void *mem);
uint64_t
(*pf_read_float_time16) (const void *mem);
#endif
size_t
(*pf_calc_stream_frame_header_sz) (uint32_t stream_id, uint64_t offset);
};
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_Q040;
#define select_pf_by_ver(ver) ( \
((1 << (ver)) & ((1 << LSQVER_035) | \
(1 << LSQVER_037) | (1 << LSQVER_038))) \
? &lsquic_parse_funcs_gquic_le : \
((1 << (ver)) & (1 << LSQVER_039)) \
? &lsquic_parse_funcs_gquic_Q039 \
: &lsquic_parse_funcs_gquic_Q040)
/* This function is QUIC-version independent */
int
parse_packet_in_begin (struct lsquic_packet_in *, size_t length,
int is_server, struct packin_parse_state *);
enum QUIC_FRAME_TYPE
parse_frame_type_gquic_Q035_thru_Q039 (unsigned char first_byte);
enum QUIC_FRAME_TYPE
parse_frame_type_gquic_Q040 (unsigned char first_byte);
unsigned
parse_stream_frame_header_sz_gquic (unsigned char type);
size_t
calc_stream_frame_header_sz_gquic (uint32_t stream_id, uint64_t offset);
/* This maps two bits as follows:
* 00 -> 1
* 01 -> 2
* 10 -> 4
* 11 -> 6
*
* Assumes that only two low bits are set.
*/
#define twobit_to_1246(bits) ((bits) * 2 + !(bits))
/* This maps two bits as follows:
* 00 -> 1
* 01 -> 2
* 10 -> 4
* 11 -> 8
*
* Assumes that only two low bits are set.
*/
#define twobit_to_1248(bits) (1 << (bits))
char *
acki2str (const struct ack_info *acki, size_t *sz);
#endif

View file

@ -0,0 +1,574 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <inttypes.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "lsquic_types.h"
#include "lsquic_alarmset.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_parse.h"
#include "lsquic_rechist.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_mm.h"
#include "lsquic_malo.h"
#include "lsquic_version.h"
#include "lsquic.h"
#include "lsquic_parse_gquic_be.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PARSE
#include "lsquic_logger.h"
int
gquic_ietf_gen_rst_frame (unsigned char *buf, size_t buf_len,
uint32_t stream_id, uint64_t offset, uint32_t error_code)
{
unsigned char *p = buf;
if (buf_len < QUIC_RST_STREAM_SZ)
return -1;
*p = 0x01;
++p;
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(p, &stream_id, 4);
p += 4;
#if __BYTE_ORDER == __LITTLE_ENDIAN
error_code = bswap_32(error_code);
#endif
memcpy(p, &error_code, 4);
p += 4;
#if __BYTE_ORDER == __LITTLE_ENDIAN
offset = bswap_64(offset);
#endif
memcpy(p, &offset, 8);
p += 8;
return p - buf;
}
int
gquic_ietf_parse_rst_frame (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id, uint64_t *offset, uint32_t *error_code)
{
if (buf_len < QUIC_RST_STREAM_SZ)
return -1;
READ_UINT(*stream_id, 32, buf + 1, 4);
READ_UINT(*error_code, 32, buf + 1 + 4, 4);
READ_UINT(*offset, 64, buf + 1 + 4 + 4, 8);
return QUIC_RST_STREAM_SZ;
}
unsigned
gquic_ietf_parse_stream_frame_header_sz (unsigned char type)
{
const unsigned data_len = (type & 1) << 1;
const unsigned stream_id_len = ((type >> 3) & 3) + 1;
const unsigned offset_len = (((type >> 1) & 3) << 1) +
((3 == ((type >> 1) & 3)) << 1);
return 1 + data_len + offset_len + stream_id_len;
}
int
gquic_ietf_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz,
stream_frame_t *stream_frame)
{
/* 11FSSOOD */
const unsigned char *p = buf;
const unsigned char *const pend = p + rem_packet_sz;
CHECK_SPACE(1, p, pend);
const char type = *p++;
const unsigned data_len = (type & 1) << 1;
const unsigned stream_id_len = ((type >> 3) & 3) + 1;
const unsigned offset_len = (((type >> 1) & 3) << 1) +
((3 == ((type >> 1) & 3)) << 1);
const unsigned need = data_len + offset_len + stream_id_len;
CHECK_SPACE(need, p, pend);
memset(stream_frame, 0, sizeof(*stream_frame));
stream_frame->data_frame.df_fin = !!(type & 0x20);
if (data_len)
{
READ_UINT(stream_frame->data_frame.df_size, 16, p, data_len);
p += data_len;
}
READ_UINT(stream_frame->stream_id, 32, p, stream_id_len);
p += stream_id_len;
READ_UINT(stream_frame->data_frame.df_offset, 64, p, offset_len);
p += offset_len;
if (data_len)
{
CHECK_SPACE(stream_frame->data_frame.df_size, p, pend);
stream_frame->data_frame.df_data = p;
p += stream_frame->data_frame.df_size;
}
else
{
stream_frame->data_frame.df_size = pend - p;
stream_frame->data_frame.df_data = p;
p = pend;
}
/* From the spec: "A stream frame must always have either non-zero
* data length or the FIN bit set.'
*/
if (!(stream_frame->data_frame.df_size ||
stream_frame->data_frame.df_fin))
return -1;
assert(p <= pend);
return p - (unsigned char *) buf;
}
static size_t
gquic_ietf_calc_stream_frame_header_sz (uint32_t stream_id, uint64_t offset)
{
return
/* Type */
1
/* SS: Stream ID length: 1, 2, 3, or 4 bytes */
+ (stream_id > 0x0000FF)
+ (stream_id > 0x00FFFF)
+ (stream_id > 0xFFFFFF)
+ 1
/* OO: Offset length: 0, 2, 4, or 8 bytes */
+ ((offset >= (1ULL << 32)) << 2)
+ ((offset >= (1ULL << 16)) << 1)
+ ((offset > 1) << 1)
;
}
int
gquic_ietf_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, gsf_fin_f gsf_fin, gsf_size_f gsf_size,
gsf_read_f gsf_read, void *stream)
{
/* 11FSSOOD */
unsigned slen, olen, dlen;
unsigned char *p = buf + 1;
int fin;
/* SS: Stream ID length: 1, 2, 3, or 4 bytes */
slen = (stream_id > 0x0000FF)
+ (stream_id > 0x00FFFF)
+ (stream_id > 0xFFFFFF)
+ 1;
/* OO: Offset length: 0, 2, 4, or 8 bytes */
olen = ((offset >= (1ULL << 32)) << 2)
+ ((offset >= (1ULL << 16)) << 1)
+ ((offset > 1) << 1)
;
fin = gsf_fin(stream);
if (!fin)
{
unsigned size, n_avail;
uint16_t nr;
size = gsf_size(stream);
n_avail = buf_len - (p + slen + olen - buf);
/* If we cannot fill remaining buffer, we need to include data
* length.
*/
dlen = (size < n_avail) << 1;
n_avail -= dlen;
CHECK_SPACE(1 + olen + slen + dlen +
+ 1 /* We need to write at least 1 byte */, buf, buf + buf_len);
p += dlen; /* Save room for data length */
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(p, (unsigned char *) &stream_id + 4 - slen, slen);
p += slen;
#if __BYTE_ORDER == __LITTLE_ENDIAN
offset = bswap_64(offset);
#endif
memcpy(p, (unsigned char *) &offset + 8 - olen, olen);
p += olen;
/* Read as much as we can */
nr = gsf_read(stream, p, n_avail, &fin);
assert(nr != 0);
if (dlen)
{
uint16_t nr_copy = nr;
#if __BYTE_ORDER == __LITTLE_ENDIAN
nr_copy = bswap_16(nr_copy);
#endif
memcpy(p - slen - olen - 2, &nr_copy, 2);
}
p += nr;
}
else
{
dlen = 2;
CHECK_SPACE(1 + slen + olen + 2, buf, buf + buf_len);
memset(p, 0, 2);
p += 2;
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(p, (unsigned char *) &stream_id + 4 - slen, slen);
p += slen;
#if __BYTE_ORDER == __LITTLE_ENDIAN
offset = bswap_64(offset);
#endif
memcpy(p, (unsigned char *) &offset + 8 - olen, olen);
p += olen;
}
/* Convert slen to bit representation: 0 - 3: */
slen -= 1;
assert(slen <= 3);
/* Convert olen to bit representation: 0 - 3: */
olen >>= 1;
olen -= olen == 4;
assert(olen <= 3);
buf[0] = 0xC0
| (fin << 5)
| (slen << 3)
| (olen << 1)
| !!dlen
;
return p - buf;
}
/* This is a special function: it is used to extract the largest observed
* packet number from ACK frame that we ourselves generated. This allows
* us to skip some checks.
*/
lsquic_packno_t
gquic_ietf_parse_ack_high (const unsigned char *buf, size_t buf_len)
{
unsigned char type;
unsigned largest_obs_len;
unsigned n_blocks_len;
lsquic_packno_t packno;
type = buf[0];
largest_obs_len = twobit_to_1248((type >> 2) & 3);
n_blocks_len = !!(type & 0x10);
assert(parse_frame_type_gquic_Q040(type) == QUIC_FRAME_ACK);
assert(buf_len >= 1 + n_blocks_len + 1 + largest_obs_len);
READ_UINT(packno, 64, buf + 1 + n_blocks_len + 1, largest_obs_len);
return packno;
}
int
gquic_ietf_parse_ack_frame (const unsigned char *buf, size_t buf_len,
ack_info_t *ack)
{
/* 101NLLMM */
lsquic_packno_t tmp_packno;
const unsigned char type = buf[0];
const unsigned char *p = buf + 1;
const unsigned char *const pend = buf + buf_len;
uint8_t n_blocks, n_ts;
assert((type & 0xE0) == 0xA0); /* We're passed correct frame type */
const int ack_block_len = twobit_to_1248(type & 3); /* MM */
const int largest_obs_len = twobit_to_1248((type >> 2) & 3); /* LL */
if (type & 0x10) { /* N */
CHECK_SPACE(2, p , pend);
n_blocks = *p++;
}
else
{
CHECK_SPACE(1, p , pend);
n_blocks = 0;
}
n_ts = *p++;
const unsigned timestamps_size =
(n_ts > 0) * (1 + 4) + /* Delta LA, First Timestamp */
(n_ts > 1) * (n_ts - 1) * (1 + 2); /* Delta LA, Time Since Previous */
CHECK_SPACE(
largest_obs_len + /* Largest Acknowledged */
2 + /* ACK delay */
ack_block_len + /* First ACK block length */
n_blocks * (1 + ack_block_len) + /* ACK blocks */
timestamps_size
,p , pend);
READ_UINT(ack->ranges[0].high, 64, p, largest_obs_len);
p += largest_obs_len;
ack->lack_delta = gquic_be_read_float_time16(p);
p += 2;
READ_UINT(tmp_packno, 64, p, ack_block_len);
ack->ranges[0].low = ack->ranges[0].high - tmp_packno + 1;
p += ack_block_len;
if (n_blocks)
{
unsigned i, n, gap;
for (i = 0, n = 1, gap = 0; i < n_blocks; ++i)
{
uint64_t length;
gap += *p;
READ_UINT(length, 64, p + 1, ack_block_len);
p += 1 + ack_block_len;
if (length)
{
ack->ranges[n].high = ack->ranges[n - 1].low - gap - 1;
ack->ranges[n].low = ack->ranges[n].high - length + 1;
++n;
gap = 0;
}
}
ack->n_ranges = n;
}
else
ack->n_ranges = 1;
ack->n_timestamps = n_ts;
if (n_ts)
{
#if LSQUIC_PARSE_ACK_TIMESTAMPS
/* TODO */
#else
/* Just skip them for now */
p += timestamps_size;
#endif
}
assert(p <= pend);
return p - (unsigned char *) buf;
}
/* This function makes an assumption that there is at least one range */
int
gquic_ietf_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing)
{
lsquic_packno_t tmp_packno;
const struct lsquic_packno_range *const first = rechist_first(rechist);
if (!first)
{
errno = EINVAL;
return -1;
}
/* Copy values from the first range, because the memory the pointer
* points to may change:
*/
const lsquic_packno_t first_low = first->low, first_high = first->high;
unsigned char *p = outbuf;
unsigned char *const type = p;
unsigned char *const end = p + outbuf_sz;
unsigned char *n_ranges_p;
#define AVAIL() (end - p)
#define CHECKOUT(sz) do { \
if ((intptr_t) (sz) > AVAIL()) { \
errno = ENOBUFS; \
return -1; \
} \
} while (0)
CHECKOUT(1);
++p;
/* 101NLLMM */
*type = 0xA0;
unsigned largest_acked_len, ack_block_len, bits;
/* Calculate largest ACKed len and set `LL' bits: */
const lsquic_packno_t maxno = first_high;
bits = (maxno >= (1ULL << 8))
+ (maxno >= (1ULL << 16))
+ (maxno >= (1ULL << 32));
largest_acked_len = twobit_to_1248(bits);
*type |= bits << 2;
/* Calculate largest ACK block length and set `MM' bits: */
unsigned n_ranges = 0;
lsquic_packno_t maxdiff = 0;
const struct lsquic_packno_range *range;
for (range = rechist_first(rechist); range; range = rechist_next(rechist))
{
++n_ranges;
const lsquic_packno_t diff = range->high - range->low + 1;
if (diff > maxdiff)
maxdiff = diff;
}
bits = (maxdiff >= (1ULL << 8))
+ (maxdiff >= (1ULL << 16))
+ (maxdiff >= (1ULL << 32));
ack_block_len = twobit_to_1248(bits);
*type |= bits;
if (n_ranges > 1)
{
CHECKOUT(2);
*type |= 0x10; /* N */
n_ranges_p = p++; /* Set Num Blocks later */
}
else
{
CHECKOUT(1);
n_ranges_p = NULL;
}
*p++ = 0; /* Do not provide any timestamps. TODO perhaps? */
CHECKOUT(largest_acked_len);
tmp_packno = maxno;
#if __BYTE_ORDER == __LITTLE_ENDIAN
tmp_packno = bswap_64(maxno);
#endif
memcpy(p, (unsigned char *) &tmp_packno + 8 - largest_acked_len,
largest_acked_len);
p += largest_acked_len;
CHECKOUT(2);
lsquic_time_t diff = now - rechist_largest_recv(rechist);
gquic_be_write_float_time16(diff, p);
LSQ_DEBUG("%s: diff: %"PRIu64"; encoded: 0x%04X", __func__, diff,
*(uint16_t*)p);
p += 2;
*has_missing = n_ranges > 1;
if (n_ranges > 1)
{
/* We need to write out at least one range */
CHECKOUT(2 * (1 + ack_block_len));
lsquic_packno_t diff = maxno - first_low + 1;
#if __BYTE_ORDER == __LITTLE_ENDIAN
diff = bswap_64(diff);
#endif
memcpy(p, (unsigned char *) &diff + 8 - ack_block_len,
ack_block_len);
p += ack_block_len;
/* Write out ack blocks until one of the following occurs:
* 1. We run out of intervals.
* 2. We run out of room.
* 3. We run out of highest possible number of ACK blocks (0xFF).
*/
range = rechist_first(rechist);
lsquic_packno_t gap = 0;
n_ranges = 0;
do {
if (0 == gap)
{
const lsquic_packno_t prev_low = range->low;
range = rechist_next(rechist);
if (!range)
break;
gap = prev_low - range->high - 1;
}
if (gap >= 0x100)
{
*p = 0xFF;
gap -= 0xFF;
memset(p + 1, 0, ack_block_len);
}
else
{
*p = gap;
gap = 0;
diff = range->high - range->low + 1;
#if __BYTE_ORDER == __LITTLE_ENDIAN
diff = bswap_64(diff);
#endif
memcpy(p + 1, (unsigned char *) &diff + 8 - ack_block_len,
ack_block_len);
}
p += ack_block_len + 1;
++n_ranges;
} while (n_ranges < 0xFF &&
AVAIL() >= (intptr_t) ack_block_len + 1 + 1 /* timestamp byte */);
*n_ranges_p = n_ranges;
}
else
{
CHECKOUT(ack_block_len);
lsquic_packno_t diff = maxno - first_low + 1;
#if __BYTE_ORDER == __LITTLE_ENDIAN
diff = bswap_64(diff);
#endif
memcpy(p, (unsigned char *) &diff + 8 - ack_block_len, ack_block_len);
p += ack_block_len;
}
return p - (unsigned char *) outbuf;
#undef CHECKOUT
}
const struct parse_funcs lsquic_parse_funcs_gquic_Q040 =
{
.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_ietf_gen_stream_frame,
.pf_calc_stream_frame_header_sz = gquic_ietf_calc_stream_frame_header_sz,
.pf_parse_stream_frame_header_sz = gquic_ietf_parse_stream_frame_header_sz,
.pf_parse_stream_frame = gquic_ietf_parse_stream_frame,
.pf_parse_ack_frame = gquic_ietf_parse_ack_frame,
.pf_parse_ack_high = gquic_ietf_parse_ack_high,
.pf_gen_ack_frame = gquic_ietf_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_ietf_gen_rst_frame,
.pf_parse_rst_frame = gquic_ietf_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_Q040,
};

View file

@ -0,0 +1,947 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_parse_gquic_be.c -- Parsing functions specific to big-endian
* (Q039 and higher) GQUIC.
*/
#include <assert.h>
#include <inttypes.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "lsquic_types.h"
#include "lsquic_alarmset.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_parse.h"
#include "lsquic_rechist.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_mm.h"
#include "lsquic_malo.h"
#include "lsquic_version.h"
#include "lsquic.h"
#include "lsquic_parse_gquic_be.h" /* Include to catch mismatches */
#define LSQUIC_LOGGER_MODULE LSQLM_PARSE
#include "lsquic_logger.h"
/* read 16 bits(2 bytes) time, unit: us */
uint64_t
gquic_be_read_float_time16 (const void *mem)
{
uint16_t val;
READ_UINT(val, 16, mem, 2);
uint64_t temp = val;
uint16_t exp = (temp >> 11) & 0x1F;
if (0 == exp)
return temp;
else
{
--exp;
temp &= 0x7FF;
temp |= 0x800;
return temp << exp;
}
}
void
gquic_be_write_float_time16 (lsquic_time_t time_us, void *mem)
{
uint16_t ret = 0;
uint16_t high, i;
if (time_us < ((uint64_t)1 << 11))
ret = time_us;
else if(time_us > 0x3FFC0000000)
ret = 0xFFFF;
else
{
high = 0;
for (i = 16; i > 0; i /= 2)
{
if (time_us >= (uint64_t)1 << (11 + i))
{
high |= i;
time_us >>= i;
}
}
ret = time_us + (high << 11);
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
ret = bswap_16(ret);
#endif
memcpy(mem, (void *)&ret, 2);
}
/* Parse out packet number */
void
gquic_be_parse_packet_in_finish (lsquic_packet_in_t *packet_in,
struct packin_parse_state *state)
{
lsquic_packno_t packno;
if (state->pps_nbytes)
{
READ_UINT(packno, 64, state->pps_p, state->pps_nbytes);
packet_in->pi_packno = packno;
}
}
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)
{
unsigned packnum_len, header_len;
unsigned char *p;
packnum_len = packno_bits2len(bits);
header_len = 1 + (!!conn_id << 3) + (!!ver << 2) + ((!!nonce) << 5)
+ packnum_len;
if (header_len > bufsz)
{
errno = ENOBUFS;
return -1;
}
p = buf;
*p = (!!conn_id << 3)
| (bits << 4)
| ((!!nonce) << 2)
| !!ver;
++p;
if (conn_id)
{
memcpy(p, conn_id , sizeof(*conn_id));
p += sizeof(*conn_id);
}
if (ver)
{
memcpy(p, ver, 4);
p += 4;
}
if (nonce)
{
memcpy(p, nonce , 32);
p += 32;
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
packno = bswap_64(packno);
#endif
memcpy(p, (unsigned char *) &packno + 8 - packnum_len, packnum_len);
p += packnum_len;
assert(p - buf == (intptr_t) header_len);
return header_len;
}
int
gquic_be_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, gsf_fin_f gsf_fin, gsf_size_f gsf_size,
gsf_read_f gsf_read, void *stream)
{
/* 1fdoooss */
unsigned slen, olen, dlen;
unsigned char *p = buf + 1;
int fin;
/* ss: Stream ID length: 1, 2, 3, or 4 bytes */
slen = (stream_id > 0x0000FF)
+ (stream_id > 0x00FFFF)
+ (stream_id > 0xFFFFFF)
+ 1;
/* ooo: Offset length: 0, 2, 3, 4, 5, 6, 7, or 8 bytes */
olen = (offset >= (1ULL << 56))
+ (offset >= (1ULL << 48))
+ (offset >= (1ULL << 40))
+ (offset >= (1ULL << 32))
+ (offset >= (1ULL << 24))
+ (offset >= (1ULL << 16))
+ ((offset > 0) << 1);
fin = gsf_fin(stream);
if (!fin)
{
unsigned size, n_avail;
uint16_t nr;
size = gsf_size(stream);
n_avail = buf_len - (p + slen + olen - buf);
/* If we cannot fill remaining buffer, we need to include data
* length.
*/
dlen = (size < n_avail) << 1;
n_avail -= dlen;
CHECK_SPACE(1 + olen + slen + dlen +
+ 1 /* We need to write at least 1 byte */, buf, buf + buf_len);
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(p, (unsigned char *) &stream_id + 4 - slen, slen);
p += slen;
#if __BYTE_ORDER == __LITTLE_ENDIAN
offset = bswap_64(offset);
#endif
memcpy(p, (unsigned char *) &offset + 8 - olen, olen);
p += olen;
/* Read as much as we can */
nr = gsf_read(stream, p + dlen, n_avail, &fin);
assert(nr != 0);
if (dlen)
{
uint16_t nr_copy = nr;
#if __BYTE_ORDER == __LITTLE_ENDIAN
nr_copy = bswap_16(nr_copy);
#endif
memcpy(p, &nr_copy, 2);
}
p += dlen + nr;
}
else
{
dlen = 2;
CHECK_SPACE(1 + slen + olen + 2, buf, buf + buf_len);
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(p, (unsigned char *) &stream_id + 4 - slen, slen);
p += slen;
#if __BYTE_ORDER == __LITTLE_ENDIAN
offset = bswap_64(offset);
#endif
memcpy(p, (unsigned char *) &offset + 8 - olen, olen);
p += olen;
memset(p, 0, 2);
p += 2;
}
/* Convert slen to bit representation: 0 - 3: */
slen -= 1;
assert(slen <= 3);
/* Convert olen to bit representation: 0 - 7: */
olen += !olen;
olen -= 1;
assert(olen <= 7);
buf[0] = 0x80
| (fin << 6)
| (dlen << 4)
| (olen << 2)
| slen
;
return p - buf;
}
/* return parsed (used) buffer length */
int
gquic_be_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz,
stream_frame_t *stream_frame)
{
/* 1fdoooss */
const unsigned char *p = buf;
const unsigned char *const pend = p + rem_packet_sz;
CHECK_SPACE(1, p, pend);
const char type = *p++;
const unsigned data_len = (type >> 4) & 2;
const unsigned offset_len = ((type >> 2) & 7) + 1 - !((type >> 2) & 7);
const unsigned stream_id_len = 1 + (type & 3);
const unsigned need = data_len + offset_len + stream_id_len;
CHECK_SPACE(need, p, pend);
memset(stream_frame, 0, sizeof(*stream_frame));
stream_frame->data_frame.df_fin = (type >> 6) & 1;
memcpy((unsigned char *) &stream_frame->stream_id + 4 - stream_id_len, p,
stream_id_len);
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_frame->stream_id = bswap_32(stream_frame->stream_id);
#endif
p += stream_id_len;
memcpy((unsigned char *) &stream_frame->data_frame.df_offset
+ 8 - offset_len, p, offset_len);
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_frame->data_frame.df_offset =
bswap_64(stream_frame->data_frame.df_offset);
#endif
p += offset_len;
if (data_len)
{
memcpy(&stream_frame->data_frame.df_size, p, data_len);
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_frame->data_frame.df_size =
bswap_16(stream_frame->data_frame.df_size);
#endif
p += data_len;
CHECK_SPACE(stream_frame->data_frame.df_size, p, pend);
stream_frame->data_frame.df_data = p;
p += stream_frame->data_frame.df_size;
}
else
{
stream_frame->data_frame.df_size = pend - p;
stream_frame->data_frame.df_data = p;
p = pend;
}
/* From the spec: "A stream frame must always have either non-zero
* data length or the FIN bit set.'
*/
if (!(stream_frame->data_frame.df_size ||
stream_frame->data_frame.df_fin))
return -1;
assert(p <= pend);
return p - (unsigned char *) buf;
}
/* This is a special function: it is used to extract the largest observed
* packet number from ACK frame that we ourselves generated. This allows
* us to skip some checks.
*/
lsquic_packno_t
gquic_be_parse_ack_high (const unsigned char *buf, size_t buf_len)
{
unsigned char type;
unsigned largest_obs_len;
lsquic_packno_t packno;
type = buf[0];
largest_obs_len = twobit_to_1246((type >> 2) & 3);
assert(parse_frame_type_gquic_Q035_thru_Q039(type) == QUIC_FRAME_ACK);
assert(buf_len >= 1 + largest_obs_len);
READ_UINT(packno, 64, buf + 1, largest_obs_len);
return packno;
}
/* Return parsed (used) buffer length.
* If parsing failed, negative value is returned.
*/
int
gquic_be_parse_ack_frame (const unsigned char *buf, size_t buf_len, ack_info_t *ack)
{
/* 01nullmm */
lsquic_packno_t tmp_packno;
const unsigned char type = buf[0];
const unsigned char *p = buf + 1;
const unsigned char *const pend = buf + buf_len;
assert((type & 0xC0) == 0x40); /* We're passed correct frame type */
const int ack_block_len = twobit_to_1246(type & 3); /* mm */
const int largest_obs_len = twobit_to_1246((type >> 2) & 3); /* ll */
CHECK_SPACE(largest_obs_len, p , pend);
READ_UINT(ack->ranges[0].high, 64, p, largest_obs_len);
p += largest_obs_len;
CHECK_SPACE(2, p , pend);
ack->lack_delta = gquic_be_read_float_time16(p);
p += 2;
unsigned n_blocks;
if (type & 0x20)
{
CHECK_SPACE(1, p , pend);
n_blocks = *p;
++p;
}
else
n_blocks = 0;
CHECK_SPACE(ack_block_len, p , pend);
READ_UINT(tmp_packno, 64, p, ack_block_len);
ack->ranges[0].low = ack->ranges[0].high - tmp_packno + 1;
p += ack_block_len;
if (n_blocks)
{
CHECK_SPACE((ack_block_len + 1) * n_blocks, p , pend);
unsigned i, n, gap;
for (i = 0, n = 1, gap = 0; i < n_blocks; ++i)
{
uint64_t length;
gap += *p;
READ_UINT(length, 64, p + 1, ack_block_len);
p += 1 + ack_block_len;
if (length)
{
ack->ranges[n].high = ack->ranges[n - 1].low - gap - 1;
ack->ranges[n].low = ack->ranges[n].high - length + 1;
++n;
gap = 0;
}
}
ack->n_ranges = n;
}
else
ack->n_ranges = 1;
CHECK_SPACE(1, p , pend);
ack->n_timestamps = *p;
++p;
if (ack->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);
CHECK_SPACE(timestamps_size, p, pend);
p += timestamps_size;
#endif
}
assert(p <= pend);
return p - (unsigned char *) buf;
}
int
gquic_be_gen_stop_waiting_frame(unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits bits,
lsquic_packno_t least_unacked_packno)
{
lsquic_packno_t delta;
unsigned packnum_len = packno_bits2len(bits);
if (buf_len >= 1 + packnum_len)
{
*buf = 0x06;
delta = cur_packno - least_unacked_packno;
#if __BYTE_ORDER == __LITTLE_ENDIAN
delta = bswap_64(delta);
#endif
memcpy(buf + 1, (unsigned char *) &delta + 8 - packnum_len,
packnum_len);
return 1 + packnum_len;
}
else
return -1;
}
int
gquic_be_parse_stop_waiting_frame (const unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits bits,
lsquic_packno_t *least_unacked)
{
lsquic_packno_t delta;
unsigned packnum_len = packno_bits2len(bits);
if (buf_len >= 1 + packnum_len)
{
READ_UINT(delta, 64, buf + 1, packnum_len);
*least_unacked = cur_packno - delta;
return 1 + packnum_len;
}
else
return -1;
}
int
gquic_be_skip_stop_waiting_frame (size_t buf_len, enum lsquic_packno_bits bits)
{
unsigned packnum_len = packno_bits2len(bits);
if (buf_len >= 1 + packnum_len)
return 1 + packnum_len;
else
return -1;
}
int
gquic_be_gen_window_update_frame (unsigned char *buf, int buf_len, uint32_t stream_id,
uint64_t offset)
{
if (buf_len < QUIC_WUF_SZ)
return -1;
*buf = 0x04;
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(buf + 1, (unsigned char *) &stream_id, 4);
#if __BYTE_ORDER == __LITTLE_ENDIAN
offset = bswap_64(offset);
#endif
memcpy(buf + 1 + 4, (unsigned char *) &offset, 8);
return QUIC_WUF_SZ;
}
int
gquic_be_parse_window_update_frame (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id, uint64_t *offset)
{
if (buf_len < QUIC_WUF_SZ)
return -1;
READ_UINT(*stream_id, 32, buf + 1, 4);
READ_UINT(*offset, 64, buf + 1 + 4, 8);
return QUIC_WUF_SZ;
}
int
gquic_be_gen_blocked_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id)
{
if (buf_len < QUIC_BLOCKED_FRAME_SZ)
return -1;
*buf = 0x05;
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(buf + 1, &stream_id, 4);
return QUIC_BLOCKED_FRAME_SZ;
}
int
gquic_be_parse_blocked_frame (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id)
{
if (buf_len < QUIC_BLOCKED_FRAME_SZ)
return -1;
READ_UINT(*stream_id, 32, buf + 1, 4);
return QUIC_BLOCKED_FRAME_SZ;
}
int
gquic_be_gen_rst_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, uint32_t error_code)
{
unsigned char *p = buf;
if (buf_len < QUIC_RST_STREAM_SZ)
return -1;
*p = 0x01;
++p;
#if __BYTE_ORDER == __LITTLE_ENDIAN
stream_id = bswap_32(stream_id);
#endif
memcpy(p, &stream_id, 4);
p += 4;
#if __BYTE_ORDER == __LITTLE_ENDIAN
offset = bswap_64(offset);
#endif
memcpy(p, &offset, 8);
p += 8;
#if __BYTE_ORDER == __LITTLE_ENDIAN
error_code = bswap_32(error_code);
#endif
memcpy(p, &error_code, 4);
p += 4;
return p - buf;
}
int
gquic_be_parse_rst_frame (const unsigned char *buf, size_t buf_len, uint32_t *stream_id,
uint64_t *offset, uint32_t *error_code)
{
if (buf_len < QUIC_RST_STREAM_SZ)
return -1;
READ_UINT(*stream_id, 32, buf + 1, 4);
READ_UINT(*offset, 64, buf + 1 + 4, 8);
READ_UINT(*error_code, 32, buf + 1 + 4 + 8, 4);
return QUIC_RST_STREAM_SZ;
}
int
gquic_be_gen_ping_frame (unsigned char *buf, int buf_len)
{
if (buf_len > 0)
{
buf[0] = 0x07;
return 1;
}
else
return -1;
}
int
gquic_be_gen_connect_close_frame (unsigned char *buf, int buf_len, uint32_t error_code,
const char *reason, int reason_len)
{
unsigned char *p = buf;
if (buf_len < 7)
return -1;
*p = 0x02;
++p;
#if __BYTE_ORDER == __LITTLE_ENDIAN
error_code = bswap_32(error_code);
#endif
memcpy(p, &error_code, 4);
p += 4;
#if __BYTE_ORDER == __LITTLE_ENDIAN
const uint16_t copy = bswap_16(reason_len);
memcpy(p, &copy, 2);
#else
memcpy(p, &reason_len, 2);
#endif
p += 2;
memcpy(p, reason, reason_len);
p += reason_len;
if (buf_len < p - buf)
return -2;
return p - buf;
}
int
gquic_be_parse_connect_close_frame (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint16_t *reason_len, uint8_t *reason_offset)
{
if (buf_len < 7)
return -1;
READ_UINT(*error_code, 32, buf + 1, 4);
READ_UINT(*reason_len, 16, buf + 1 + 4, 2);
*reason_offset = 7;
if (buf_len < 7u + *reason_len)
return -2;
return 7 + *reason_len;
}
int
gquic_be_gen_goaway_frame(unsigned char *buf, size_t buf_len, uint32_t error_code,
uint32_t last_good_stream_id, const char *reason,
size_t reason_len)
{
unsigned char *p = buf;
if (buf_len < QUIC_GOAWAY_FRAME_SZ + reason_len)
return -1;
*p = 0x03;
++p;
#if __BYTE_ORDER == __LITTLE_ENDIAN
error_code = bswap_32(error_code);
#endif
memcpy(p, &error_code, 4);
p += 4;
#if __BYTE_ORDER == __LITTLE_ENDIAN
last_good_stream_id = bswap_32(last_good_stream_id);
#endif
memcpy(p, &last_good_stream_id, 4);
p += 4;
#if __BYTE_ORDER == __LITTLE_ENDIAN
uint16_t copy = bswap_16(reason_len);
memcpy(p, &copy, 2);
#else
memcpy(p, &reason_len, 2);
#endif
p += 2;
if (reason_len)
{
memcpy(p, reason, reason_len);
p += reason_len;
}
return p - buf;
}
/* the reason is buf + *reason_offset, length is *reason_length */
int
gquic_be_parse_goaway_frame (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint32_t *last_good_stream_id,
uint16_t *reason_length, const char **reason)
{
if (buf_len < QUIC_GOAWAY_FRAME_SZ)
return -1;
READ_UINT(*error_code, 32, buf + 1, 4);
READ_UINT(*last_good_stream_id, 32, buf + 1 + 4, 4);
READ_UINT(*reason_length, 16, buf + 1 + 4 + 4, 2);
if (*reason_length)
{
if ((int)buf_len < QUIC_GOAWAY_FRAME_SZ + *reason_length)
return -2;
*reason = (const char *) buf + QUIC_GOAWAY_FRAME_SZ;
}
else
*reason = NULL;
return QUIC_GOAWAY_FRAME_SZ + *reason_length;
}
/* Returns number of bytes written or -1 on failure */
/* This function makes an assumption that there is at least one range */
int
gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing)
{
lsquic_packno_t tmp_packno;
const struct lsquic_packno_range *const first = rechist_first(rechist);
if (!first)
{
errno = EINVAL;
return -1;
}
/* Copy values from the first range, because the memory the pointer
* points to may change:
*/
const lsquic_packno_t first_low = first->low, first_high = first->high;
unsigned char *p = outbuf;
unsigned char *const type = p;
unsigned char *const end = p + outbuf_sz;
#define AVAIL() (end - p)
#define CHECKOUT(sz) do { \
if ((intptr_t) (sz) > AVAIL()) { \
errno = ENOBUFS; \
return -1; \
} \
} while (0)
CHECKOUT(1);
++p;
/* 01nullmm */
*type = 0x40;
unsigned largest_acked_len, ack_block_len, bits;
/* Calculate largest ACKed len and set `ll' bits: */
const lsquic_packno_t maxno = first_high;
bits = (maxno >= (1ULL << 8))
+ (maxno >= (1ULL << 16))
+ (maxno >= (1ULL << 32));
largest_acked_len = (1 << bits) - ((maxno >= (1ULL << 32)) << 1);
*type |= bits << 2;
/* Calculate largest ACK block length and set `mm' bits: */
unsigned n_ranges = 0;
lsquic_packno_t maxdiff = 0;
const struct lsquic_packno_range *range;
for (range = rechist_first(rechist); range; range = rechist_next(rechist))
{
++n_ranges;
const lsquic_packno_t diff = range->high - range->low + 1;
if (diff > maxdiff)
maxdiff = diff;
}
bits = (maxdiff >= (1ULL << 8))
+ (maxdiff >= (1ULL << 16))
+ (maxdiff >= (1ULL << 32));
ack_block_len = (1 << bits) - ((maxdiff >= (1ULL << 32)) << 1);
*type |= bits;
CHECKOUT(largest_acked_len);
tmp_packno = maxno;
#if __BYTE_ORDER == __LITTLE_ENDIAN
tmp_packno = bswap_64(maxno);
#endif
memcpy(p, (unsigned char *) &tmp_packno + 8 - largest_acked_len,
largest_acked_len);
p += largest_acked_len;
CHECKOUT(2);
lsquic_time_t diff = now - rechist_largest_recv(rechist);
gquic_be_write_float_time16(diff, p);
LSQ_DEBUG("%s: diff: %"PRIu64"; encoded: 0x%04X", __func__, diff,
*(uint16_t*)p);
p += 2;
if (n_ranges > 1)
{
*has_missing = 1;
*type |= 0x20;
/* We need to write out at least one range */
CHECKOUT(2 * (1 + ack_block_len));
unsigned char *const n_ranges_p = p; /* Set this later */
lsquic_packno_t diff = maxno - first_low + 1;
#if __BYTE_ORDER == __LITTLE_ENDIAN
diff = bswap_64(diff);
#endif
memcpy(p + 1, (unsigned char *) &diff + 8 - ack_block_len,
ack_block_len);
p += ack_block_len + 1;
/* Write out ack blocks until one of the following occurs:
* 1. We run out of intervals.
* 2. We run out of room.
* 3. We run out of highest possible number of ACK blocks (0xFF).
*/
range = rechist_first(rechist);
lsquic_packno_t gap = 0;
n_ranges = 0;
do {
if (0 == gap)
{
const lsquic_packno_t prev_low = range->low;
range = rechist_next(rechist);
if (!range)
break;
gap = prev_low - range->high - 1;
}
if (gap >= 0x100)
{
*p = 0xFF;
gap -= 0xFF;
memset(p + 1, 0, ack_block_len);
}
else
{
*p = gap;
gap = 0;
diff = range->high - range->low + 1;
#if __BYTE_ORDER == __LITTLE_ENDIAN
diff = bswap_64(diff);
#endif
memcpy(p + 1, (unsigned char *) &diff + 8 - ack_block_len,
ack_block_len);
}
p += ack_block_len + 1;
++n_ranges;
} while (n_ranges < 0xFF &&
AVAIL() >= (intptr_t) ack_block_len + 1 + 1 /* timestamp byte */);
*n_ranges_p = n_ranges;
}
else
{
*has_missing = 0;
CHECKOUT(ack_block_len);
lsquic_packno_t diff = maxno - first_low + 1;
#if __BYTE_ORDER == __LITTLE_ENDIAN
diff = bswap_64(diff);
#endif
memcpy(p, (unsigned char *) &diff + 8 - ack_block_len, ack_block_len);
p += ack_block_len;
}
/* We do not generate timestamp list because the reference implementation
* does not use them. When that changes, we will start sending timestamps
* over.
*/
CHECKOUT(1);
*p = 0;
++p;
return p - (unsigned char *) outbuf;
#undef CHECKOUT
}
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,
.pf_calc_stream_frame_header_sz = calc_stream_frame_header_sz_gquic,
.pf_parse_stream_frame_header_sz = parse_stream_frame_header_sz_gquic,
.pf_parse_stream_frame = gquic_be_parse_stream_frame,
.pf_parse_ack_frame = gquic_be_parse_ack_frame,
.pf_parse_ack_high = gquic_be_parse_ack_high,
.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,
};

View file

@ -0,0 +1,135 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_PARSE_GQUIC_BE_H
#define LSQUIC_PARSE_GQUIC_BE_H
/* Header file to make it easy to reference gQUIC parsing functions. This
* is only meant to be used internally. The alternative would be to place
* all gQUIC-big-endian functions -- from all versions -- in a single file,
* 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
#else
#include <byteswap.h>
#endif
#define CHECK_SPACE(need, pstart, pend) \
do { if ((intptr_t) (need) > ((pend) - (pstart))) { return -1; } } while (0)
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define READ_UINT(varname, varwidth, src, nbytes) do { \
varname = 0; \
memcpy((unsigned char *) &varname + varwidth / 8 - (nbytes), (src), \
(nbytes)); \
varname = bswap_##varwidth(varname); \
} while (0)
#else
#define READ_UINT(varname, varwidth, src, nbytes) do { \
varname = 0; \
memcpy((unsigned char *) &varname + varwidth / 8 - (nbytes), (src), \
(nbytes)); \
} while (0)
#endif
uint64_t
gquic_be_read_float_time16 (const void *mem);
void
gquic_be_write_float_time16 (lsquic_time_t time_us, void *mem);
void
gquic_be_parse_packet_in_finish (lsquic_packet_in_t *packet_in,
struct packin_parse_state *state);
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, gsf_fin_f gsf_fin, gsf_size_f gsf_size,
gsf_read_f gsf_read, void *stream);
int
gquic_be_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz,
stream_frame_t *stream_frame);
lsquic_packno_t
gquic_be_parse_ack_high (const unsigned char *buf, size_t buf_len);
int
gquic_be_parse_ack_frame (const unsigned char *buf, size_t buf_len, ack_info_t *ack);
int
gquic_be_gen_stop_waiting_frame(unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits bits,
lsquic_packno_t least_unacked_packno);
int
gquic_be_parse_stop_waiting_frame (const unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits bits,
lsquic_packno_t *least_unacked);
int
gquic_be_skip_stop_waiting_frame (size_t buf_len, enum lsquic_packno_bits bits);
int
gquic_be_gen_window_update_frame (unsigned char *buf, int buf_len, uint32_t stream_id,
uint64_t offset);
int
gquic_be_parse_window_update_frame (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id, uint64_t *offset);
int
gquic_be_gen_blocked_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id);
int
gquic_be_parse_blocked_frame (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id);
int
gquic_be_gen_rst_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, uint32_t error_code);
int
gquic_be_parse_rst_frame (const unsigned char *buf, size_t buf_len, uint32_t *stream_id,
uint64_t *offset, uint32_t *error_code);
int
gquic_be_gen_ping_frame (unsigned char *buf, int buf_len);
int
gquic_be_gen_connect_close_frame (unsigned char *buf, int buf_len, uint32_t error_code,
const char *reason, int reason_len);
int
gquic_be_parse_connect_close_frame (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint16_t *reason_len, uint8_t *reason_offset);
int
gquic_be_gen_goaway_frame(unsigned char *buf, size_t buf_len, uint32_t error_code,
uint32_t last_good_stream_id, const char *reason,
size_t reason_len);
int
gquic_be_parse_goaway_frame (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint32_t *last_good_stream_id,
uint16_t *reason_length, const char **reason);
int
gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing);
#endif

View file

@ -0,0 +1,731 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_parse_gquic_common.c -- Parsing functions common to GQUIC
*/
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include "lsquic_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_parse.h"
#include "lsquic.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PARSE
#include "lsquic_logger.h"
#define CHECK_SPACE(need, pstart, pend) \
do { if ((intptr_t) (need) > ((pend) - (pstart))) { return -1; } } while (0)
/* This partially parses `packet_in' and returns 0 if in case it succeeded and
* -1 on failure.
*
* After this function returns 0, connection ID, nonce, and version fields can
* be examined. To finsh parsing the packet, call version-specific
* 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)
{
int nbytes;
enum PACKET_PUBLIC_FLAGS public_flags;
const unsigned char *p = packet_in->pi_data;
const unsigned char *const pend = packet_in->pi_data + length;
CHECK_SPACE(1, p, pend);
public_flags = *p++;
if (public_flags & PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID)
{
CHECK_SPACE(8, p, pend);
memcpy(&packet_in->pi_conn_id, p, 8);
packet_in->pi_flags |= PI_CONN_ID;
p += 8;
}
if (public_flags & PACKET_PUBLIC_FLAGS_VERSION)
{
/* It seems that version negotiation packets sent by Google may have
* NONCE bit set. Ignore it:
*/
public_flags &= ~PACKET_PUBLIC_FLAGS_NONCE;
if (is_server)
{
CHECK_SPACE(4, p, pend);
packet_in->pi_quic_ver = p - packet_in->pi_data;
p += 4;
}
else
{ /* OK, we have a version negotiation packet. We need to verify
* that it has correct structure. See Section 4.3 of
* [draft-ietf-quic-transport-00].
*/
if ((public_flags & ~(PACKET_PUBLIC_FLAGS_VERSION|
PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID))
|| ((pend - p) & 3))
return -1;
CHECK_SPACE(4, p, pend);
packet_in->pi_quic_ver = p - packet_in->pi_data;
p = pend;
}
}
else
{
/* From [draft-hamilton-quic-transport-protocol-01]:
* 0x40 = MULTIPATH. This bit is reserved for multipath use.
* 0x80 is currently unused, and must be set to 0.
*
* The reference implementation checks that two high bits are not set
* if version flag is not set or if the version is the same. For our
* purposes, all GQUIC version we support so far have these bits set
* to zero.
*/
if (public_flags & (0x80|0x40))
return -1;
packet_in->pi_quic_ver = 0;
}
if (!is_server && (public_flags & PACKET_PUBLIC_FLAGS_NONCE) ==
PACKET_PUBLIC_FLAGS_NONCE)
{
CHECK_SPACE(32, p, pend);
packet_in->pi_nonce = p - packet_in->pi_data;
p += 32;
}
else
packet_in->pi_nonce = 0;
state->pps_p = p;
packet_in->pi_packno = 0;
if (0 == (public_flags & (PACKET_PUBLIC_FLAGS_VERSION|PACKET_PUBLIC_FLAGS_RST))
|| ((public_flags & PACKET_PUBLIC_FLAGS_VERSION) && is_server))
{
nbytes = twobit_to_1246((public_flags >> 4) & 3);
CHECK_SPACE(nbytes, p, pend);
p += nbytes;
state->pps_nbytes = nbytes;
}
else
state->pps_nbytes = 0;
packet_in->pi_header_sz = p - packet_in->pi_data;
packet_in->pi_frame_types = 0;
memset(&packet_in->pi_next, 0, sizeof(packet_in->pi_next));
packet_in->pi_data_sz = length;
packet_in->pi_refcnt = 0;
packet_in->pi_received = 0;
return 0;
}
static const enum QUIC_FRAME_TYPE byte2frame_type_Q035_thru_Q039[0x100] =
{
[0x00] = QUIC_FRAME_PADDING,
[0x01] = QUIC_FRAME_RST_STREAM,
[0x02] = QUIC_FRAME_CONNECTION_CLOSE,
[0x03] = QUIC_FRAME_GOAWAY,
[0x04] = QUIC_FRAME_WINDOW_UPDATE,
[0x05] = QUIC_FRAME_BLOCKED,
[0x06] = QUIC_FRAME_STOP_WAITING,
[0x07] = QUIC_FRAME_PING,
[0x08] = QUIC_FRAME_INVALID,
[0x09] = QUIC_FRAME_INVALID,
[0x0A] = QUIC_FRAME_INVALID,
[0x0B] = QUIC_FRAME_INVALID,
[0x0C] = QUIC_FRAME_INVALID,
[0x0D] = QUIC_FRAME_INVALID,
[0x0E] = QUIC_FRAME_INVALID,
[0x0F] = QUIC_FRAME_INVALID,
[0x10] = QUIC_FRAME_INVALID,
[0x11] = QUIC_FRAME_INVALID,
[0x12] = QUIC_FRAME_INVALID,
[0x13] = QUIC_FRAME_INVALID,
[0x14] = QUIC_FRAME_INVALID,
[0x15] = QUIC_FRAME_INVALID,
[0x16] = QUIC_FRAME_INVALID,
[0x17] = QUIC_FRAME_INVALID,
[0x18] = QUIC_FRAME_INVALID,
[0x19] = QUIC_FRAME_INVALID,
[0x1A] = QUIC_FRAME_INVALID,
[0x1B] = QUIC_FRAME_INVALID,
[0x1C] = QUIC_FRAME_INVALID,
[0x1D] = QUIC_FRAME_INVALID,
[0x1E] = QUIC_FRAME_INVALID,
[0x1F] = QUIC_FRAME_INVALID,
[0x20] = QUIC_FRAME_INVALID,
[0x21] = QUIC_FRAME_INVALID,
[0x22] = QUIC_FRAME_INVALID,
[0x23] = QUIC_FRAME_INVALID,
[0x24] = QUIC_FRAME_INVALID,
[0x25] = QUIC_FRAME_INVALID,
[0x26] = QUIC_FRAME_INVALID,
[0x27] = QUIC_FRAME_INVALID,
[0x28] = QUIC_FRAME_INVALID,
[0x29] = QUIC_FRAME_INVALID,
[0x2A] = QUIC_FRAME_INVALID,
[0x2B] = QUIC_FRAME_INVALID,
[0x2C] = QUIC_FRAME_INVALID,
[0x2D] = QUIC_FRAME_INVALID,
[0x2E] = QUIC_FRAME_INVALID,
[0x2F] = QUIC_FRAME_INVALID,
[0x30] = QUIC_FRAME_INVALID,
[0x31] = QUIC_FRAME_INVALID,
[0x32] = QUIC_FRAME_INVALID,
[0x33] = QUIC_FRAME_INVALID,
[0x34] = QUIC_FRAME_INVALID,
[0x35] = QUIC_FRAME_INVALID,
[0x36] = QUIC_FRAME_INVALID,
[0x37] = QUIC_FRAME_INVALID,
[0x38] = QUIC_FRAME_INVALID,
[0x39] = QUIC_FRAME_INVALID,
[0x3A] = QUIC_FRAME_INVALID,
[0x3B] = QUIC_FRAME_INVALID,
[0x3C] = QUIC_FRAME_INVALID,
[0x3D] = QUIC_FRAME_INVALID,
[0x3E] = QUIC_FRAME_INVALID,
[0x3F] = QUIC_FRAME_INVALID,
[0x40] = QUIC_FRAME_ACK,
[0x41] = QUIC_FRAME_ACK,
[0x42] = QUIC_FRAME_ACK,
[0x43] = QUIC_FRAME_ACK,
[0x44] = QUIC_FRAME_ACK,
[0x45] = QUIC_FRAME_ACK,
[0x46] = QUIC_FRAME_ACK,
[0x47] = QUIC_FRAME_ACK,
[0x48] = QUIC_FRAME_ACK,
[0x49] = QUIC_FRAME_ACK,
[0x4A] = QUIC_FRAME_ACK,
[0x4B] = QUIC_FRAME_ACK,
[0x4C] = QUIC_FRAME_ACK,
[0x4D] = QUIC_FRAME_ACK,
[0x4E] = QUIC_FRAME_ACK,
[0x4F] = QUIC_FRAME_ACK,
[0x50] = QUIC_FRAME_ACK,
[0x51] = QUIC_FRAME_ACK,
[0x52] = QUIC_FRAME_ACK,
[0x53] = QUIC_FRAME_ACK,
[0x54] = QUIC_FRAME_ACK,
[0x55] = QUIC_FRAME_ACK,
[0x56] = QUIC_FRAME_ACK,
[0x57] = QUIC_FRAME_ACK,
[0x58] = QUIC_FRAME_ACK,
[0x59] = QUIC_FRAME_ACK,
[0x5A] = QUIC_FRAME_ACK,
[0x5B] = QUIC_FRAME_ACK,
[0x5C] = QUIC_FRAME_ACK,
[0x5D] = QUIC_FRAME_ACK,
[0x5E] = QUIC_FRAME_ACK,
[0x5F] = QUIC_FRAME_ACK,
[0x60] = QUIC_FRAME_ACK,
[0x61] = QUIC_FRAME_ACK,
[0x62] = QUIC_FRAME_ACK,
[0x63] = QUIC_FRAME_ACK,
[0x64] = QUIC_FRAME_ACK,
[0x65] = QUIC_FRAME_ACK,
[0x66] = QUIC_FRAME_ACK,
[0x67] = QUIC_FRAME_ACK,
[0x68] = QUIC_FRAME_ACK,
[0x69] = QUIC_FRAME_ACK,
[0x6A] = QUIC_FRAME_ACK,
[0x6B] = QUIC_FRAME_ACK,
[0x6C] = QUIC_FRAME_ACK,
[0x6D] = QUIC_FRAME_ACK,
[0x6E] = QUIC_FRAME_ACK,
[0x6F] = QUIC_FRAME_ACK,
[0x70] = QUIC_FRAME_ACK,
[0x71] = QUIC_FRAME_ACK,
[0x72] = QUIC_FRAME_ACK,
[0x73] = QUIC_FRAME_ACK,
[0x74] = QUIC_FRAME_ACK,
[0x75] = QUIC_FRAME_ACK,
[0x76] = QUIC_FRAME_ACK,
[0x77] = QUIC_FRAME_ACK,
[0x78] = QUIC_FRAME_ACK,
[0x79] = QUIC_FRAME_ACK,
[0x7A] = QUIC_FRAME_ACK,
[0x7B] = QUIC_FRAME_ACK,
[0x7C] = QUIC_FRAME_ACK,
[0x7D] = QUIC_FRAME_ACK,
[0x7E] = QUIC_FRAME_ACK,
[0x7F] = QUIC_FRAME_ACK,
[0x80] = QUIC_FRAME_STREAM,
[0x81] = QUIC_FRAME_STREAM,
[0x82] = QUIC_FRAME_STREAM,
[0x83] = QUIC_FRAME_STREAM,
[0x84] = QUIC_FRAME_STREAM,
[0x85] = QUIC_FRAME_STREAM,
[0x86] = QUIC_FRAME_STREAM,
[0x87] = QUIC_FRAME_STREAM,
[0x88] = QUIC_FRAME_STREAM,
[0x89] = QUIC_FRAME_STREAM,
[0x8A] = QUIC_FRAME_STREAM,
[0x8B] = QUIC_FRAME_STREAM,
[0x8C] = QUIC_FRAME_STREAM,
[0x8D] = QUIC_FRAME_STREAM,
[0x8E] = QUIC_FRAME_STREAM,
[0x8F] = QUIC_FRAME_STREAM,
[0x90] = QUIC_FRAME_STREAM,
[0x91] = QUIC_FRAME_STREAM,
[0x92] = QUIC_FRAME_STREAM,
[0x93] = QUIC_FRAME_STREAM,
[0x94] = QUIC_FRAME_STREAM,
[0x95] = QUIC_FRAME_STREAM,
[0x96] = QUIC_FRAME_STREAM,
[0x97] = QUIC_FRAME_STREAM,
[0x98] = QUIC_FRAME_STREAM,
[0x99] = QUIC_FRAME_STREAM,
[0x9A] = QUIC_FRAME_STREAM,
[0x9B] = QUIC_FRAME_STREAM,
[0x9C] = QUIC_FRAME_STREAM,
[0x9D] = QUIC_FRAME_STREAM,
[0x9E] = QUIC_FRAME_STREAM,
[0x9F] = QUIC_FRAME_STREAM,
[0xA0] = QUIC_FRAME_STREAM,
[0xA1] = QUIC_FRAME_STREAM,
[0xA2] = QUIC_FRAME_STREAM,
[0xA3] = QUIC_FRAME_STREAM,
[0xA4] = QUIC_FRAME_STREAM,
[0xA5] = QUIC_FRAME_STREAM,
[0xA6] = QUIC_FRAME_STREAM,
[0xA7] = QUIC_FRAME_STREAM,
[0xA8] = QUIC_FRAME_STREAM,
[0xA9] = QUIC_FRAME_STREAM,
[0xAA] = QUIC_FRAME_STREAM,
[0xAB] = QUIC_FRAME_STREAM,
[0xAC] = QUIC_FRAME_STREAM,
[0xAD] = QUIC_FRAME_STREAM,
[0xAE] = QUIC_FRAME_STREAM,
[0xAF] = QUIC_FRAME_STREAM,
[0xB0] = QUIC_FRAME_STREAM,
[0xB1] = QUIC_FRAME_STREAM,
[0xB2] = QUIC_FRAME_STREAM,
[0xB3] = QUIC_FRAME_STREAM,
[0xB4] = QUIC_FRAME_STREAM,
[0xB5] = QUIC_FRAME_STREAM,
[0xB6] = QUIC_FRAME_STREAM,
[0xB7] = QUIC_FRAME_STREAM,
[0xB8] = QUIC_FRAME_STREAM,
[0xB9] = QUIC_FRAME_STREAM,
[0xBA] = QUIC_FRAME_STREAM,
[0xBB] = QUIC_FRAME_STREAM,
[0xBC] = QUIC_FRAME_STREAM,
[0xBD] = QUIC_FRAME_STREAM,
[0xBE] = QUIC_FRAME_STREAM,
[0xBF] = QUIC_FRAME_STREAM,
[0xC0] = QUIC_FRAME_STREAM,
[0xC1] = QUIC_FRAME_STREAM,
[0xC2] = QUIC_FRAME_STREAM,
[0xC3] = QUIC_FRAME_STREAM,
[0xC4] = QUIC_FRAME_STREAM,
[0xC5] = QUIC_FRAME_STREAM,
[0xC6] = QUIC_FRAME_STREAM,
[0xC7] = QUIC_FRAME_STREAM,
[0xC8] = QUIC_FRAME_STREAM,
[0xC9] = QUIC_FRAME_STREAM,
[0xCA] = QUIC_FRAME_STREAM,
[0xCB] = QUIC_FRAME_STREAM,
[0xCC] = QUIC_FRAME_STREAM,
[0xCD] = QUIC_FRAME_STREAM,
[0xCE] = QUIC_FRAME_STREAM,
[0xCF] = QUIC_FRAME_STREAM,
[0xD0] = QUIC_FRAME_STREAM,
[0xD1] = QUIC_FRAME_STREAM,
[0xD2] = QUIC_FRAME_STREAM,
[0xD3] = QUIC_FRAME_STREAM,
[0xD4] = QUIC_FRAME_STREAM,
[0xD5] = QUIC_FRAME_STREAM,
[0xD6] = QUIC_FRAME_STREAM,
[0xD7] = QUIC_FRAME_STREAM,
[0xD8] = QUIC_FRAME_STREAM,
[0xD9] = QUIC_FRAME_STREAM,
[0xDA] = QUIC_FRAME_STREAM,
[0xDB] = QUIC_FRAME_STREAM,
[0xDC] = QUIC_FRAME_STREAM,
[0xDD] = QUIC_FRAME_STREAM,
[0xDE] = QUIC_FRAME_STREAM,
[0xDF] = QUIC_FRAME_STREAM,
[0xE0] = QUIC_FRAME_STREAM,
[0xE1] = QUIC_FRAME_STREAM,
[0xE2] = QUIC_FRAME_STREAM,
[0xE3] = QUIC_FRAME_STREAM,
[0xE4] = QUIC_FRAME_STREAM,
[0xE5] = QUIC_FRAME_STREAM,
[0xE6] = QUIC_FRAME_STREAM,
[0xE7] = QUIC_FRAME_STREAM,
[0xE8] = QUIC_FRAME_STREAM,
[0xE9] = QUIC_FRAME_STREAM,
[0xEA] = QUIC_FRAME_STREAM,
[0xEB] = QUIC_FRAME_STREAM,
[0xEC] = QUIC_FRAME_STREAM,
[0xED] = QUIC_FRAME_STREAM,
[0xEE] = QUIC_FRAME_STREAM,
[0xEF] = QUIC_FRAME_STREAM,
[0xF0] = QUIC_FRAME_STREAM,
[0xF1] = QUIC_FRAME_STREAM,
[0xF2] = QUIC_FRAME_STREAM,
[0xF3] = QUIC_FRAME_STREAM,
[0xF4] = QUIC_FRAME_STREAM,
[0xF5] = QUIC_FRAME_STREAM,
[0xF6] = QUIC_FRAME_STREAM,
[0xF7] = QUIC_FRAME_STREAM,
[0xF8] = QUIC_FRAME_STREAM,
[0xF9] = QUIC_FRAME_STREAM,
[0xFA] = QUIC_FRAME_STREAM,
[0xFB] = QUIC_FRAME_STREAM,
[0xFC] = QUIC_FRAME_STREAM,
[0xFD] = QUIC_FRAME_STREAM,
[0xFE] = QUIC_FRAME_STREAM,
[0xFF] = QUIC_FRAME_STREAM,
};
static const enum QUIC_FRAME_TYPE byte2frame_type_Q040[0x100] =
{
[0x00] = QUIC_FRAME_PADDING,
[0x01] = QUIC_FRAME_RST_STREAM,
[0x02] = QUIC_FRAME_CONNECTION_CLOSE,
[0x03] = QUIC_FRAME_GOAWAY,
[0x04] = QUIC_FRAME_WINDOW_UPDATE,
[0x05] = QUIC_FRAME_BLOCKED,
[0x06] = QUIC_FRAME_STOP_WAITING,
[0x07] = QUIC_FRAME_PING,
[0x08] = QUIC_FRAME_INVALID,
[0x09] = QUIC_FRAME_INVALID,
[0x0A] = QUIC_FRAME_INVALID,
[0x0B] = QUIC_FRAME_INVALID,
[0x0C] = QUIC_FRAME_INVALID,
[0x0D] = QUIC_FRAME_INVALID,
[0x0E] = QUIC_FRAME_INVALID,
[0x0F] = QUIC_FRAME_INVALID,
[0x10] = QUIC_FRAME_INVALID,
[0x11] = QUIC_FRAME_INVALID,
[0x12] = QUIC_FRAME_INVALID,
[0x13] = QUIC_FRAME_INVALID,
[0x14] = QUIC_FRAME_INVALID,
[0x15] = QUIC_FRAME_INVALID,
[0x16] = QUIC_FRAME_INVALID,
[0x17] = QUIC_FRAME_INVALID,
[0x18] = QUIC_FRAME_INVALID,
[0x19] = QUIC_FRAME_INVALID,
[0x1A] = QUIC_FRAME_INVALID,
[0x1B] = QUIC_FRAME_INVALID,
[0x1C] = QUIC_FRAME_INVALID,
[0x1D] = QUIC_FRAME_INVALID,
[0x1E] = QUIC_FRAME_INVALID,
[0x1F] = QUIC_FRAME_INVALID,
[0x20] = QUIC_FRAME_INVALID,
[0x21] = QUIC_FRAME_INVALID,
[0x22] = QUIC_FRAME_INVALID,
[0x23] = QUIC_FRAME_INVALID,
[0x24] = QUIC_FRAME_INVALID,
[0x25] = QUIC_FRAME_INVALID,
[0x26] = QUIC_FRAME_INVALID,
[0x27] = QUIC_FRAME_INVALID,
[0x28] = QUIC_FRAME_INVALID,
[0x29] = QUIC_FRAME_INVALID,
[0x2A] = QUIC_FRAME_INVALID,
[0x2B] = QUIC_FRAME_INVALID,
[0x2C] = QUIC_FRAME_INVALID,
[0x2D] = QUIC_FRAME_INVALID,
[0x2E] = QUIC_FRAME_INVALID,
[0x2F] = QUIC_FRAME_INVALID,
[0x30] = QUIC_FRAME_INVALID,
[0x31] = QUIC_FRAME_INVALID,
[0x32] = QUIC_FRAME_INVALID,
[0x33] = QUIC_FRAME_INVALID,
[0x34] = QUIC_FRAME_INVALID,
[0x35] = QUIC_FRAME_INVALID,
[0x36] = QUIC_FRAME_INVALID,
[0x37] = QUIC_FRAME_INVALID,
[0x38] = QUIC_FRAME_INVALID,
[0x39] = QUIC_FRAME_INVALID,
[0x3A] = QUIC_FRAME_INVALID,
[0x3B] = QUIC_FRAME_INVALID,
[0x3C] = QUIC_FRAME_INVALID,
[0x3D] = QUIC_FRAME_INVALID,
[0x3E] = QUIC_FRAME_INVALID,
[0x3F] = QUIC_FRAME_INVALID,
[0x40] = QUIC_FRAME_INVALID,
[0x41] = QUIC_FRAME_INVALID,
[0x42] = QUIC_FRAME_INVALID,
[0x43] = QUIC_FRAME_INVALID,
[0x44] = QUIC_FRAME_INVALID,
[0x45] = QUIC_FRAME_INVALID,
[0x46] = QUIC_FRAME_INVALID,
[0x47] = QUIC_FRAME_INVALID,
[0x48] = QUIC_FRAME_INVALID,
[0x49] = QUIC_FRAME_INVALID,
[0x4A] = QUIC_FRAME_INVALID,
[0x4B] = QUIC_FRAME_INVALID,
[0x4C] = QUIC_FRAME_INVALID,
[0x4D] = QUIC_FRAME_INVALID,
[0x4E] = QUIC_FRAME_INVALID,
[0x4F] = QUIC_FRAME_INVALID,
[0x50] = QUIC_FRAME_INVALID,
[0x51] = QUIC_FRAME_INVALID,
[0x52] = QUIC_FRAME_INVALID,
[0x53] = QUIC_FRAME_INVALID,
[0x54] = QUIC_FRAME_INVALID,
[0x55] = QUIC_FRAME_INVALID,
[0x56] = QUIC_FRAME_INVALID,
[0x57] = QUIC_FRAME_INVALID,
[0x58] = QUIC_FRAME_INVALID,
[0x59] = QUIC_FRAME_INVALID,
[0x5A] = QUIC_FRAME_INVALID,
[0x5B] = QUIC_FRAME_INVALID,
[0x5C] = QUIC_FRAME_INVALID,
[0x5D] = QUIC_FRAME_INVALID,
[0x5E] = QUIC_FRAME_INVALID,
[0x5F] = QUIC_FRAME_INVALID,
[0x60] = QUIC_FRAME_INVALID,
[0x61] = QUIC_FRAME_INVALID,
[0x62] = QUIC_FRAME_INVALID,
[0x63] = QUIC_FRAME_INVALID,
[0x64] = QUIC_FRAME_INVALID,
[0x65] = QUIC_FRAME_INVALID,
[0x66] = QUIC_FRAME_INVALID,
[0x67] = QUIC_FRAME_INVALID,
[0x68] = QUIC_FRAME_INVALID,
[0x69] = QUIC_FRAME_INVALID,
[0x6A] = QUIC_FRAME_INVALID,
[0x6B] = QUIC_FRAME_INVALID,
[0x6C] = QUIC_FRAME_INVALID,
[0x6D] = QUIC_FRAME_INVALID,
[0x6E] = QUIC_FRAME_INVALID,
[0x6F] = QUIC_FRAME_INVALID,
[0x70] = QUIC_FRAME_INVALID,
[0x71] = QUIC_FRAME_INVALID,
[0x72] = QUIC_FRAME_INVALID,
[0x73] = QUIC_FRAME_INVALID,
[0x74] = QUIC_FRAME_INVALID,
[0x75] = QUIC_FRAME_INVALID,
[0x76] = QUIC_FRAME_INVALID,
[0x77] = QUIC_FRAME_INVALID,
[0x78] = QUIC_FRAME_INVALID,
[0x79] = QUIC_FRAME_INVALID,
[0x7A] = QUIC_FRAME_INVALID,
[0x7B] = QUIC_FRAME_INVALID,
[0x7C] = QUIC_FRAME_INVALID,
[0x7D] = QUIC_FRAME_INVALID,
[0x7E] = QUIC_FRAME_INVALID,
[0x7F] = QUIC_FRAME_INVALID,
[0x80] = QUIC_FRAME_INVALID,
[0x81] = QUIC_FRAME_INVALID,
[0x82] = QUIC_FRAME_INVALID,
[0x83] = QUIC_FRAME_INVALID,
[0x84] = QUIC_FRAME_INVALID,
[0x85] = QUIC_FRAME_INVALID,
[0x86] = QUIC_FRAME_INVALID,
[0x87] = QUIC_FRAME_INVALID,
[0x88] = QUIC_FRAME_INVALID,
[0x89] = QUIC_FRAME_INVALID,
[0x8A] = QUIC_FRAME_INVALID,
[0x8B] = QUIC_FRAME_INVALID,
[0x8C] = QUIC_FRAME_INVALID,
[0x8D] = QUIC_FRAME_INVALID,
[0x8E] = QUIC_FRAME_INVALID,
[0x8F] = QUIC_FRAME_INVALID,
[0x90] = QUIC_FRAME_INVALID,
[0x91] = QUIC_FRAME_INVALID,
[0x92] = QUIC_FRAME_INVALID,
[0x93] = QUIC_FRAME_INVALID,
[0x94] = QUIC_FRAME_INVALID,
[0x95] = QUIC_FRAME_INVALID,
[0x96] = QUIC_FRAME_INVALID,
[0x97] = QUIC_FRAME_INVALID,
[0x98] = QUIC_FRAME_INVALID,
[0x99] = QUIC_FRAME_INVALID,
[0x9A] = QUIC_FRAME_INVALID,
[0x9B] = QUIC_FRAME_INVALID,
[0x9C] = QUIC_FRAME_INVALID,
[0x9D] = QUIC_FRAME_INVALID,
[0x9E] = QUIC_FRAME_INVALID,
[0x9F] = QUIC_FRAME_INVALID,
[0xA0] = QUIC_FRAME_ACK,
[0xA1] = QUIC_FRAME_ACK,
[0xA2] = QUIC_FRAME_ACK,
[0xA3] = QUIC_FRAME_ACK,
[0xA4] = QUIC_FRAME_ACK,
[0xA5] = QUIC_FRAME_ACK,
[0xA6] = QUIC_FRAME_ACK,
[0xA7] = QUIC_FRAME_ACK,
[0xA8] = QUIC_FRAME_ACK,
[0xA9] = QUIC_FRAME_ACK,
[0xAA] = QUIC_FRAME_ACK,
[0xAB] = QUIC_FRAME_ACK,
[0xAC] = QUIC_FRAME_ACK,
[0xAD] = QUIC_FRAME_ACK,
[0xAE] = QUIC_FRAME_ACK,
[0xAF] = QUIC_FRAME_ACK,
[0xB0] = QUIC_FRAME_ACK,
[0xB1] = QUIC_FRAME_ACK,
[0xB2] = QUIC_FRAME_ACK,
[0xB3] = QUIC_FRAME_ACK,
[0xB4] = QUIC_FRAME_ACK,
[0xB5] = QUIC_FRAME_ACK,
[0xB6] = QUIC_FRAME_ACK,
[0xB7] = QUIC_FRAME_ACK,
[0xB8] = QUIC_FRAME_ACK,
[0xB9] = QUIC_FRAME_ACK,
[0xBA] = QUIC_FRAME_ACK,
[0xBB] = QUIC_FRAME_ACK,
[0xBC] = QUIC_FRAME_ACK,
[0xBD] = QUIC_FRAME_ACK,
[0xBE] = QUIC_FRAME_ACK,
[0xBF] = QUIC_FRAME_ACK,
[0xC0] = QUIC_FRAME_STREAM,
[0xC1] = QUIC_FRAME_STREAM,
[0xC2] = QUIC_FRAME_STREAM,
[0xC3] = QUIC_FRAME_STREAM,
[0xC4] = QUIC_FRAME_STREAM,
[0xC5] = QUIC_FRAME_STREAM,
[0xC6] = QUIC_FRAME_STREAM,
[0xC7] = QUIC_FRAME_STREAM,
[0xC8] = QUIC_FRAME_STREAM,
[0xC9] = QUIC_FRAME_STREAM,
[0xCA] = QUIC_FRAME_STREAM,
[0xCB] = QUIC_FRAME_STREAM,
[0xCC] = QUIC_FRAME_STREAM,
[0xCD] = QUIC_FRAME_STREAM,
[0xCE] = QUIC_FRAME_STREAM,
[0xCF] = QUIC_FRAME_STREAM,
[0xD0] = QUIC_FRAME_STREAM,
[0xD1] = QUIC_FRAME_STREAM,
[0xD2] = QUIC_FRAME_STREAM,
[0xD3] = QUIC_FRAME_STREAM,
[0xD4] = QUIC_FRAME_STREAM,
[0xD5] = QUIC_FRAME_STREAM,
[0xD6] = QUIC_FRAME_STREAM,
[0xD7] = QUIC_FRAME_STREAM,
[0xD8] = QUIC_FRAME_STREAM,
[0xD9] = QUIC_FRAME_STREAM,
[0xDA] = QUIC_FRAME_STREAM,
[0xDB] = QUIC_FRAME_STREAM,
[0xDC] = QUIC_FRAME_STREAM,
[0xDD] = QUIC_FRAME_STREAM,
[0xDE] = QUIC_FRAME_STREAM,
[0xDF] = QUIC_FRAME_STREAM,
[0xE0] = QUIC_FRAME_STREAM,
[0xE1] = QUIC_FRAME_STREAM,
[0xE2] = QUIC_FRAME_STREAM,
[0xE3] = QUIC_FRAME_STREAM,
[0xE4] = QUIC_FRAME_STREAM,
[0xE5] = QUIC_FRAME_STREAM,
[0xE6] = QUIC_FRAME_STREAM,
[0xE7] = QUIC_FRAME_STREAM,
[0xE8] = QUIC_FRAME_STREAM,
[0xE9] = QUIC_FRAME_STREAM,
[0xEA] = QUIC_FRAME_STREAM,
[0xEB] = QUIC_FRAME_STREAM,
[0xEC] = QUIC_FRAME_STREAM,
[0xED] = QUIC_FRAME_STREAM,
[0xEE] = QUIC_FRAME_STREAM,
[0xEF] = QUIC_FRAME_STREAM,
[0xF0] = QUIC_FRAME_STREAM,
[0xF1] = QUIC_FRAME_STREAM,
[0xF2] = QUIC_FRAME_STREAM,
[0xF3] = QUIC_FRAME_STREAM,
[0xF4] = QUIC_FRAME_STREAM,
[0xF5] = QUIC_FRAME_STREAM,
[0xF6] = QUIC_FRAME_STREAM,
[0xF7] = QUIC_FRAME_STREAM,
[0xF8] = QUIC_FRAME_STREAM,
[0xF9] = QUIC_FRAME_STREAM,
[0xFA] = QUIC_FRAME_STREAM,
[0xFB] = QUIC_FRAME_STREAM,
[0xFC] = QUIC_FRAME_STREAM,
[0xFD] = QUIC_FRAME_STREAM,
[0xFE] = QUIC_FRAME_STREAM,
[0xFF] = QUIC_FRAME_STREAM,
};
enum QUIC_FRAME_TYPE
parse_frame_type_gquic_Q035_thru_Q039 (unsigned char b)
{
return byte2frame_type_Q035_thru_Q039[b];
}
enum QUIC_FRAME_TYPE
parse_frame_type_gquic_Q040 (unsigned char b)
{
return byte2frame_type_Q040[b];
}
unsigned
parse_stream_frame_header_sz_gquic (unsigned char type)
{
const unsigned data_len = (type >> 4) & 2;
const unsigned offset_len = ((type >> 2) & 7) + 1 - !((type >> 2) & 7);
const unsigned stream_id_len = 1 + (type & 3);
return 1 + data_len + offset_len + stream_id_len;
}
size_t
calc_stream_frame_header_sz_gquic (uint32_t stream_id, uint64_t offset)
{
return
/* Type */
1
/* Stream ID length */
+ ((stream_id) > 0x0000FF)
+ ((stream_id) > 0x00FFFF)
+ ((stream_id) > 0xFFFFFF)
+ 1
/* Offset length */
+ ((offset) >= (1ULL << 56))
+ ((offset) >= (1ULL << 48))
+ ((offset) >= (1ULL << 40))
+ ((offset) >= (1ULL << 32))
+ ((offset) >= (1ULL << 24))
+ ((offset) >= (1ULL << 16))
+ (((offset) > 0) << 1)
/* Add data length (2) yourself, if necessary */
;
}
char *
acki2str (const struct ack_info *acki, size_t *sz)
{
size_t off, bufsz, nw;
unsigned n;
char *buf;
bufsz = acki->n_ranges * (3 /* [-] */ + 20 /* ~0ULL */ * 2);
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)
{
nw = snprintf(buf + off, bufsz - off, "[%"PRIu64"-%"PRIu64"]",
acki->ranges[n].high, acki->ranges[n].low);
if (nw > bufsz - off)
break;
off += nw;
}
*sz = off;
return buf;
}

View file

@ -0,0 +1,878 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_parse_gquic_le.c -- Parsing functions specific to little-endian
* (Q038 and lower) GQUIC.
*/
#include <assert.h>
#include <inttypes.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "lsquic_types.h"
#include "lsquic_alarmset.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_parse.h"
#include "lsquic_rechist.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_mm.h"
#include "lsquic_malo.h"
#include "lsquic_version.h"
#include "lsquic.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PARSE
#include "lsquic_logger.h"
#define CHECK_SPACE(need, pstart, pend) \
do { if ((intptr_t) (need) > ((pend) - (pstart))) { return -1; } } while (0)
/* get the len bytes as a uint64_t */
static uint64_t get_vary_len_int64(const void *mem, uint8_t len)
{
assert(len <= 8);
uint64_t v = 0;
memcpy(&v, mem, len);
return v;
}
static void write_vary_len_to_buf(uint64_t v, unsigned char *mem, uint8_t len)
{
assert(len <= 8);
memcpy(mem, &v, len);
}
/* read 16 bits(2 bytes) time, unit: us */
static uint64_t
gquic_le_read_float_time16 (const void *mem)
{
uint64_t temp = get_vary_len_int64(mem, 2);
uint16_t exp = (temp >> 11) & 0x1F;
if (0 == exp)
return temp;
else
{
--exp;
temp &= 0x7FF;
temp |= 0x800;
return temp << exp;
}
}
static void
gquic_le_write_float_time16 (lsquic_time_t time_us, void *mem)
{
uint16_t ret = 0;
uint16_t high, i;
if (time_us < ((uint64_t)1 << 11))
ret = time_us;
else if(time_us > 0x3FFC0000000)
ret = 0xFFFF;
else
{
high = 0;
for (i = 16; i > 0; i /= 2)
{
if (time_us >= (uint64_t)1 << (11 + i))
{
high |= i;
time_us >>= i;
}
}
ret = time_us + (high << 11);
}
memcpy(mem, (void *)&ret, 2);
}
static int packet_length_arr[] = {1, 2, 4, 6};
/* make sure the flag is lower 2 bits set for this checking */
static int
flag_to_pkt_num_len(unsigned char flag)
{
return packet_length_arr[flag & 0x03];
}
/* Parse out packet number */
static void
gquic_le_parse_packet_in_finish (lsquic_packet_in_t *packet_in,
struct packin_parse_state *state)
{
if (state->pps_nbytes)
memcpy((void *)&packet_in->pi_packno, state->pps_p, state->pps_nbytes);
}
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)
{
unsigned packnum_len, header_len;
unsigned char *p;
packnum_len = packno_bits2len(bits);
header_len = 1 + (!!conn_id << 3) + (!!ver << 2) + ((!!nonce) << 5)
+ packnum_len;
if (header_len > bufsz)
{
errno = ENOBUFS;
return -1;
}
p = buf;
*p = (!!conn_id << 3)
| (bits << 4)
| ((!!nonce) << 2)
| !!ver;
++p;
if (conn_id)
{
memcpy(p, conn_id , sizeof(*conn_id));
p += sizeof(*conn_id);
}
if (ver)
{
memcpy(p, ver, 4);
p += 4;
}
if (nonce)
{
memcpy(p, nonce , 32);
p += 32;
}
/* ENDIAN */
memcpy(p, &packno, packnum_len);
p += packnum_len;
assert(p - buf == (intptr_t) header_len);
return header_len;
}
static int
gquic_le_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, gsf_fin_f gsf_fin, gsf_size_f gsf_size,
gsf_read_f gsf_read, void *stream)
{
/* 1fdoooss */
unsigned slen, olen, dlen;
unsigned char *p = buf + 1;
int fin;
/* ss: Stream ID length: 1, 2, 3, or 4 bytes */
slen = (stream_id > 0x0000FF)
+ (stream_id > 0x00FFFF)
+ (stream_id > 0xFFFFFF)
+ 1;
/* ooo: Offset length: 0, 2, 3, 4, 5, 6, 7, or 8 bytes */
olen = (offset >= (1ULL << 56))
+ (offset >= (1ULL << 48))
+ (offset >= (1ULL << 40))
+ (offset >= (1ULL << 32))
+ (offset >= (1ULL << 24))
+ (offset >= (1ULL << 16))
+ ((offset > 0) << 1);
fin = gsf_fin(stream);
if (!fin)
{
unsigned size, n_avail;
uint16_t nr;
size = gsf_size(stream);
n_avail = buf_len - (p + slen + olen - buf);
/* If we cannot fill remaining buffer, we need to include data
* length.
*/
dlen = (size < n_avail) << 1;
n_avail -= dlen;
CHECK_SPACE(1 + olen + slen + dlen +
+ 1 /* We need to write at least 1 byte */, buf, buf + buf_len);
memcpy(p, &stream_id, slen);
p += slen;
memcpy(p, &offset, olen);
p += olen;
/* Read as much as we can */
nr = gsf_read(stream, p + dlen, n_avail, &fin);
assert(nr != 0);
if (dlen)
memcpy(p, &nr, 2);
p += dlen + nr;
}
else
{
dlen = 2;
CHECK_SPACE(1 + slen + olen + 2, buf, buf + buf_len);
memcpy(p, &stream_id, slen);
p += slen;
memcpy(p, &offset, olen);
p += olen;
memset(p, 0, 2);
p += 2;
}
/* Convert slen to bit representation: 0 - 3: */
slen -= 1;
assert(slen <= 3);
/* Convert olen to bit representation: 0 - 7: */
olen += !olen;
olen -= 1;
assert(olen <= 7);
buf[0] = 0x80
| (fin << 6)
| (dlen << 4)
| (olen << 2)
| slen
;
return p - buf;
}
/* return parsed (used) buffer length */
static int
gquic_le_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz,
stream_frame_t *stream_frame)
{
/* 1fdoooss */
const unsigned char *p = buf;
const unsigned char *const pend = p + rem_packet_sz;
CHECK_SPACE(1, p, pend);
const char type = *p++;
const unsigned data_len = (type >> 4) & 2;
const unsigned offset_len = ((type >> 2) & 7) + 1 - !((type >> 2) & 7);
const unsigned stream_id_len = 1 + (type & 3);
const unsigned need = data_len + offset_len + stream_id_len;
CHECK_SPACE(need, p, pend);
memset(stream_frame, 0, sizeof(*stream_frame));
stream_frame->data_frame.df_fin = (type >> 6) & 1;
memcpy(&stream_frame->stream_id, p, stream_id_len);
p += stream_id_len;
memcpy(&stream_frame->data_frame.df_offset, p, offset_len);
p += offset_len;
if (data_len)
{
memcpy(&stream_frame->data_frame.df_size, p, data_len);
p += data_len;
CHECK_SPACE(stream_frame->data_frame.df_size, p, pend);
stream_frame->data_frame.df_data = p;
p += stream_frame->data_frame.df_size;
}
else
{
stream_frame->data_frame.df_size = pend - p;
stream_frame->data_frame.df_data = p;
p = pend;
}
/* From the spec: "A stream frame must always have either non-zero
* data length or the FIN bit set.'
*/
if (!(stream_frame->data_frame.df_size ||
stream_frame->data_frame.df_fin))
return -1;
assert(p <= pend);
return p - (unsigned char *) buf;
}
/* This is a special function: it is used to extract the largest observed
* packet number from ACK frame that we ourselves generated. This allows
* us to skip some checks.
*/
static lsquic_packno_t
gquic_le_parse_ack_high (const unsigned char *buf, size_t buf_len)
{
unsigned char type;
unsigned largest_obs_len;
type = buf[0];
largest_obs_len = flag_to_pkt_num_len(type >> 2);
assert(parse_frame_type_gquic_Q035_thru_Q039(type) == QUIC_FRAME_ACK);
assert(buf_len >= 1 + largest_obs_len);
return get_vary_len_int64(buf + 1, largest_obs_len);
}
/* Return parsed (used) buffer length.
* If parsing failed, negative value is returned.
*/
static int
gquic_le_parse_ack_frame (const unsigned char *buf, size_t buf_len, ack_info_t *ack)
{
/* 01nullmm */
const unsigned char type = buf[0];
const unsigned char *p = buf + 1;
const unsigned char *const pend = buf + buf_len;
assert((type & 0xC0) == 0x40); /* We're passed correct frame type */
const int ack_block_len = flag_to_pkt_num_len(type); /* mm */
const int largest_obs_len = flag_to_pkt_num_len(type >> 2); /* ll */
CHECK_SPACE(largest_obs_len, p , pend);
ack->ranges[0].high = get_vary_len_int64(p, largest_obs_len);
p += largest_obs_len;
CHECK_SPACE(2, p , pend);
ack->lack_delta = gquic_le_read_float_time16(p);
p += 2;
unsigned n_blocks;
if (type & 0x20)
{
CHECK_SPACE(1, p , pend);
n_blocks = *p;
++p;
}
else
n_blocks = 0;
CHECK_SPACE(ack_block_len, p , pend);
ack->ranges[0].low = ack->ranges[0].high
- get_vary_len_int64(p, ack_block_len) + 1;
p += ack_block_len;
if (n_blocks)
{
CHECK_SPACE((ack_block_len + 1) * n_blocks, p , pend);
unsigned i, n, gap;
for (i = 0, n = 1, gap = 0; i < n_blocks; ++i)
{
gap += *p;
const uint64_t length = get_vary_len_int64(p + 1, ack_block_len);
p += 1 + ack_block_len;
if (length)
{
ack->ranges[n].high = ack->ranges[n - 1].low - gap - 1;
ack->ranges[n].low = ack->ranges[n].high - length + 1;
++n;
gap = 0;
}
}
ack->n_ranges = n;
}
else
ack->n_ranges = 1;
CHECK_SPACE(1, p , pend);
ack->n_timestamps = *p;
++p;
if (ack->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);
CHECK_SPACE(timestamps_size, p, pend);
p += timestamps_size;
#endif
}
assert(p <= pend);
return p - (unsigned char *) buf;
}
static int
gquic_le_gen_stop_waiting_frame(unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits bits,
lsquic_packno_t least_unacked_packno)
{
lsquic_packno_t delta;
unsigned packnum_len = packno_bits2len(bits);
if (buf_len >= 1 + packnum_len)
{
*buf = 0x06;
delta = cur_packno - least_unacked_packno;
write_vary_len_to_buf(delta, buf + 1, packnum_len);
return 1 + packnum_len;
}
else
return -1;
}
static int
gquic_le_parse_stop_waiting_frame (const unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits bits,
lsquic_packno_t *least_unacked)
{
lsquic_packno_t delta;
unsigned packnum_len = packno_bits2len(bits);
if (buf_len >= 1 + packnum_len)
{
delta = 0;
memcpy(&delta, buf + 1, packnum_len);
*least_unacked = cur_packno - delta;
return 1 + packnum_len;
}
else
return -1;
}
static int
gquic_le_skip_stop_waiting_frame (size_t buf_len, enum lsquic_packno_bits bits)
{
unsigned packnum_len = packno_bits2len(bits);
if (buf_len >= 1 + packnum_len)
return 1 + packnum_len;
else
return -1;
}
static int
gquic_le_gen_window_update_frame (unsigned char *buf, int buf_len, uint32_t stream_id,
uint64_t offset)
{
unsigned char *p = buf;
if (buf_len < QUIC_WUF_SZ)
return -1;
*p = 0x04;
++p;
write_vary_len_to_buf(stream_id, p, 4);
p += 4;
write_vary_len_to_buf(offset, p, 8);
p += 8;
return p - buf;
}
static int
gquic_le_parse_window_update_frame (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id, uint64_t *offset)
{
if (buf_len < QUIC_WUF_SZ)
return -1;
*stream_id = get_vary_len_int64(buf + 1, 4);
*offset = get_vary_len_int64(buf + 1 + 4, 8);
return QUIC_WUF_SZ;
}
static int
gquic_le_gen_blocked_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id)
{
unsigned char *p = buf;
if (buf_len < QUIC_BLOCKED_FRAME_SZ)
return -1;
*p = 0x05;
++p;
write_vary_len_to_buf(stream_id, p, 4);
p += 4;
return p - buf;
}
static int
gquic_le_parse_blocked_frame (const unsigned char *buf, size_t buf_len,
uint32_t *stream_id)
{
if (buf_len < QUIC_BLOCKED_FRAME_SZ)
return -1;
*stream_id = get_vary_len_int64(buf + 1, 4);
return QUIC_BLOCKED_FRAME_SZ;
}
static int
gquic_le_gen_rst_frame (unsigned char *buf, size_t buf_len, uint32_t stream_id,
uint64_t offset, uint32_t error_code)
{
unsigned char *p = buf;
if (buf_len < QUIC_RST_STREAM_SZ)
return -1;
*p = 0x01;
++p;
write_vary_len_to_buf(stream_id, p, 4);
p += 4;
write_vary_len_to_buf(offset, p, 8);
p += 8;
write_vary_len_to_buf(error_code, p, 4);
p += 4;
return p - buf;
}
static int
gquic_le_parse_rst_frame (const unsigned char *buf, size_t buf_len, uint32_t *stream_id,
uint64_t *offset, uint32_t *error_code)
{
if (buf_len < 17)
return -1;
*stream_id = get_vary_len_int64(buf + 1, 4);
*offset = get_vary_len_int64(buf + 1 + 4, 8);
*error_code = get_vary_len_int64(buf + 1 + 4 + 8, 4);
return 17;
}
static int
gquic_le_gen_ping_frame (unsigned char *buf, int buf_len)
{
if (buf_len > 0)
{
buf[0] = 0x07;
return 1;
}
else
return -1;
}
static int
gquic_le_gen_connect_close_frame (unsigned char *buf, int buf_len, uint32_t error_code,
const char *reason, int reason_len)
{
unsigned char *p = buf;
if (buf_len < 7)
return -1;
*p = 0x02;
++p;
write_vary_len_to_buf(error_code, p, 4);
p += 4;
write_vary_len_to_buf(reason_len, p, 2);
p += 2;
memcpy(p, reason, reason_len);
p += reason_len;
if (buf_len < p - buf)
return -2;
return p - buf;
}
static int
gquic_le_parse_connect_close_frame (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint16_t *reason_len, uint8_t *reason_offset)
{
if (buf_len < 7)
return -1;
*error_code = get_vary_len_int64(buf + 1, 4);
*reason_len = get_vary_len_int64(buf + 1 + 4, 2);
*reason_offset = 7;
if (buf_len < 7u + *reason_len)
return -2;
return 7 + *reason_len;
}
static int
gquic_le_gen_goaway_frame(unsigned char *buf, size_t buf_len, uint32_t error_code,
uint32_t last_good_stream_id, const char *reason,
size_t reason_len)
{
unsigned char *p = buf;
if (buf_len < QUIC_GOAWAY_FRAME_SZ + reason_len)
return -1;
*p = 0x03;
++p;
write_vary_len_to_buf(error_code, p, 4);
p += 4;
write_vary_len_to_buf(last_good_stream_id, p, 4);
p += 4;
write_vary_len_to_buf(reason_len, p, 2);
p += 2;
if (reason_len)
{
memcpy(p, reason, reason_len);
p += reason_len;
}
return p - buf;
}
/* the reason is buf + *reason_offset, length is *reason_length */
static int
gquic_le_parse_goaway_frame (const unsigned char *buf, size_t buf_len,
uint32_t *error_code, uint32_t *last_good_stream_id,
uint16_t *reason_length, const char **reason)
{
if (buf_len < QUIC_GOAWAY_FRAME_SZ)
return -1;
*error_code = get_vary_len_int64(buf + 1, 4);
*last_good_stream_id = get_vary_len_int64(buf + 1 + 4, 4);
*reason_length = get_vary_len_int64(buf + 1 + 4 + 4, 2);
if (*reason_length)
{
if ((int)buf_len < QUIC_GOAWAY_FRAME_SZ + *reason_length)
return -2;
*reason = (const char *) buf + QUIC_GOAWAY_FRAME_SZ;
}
else
*reason = NULL;
return QUIC_GOAWAY_FRAME_SZ + *reason_length;
}
/* Returns number of bytes written or -1 on failure */
/* This function makes an assumption that there is at least one range */
static int
gquic_le_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing)
{
const struct lsquic_packno_range *const first = rechist_first(rechist);
if (!first)
{
errno = EINVAL;
return -1;
}
/* Copy values from the first range, because the memory the pointer
* points to may change:
*/
const lsquic_packno_t first_low = first->low, first_high = first->high;
unsigned char *p = outbuf;
unsigned char *const type = p;
unsigned char *const end = p + outbuf_sz;
#define AVAIL() (end - p)
#define CHECKOUT(sz) do { \
if ((intptr_t) (sz) > AVAIL()) { \
errno = ENOBUFS; \
return -1; \
} \
} while (0)
CHECKOUT(1);
++p;
/* 01nullmm */
*type = 0x40;
unsigned largest_acked_len, ack_block_len, bits;
/* Calculate largest ACKed len and set `ll' bits: */
const lsquic_packno_t maxno = first_high;
bits = (maxno >= (1ULL << 8))
+ (maxno >= (1ULL << 16))
+ (maxno >= (1ULL << 32));
largest_acked_len = (1 << bits) - ((maxno >= (1ULL << 32)) << 1);
*type |= bits << 2;
/* Calculate largest ACK block length and set `mm' bits: */
unsigned n_ranges = 0;
lsquic_packno_t maxdiff = 0;
const struct lsquic_packno_range *range;
for (range = rechist_first(rechist); range; range = rechist_next(rechist))
{
++n_ranges;
const lsquic_packno_t diff = range->high - range->low + 1;
if (diff > maxdiff)
maxdiff = diff;
}
bits = (maxdiff >= (1ULL << 8))
+ (maxdiff >= (1ULL << 16))
+ (maxdiff >= (1ULL << 32));
ack_block_len = (1 << bits) - ((maxdiff >= (1ULL << 32)) << 1);
*type |= bits;
CHECKOUT(largest_acked_len);
memcpy(p, &maxno, largest_acked_len);
p += largest_acked_len;
CHECKOUT(2);
lsquic_time_t diff = now - rechist_largest_recv(rechist);
gquic_le_write_float_time16(diff, p);
LSQ_DEBUG("%s: diff: %"PRIu64"; encoded: 0x%04X", __func__, diff,
*(uint16_t*)p);
p += 2;
if (n_ranges > 1)
{
*has_missing = 1;
*type |= 0x20;
/* We need to write out at least one range */
CHECKOUT(2 * (1 + ack_block_len));
unsigned char *const n_ranges_p = p; /* Set this later */
lsquic_packno_t diff = maxno - first_low + 1;
memcpy(p + 1, &diff, ack_block_len);
p += ack_block_len + 1;
/* Write out ack blocks until one of the following occurs:
* 1. We run out of intervals.
* 2. We run out of room.
* 3. We run out of highest possible number of ACK blocks (0xFF).
*/
range = rechist_first(rechist);
lsquic_packno_t gap = 0;
n_ranges = 0;
do {
if (0 == gap)
{
const lsquic_packno_t prev_low = range->low;
range = rechist_next(rechist);
if (!range)
break;
gap = prev_low - range->high - 1;
}
if (gap >= 0x100)
{
*p = 0xFF;
gap -= 0xFF;
memset(p + 1, 0, ack_block_len);
}
else
{
*p = gap;
gap = 0;
diff = range->high - range->low + 1;
memcpy(p + 1, &diff, ack_block_len);
}
p += ack_block_len + 1;
++n_ranges;
} while (n_ranges < 0xFF &&
AVAIL() >= (intptr_t) ack_block_len + 1 + 1 /* timestamp byte */);
*n_ranges_p = n_ranges;
}
else
{
*has_missing = 0;
CHECKOUT(ack_block_len);
const lsquic_packno_t diff = maxno - first_low + 1;
memcpy(p, &diff, ack_block_len);
p += ack_block_len;
}
/* We do not generate timestamp list because the reference implementation
* does not use them. When that changes, we will start sending timestamps
* over.
*/
CHECKOUT(1);
*p = 0;
++p;
return p - (unsigned char *) outbuf;
#undef CHECKOUT
}
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,
.pf_calc_stream_frame_header_sz = calc_stream_frame_header_sz_gquic,
.pf_parse_stream_frame_header_sz = parse_stream_frame_header_sz_gquic,
.pf_parse_stream_frame = gquic_le_parse_stream_frame,
.pf_parse_ack_frame = gquic_le_parse_ack_frame,
.pf_parse_ack_high = gquic_le_parse_ack_high,
.pf_gen_ack_frame = gquic_le_gen_ack_frame,
.pf_gen_stop_waiting_frame = gquic_le_gen_stop_waiting_frame,
.pf_parse_stop_waiting_frame = gquic_le_parse_stop_waiting_frame,
.pf_skip_stop_waiting_frame = gquic_le_skip_stop_waiting_frame,
.pf_gen_window_update_frame = gquic_le_gen_window_update_frame,
.pf_parse_window_update_frame = gquic_le_parse_window_update_frame,
.pf_gen_blocked_frame = gquic_le_gen_blocked_frame,
.pf_parse_blocked_frame = gquic_le_parse_blocked_frame,
.pf_gen_rst_frame = gquic_le_gen_rst_frame,
.pf_parse_rst_frame = gquic_le_parse_rst_frame,
.pf_gen_connect_close_frame = gquic_le_gen_connect_close_frame,
.pf_parse_connect_close_frame = gquic_le_parse_connect_close_frame,
.pf_gen_goaway_frame = gquic_le_gen_goaway_frame,
.pf_parse_goaway_frame = gquic_le_parse_goaway_frame,
.pf_gen_ping_frame = gquic_le_gen_ping_frame,
#ifndef NDEBUG
.pf_write_float_time16 = gquic_le_write_float_time16,
.pf_read_float_time16 = gquic_le_read_float_time16,
#endif
.pf_parse_frame_type = parse_frame_type_gquic_Q035_thru_Q039,
};

View file

@ -0,0 +1,61 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_QTAGS_H
#define LSQUIC_QTAGS_H 1
#define TAG(a, b, c, d) ((uint32_t)(((unsigned) d << 24) + \
((unsigned) c << 16) + ((unsigned) b << 8) + (unsigned) a))
#define QTAG_AEAD TAG('A', 'E', 'A', 'D')
#define QTAG_AESG TAG('A', 'E', 'S', 'G')
#define QTAG_C255 TAG('C', '2', '5', '5')
#define QTAG_CCRT TAG('C', 'C', 'R', 'T')
#define QTAG_CCS TAG('C', 'C', 'S', 0 )
#define QTAG_CFCW TAG('C', 'F', 'C', 'W')
#define QTAG_CHLO TAG('C', 'H', 'L', 'O')
#define QTAG_COPT TAG('C', 'O', 'P', 'T')
#define QTAG_CSCT TAG('C', 'S', 'C', 'T')
#define QTAG_CTIM TAG('C', 'T', 'I', 'M')
#define QTAG_EXPY TAG('E', 'X', 'P', 'Y')
#define QTAG_ICSL TAG('I', 'C', 'S', 'L')
#define QTAG_IRTT TAG('I', 'R', 'T', 'T')
#define QTAG_KEXS TAG('K', 'E', 'X', 'S')
#define QTAG_MIDS TAG('M', 'I', 'D', 'S')
#define QTAG_NONC TAG('N', 'O', 'N', 'C')
#define QTAG_ORBT TAG('O', 'B', 'I', 'T')
#define QTAG_PAD TAG('P', 'A', 'D', 0 )
#define QTAG_PDMD TAG('P', 'D', 'M', 'D')
#define QTAG_PROF TAG('P', 'R', 'O', 'F')
#define QTAG_PUBS TAG('P', 'U', 'B', 'S')
#define QTAG_RCID TAG('R', 'C', 'I', 'D')
#define QTAG_REJ TAG('R', 'E', 'J', 0 )
#define QTAG_RREJ TAG('R', 'R', 'E', 'J')
#define QTAG_SCFG TAG('S', 'C', 'F', 'G')
#define QTAG_SCID TAG('S', 'C', 'I', 'D')
#define QTAG_SCLS TAG('S', 'C', 'L', 'S')
#define QTAG_SFCW TAG('S', 'F', 'C', 'W')
#define QTAG_SHLO TAG('S', 'H', 'L', 'O')
#define QTAG_SNI TAG('S', 'N', 'I', 0 )
#define QTAG_SRBF TAG('S', 'R', 'B', 'F')
#define QTAG_SREJ TAG('S', 'R', 'E', 'J')
#define QTAG_STTL TAG('S', 'T', 'T', 'L')
#define QTAG_TCID TAG('T', 'C', 'I', 'D')
#define QTAG_UAID TAG('U', 'A', 'I', 'D')
#define QTAG_VER TAG('V', 'E', 'R', 0 )
#define QTAG_X509 TAG('X', '5', '0', '9')
#define QTAG_XLCT TAG('X', 'L', 'C', 'T')
#define QTAG_STK TAG('S', 'T', 'K', '\0')
#define QTAG_SNO TAG('S', 'N', 'O', '\0')
#define QTAG_CRT TAG('C', 'R', 'T', '\xFF')
/* SMHL: Support SETTINGS_MAX_HEADER_LIST_SIZE. Based on comments in
* Chrome code, this setting frame will be supported by default in
* Q037.
*/
#define QTAG_SMHL TAG('S', 'M', 'H', 'L')
/* Supported in Q037 and later. If this option is specified in the
* handshake, do not send or process STOP_WINDOW frames.
*/
#define QTAG_NSTP TAG('N', 'S', 'T', 'P')
#endif

View file

@ -0,0 +1,151 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_rechist.c -- History of received packets.
*/
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_rechist.h"
#define LSQUIC_LOGGER_MODULE LSQLM_RECHIST
#define LSQUIC_LOG_CONN_ID rechist->rh_cid
#include "lsquic_logger.h"
void
lsquic_rechist_init (struct lsquic_rechist *rechist, lsquic_cid_t cid)
{
memset(rechist, 0, sizeof(*rechist));
rechist->rh_cid = cid;
rechist->rh_cutoff = 1;
lsquic_packints_init(&rechist->rh_pints);
LSQ_DEBUG("instantiated received packet history");
}
void
lsquic_rechist_cleanup (lsquic_rechist_t *rechist)
{
lsquic_packints_cleanup(&rechist->rh_pints);
memset(rechist, 0, sizeof(*rechist));
}
enum received_st
lsquic_rechist_received (lsquic_rechist_t *rechist, lsquic_packno_t packno,
lsquic_time_t now)
{
const struct lsquic_packno_range *first_range;
LSQ_DEBUG("received %"PRIu64, packno);
if (packno < rechist->rh_cutoff)
{
if (packno)
return REC_ST_DUP;
else
return REC_ST_ERR;
}
first_range = lsquic_packints_first(&rechist->rh_pints);
if (!first_range || packno > first_range->high)
rechist->rh_largest_acked_received = now;
switch (lsquic_packints_add(&rechist->rh_pints, packno))
{
case PACKINTS_OK:
++rechist->rh_n_packets;
return REC_ST_OK;
case PACKINTS_DUP:
return REC_ST_DUP;
default:
assert(0);
case PACKINTS_ERR:
return REC_ST_ERR;
}
}
void
lsquic_rechist_stop_wait (lsquic_rechist_t *rechist, lsquic_packno_t cutoff)
{
LSQ_INFO("stop wait: %"PRIu64, cutoff);
if (rechist->rh_flags & RH_CUTOFF_SET)
{
assert(cutoff >= rechist->rh_cutoff); /* Check performed in full_conn */
if (cutoff == rechist->rh_cutoff)
return;
}
rechist->rh_cutoff = cutoff;
rechist->rh_flags |= RH_CUTOFF_SET;
struct packet_interval *pi, *next;
for (pi = TAILQ_FIRST(&rechist->rh_pints.pk_intervals); pi; pi = next)
{
next = TAILQ_NEXT(pi, next_pi);
if (pi->range.low < cutoff)
{
if (pi->range.high < cutoff)
{
rechist->rh_n_packets -= pi->range.high - pi->range.low + 1;
TAILQ_REMOVE(&rechist->rh_pints.pk_intervals, pi, next_pi);
free(pi);
}
else
{
rechist->rh_n_packets -= cutoff - pi->range.low;
pi->range.low = cutoff;
}
}
}
lsquic_packints_sanity_check(&rechist->rh_pints);
}
lsquic_packno_t
lsquic_rechist_largest_packno (const lsquic_rechist_t *rechist)
{
const struct packet_interval *pi =
TAILQ_FIRST(&rechist->rh_pints.pk_intervals);
if (pi)
return pi->range.high;
else
return 0; /* Don't call this function if history is empty */
}
lsquic_packno_t
lsquic_rechist_cutoff (const lsquic_rechist_t *rechist)
{
if (rechist->rh_flags & RH_CUTOFF_SET)
return rechist->rh_cutoff;
else
return 0;
}
lsquic_time_t
lsquic_rechist_largest_recv (const lsquic_rechist_t *rechist)
{
return rechist->rh_largest_acked_received;
}
const struct lsquic_packno_range *
lsquic_rechist_first (lsquic_rechist_t *rechist)
{
return lsquic_packints_first(&rechist->rh_pints);
}
const struct lsquic_packno_range *
lsquic_rechist_next (lsquic_rechist_t *rechist)
{
return lsquic_packints_next(&rechist->rh_pints);
}

View file

@ -0,0 +1,69 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_rechist.h -- History of received packets.
*
* The purpose of received packet history is to generate ACK frames.
*/
#ifndef LSQUIC_RECHIST_H
#define LSQUIC_RECHIST_H 1
#include "lsquic_packints.h"
struct lsquic_rechist {
struct packints rh_pints;
lsquic_packno_t rh_cutoff;
lsquic_time_t rh_largest_acked_received;
lsquic_cid_t rh_cid; /* Used for logging */
/* Chromium limits the number of tracked packets (see
* kMaxTrackedPackets). We could do this, too.
*/
unsigned rh_n_packets;
enum {
RH_CUTOFF_SET = (1 << 0),
} rh_flags;
};
typedef struct lsquic_rechist lsquic_rechist_t;
void
lsquic_rechist_init (struct lsquic_rechist *, lsquic_cid_t);
void
lsquic_rechist_cleanup (struct lsquic_rechist *);
enum received_st {
REC_ST_OK,
REC_ST_DUP,
REC_ST_ERR,
};
enum received_st
lsquic_rechist_received (lsquic_rechist_t *, lsquic_packno_t,
lsquic_time_t now);
void
lsquic_rechist_stop_wait (lsquic_rechist_t *, lsquic_packno_t);
/* Returns number of bytes written on success, -1 on failure */
int
lsquic_rechist_make_ackframe (lsquic_rechist_t *,
void *outbuf, size_t outbuf_sz, int *has_missing,
lsquic_time_t now);
const struct lsquic_packno_range *
lsquic_rechist_first (lsquic_rechist_t *);
const struct lsquic_packno_range *
lsquic_rechist_next (lsquic_rechist_t *);
lsquic_packno_t
lsquic_rechist_largest_packno (const lsquic_rechist_t *);
lsquic_packno_t
lsquic_rechist_cutoff (const lsquic_rechist_t *);
lsquic_time_t
lsquic_rechist_largest_recv (const lsquic_rechist_t *);
#endif

View file

@ -0,0 +1,39 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_rtt.c -- RTT calculation
*/
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "lsquic_int_types.h"
#include "lsquic_rtt.h"
/* See RFC 2988 */
#define ALPHA_SHIFT 3 /* Alpha is 1/8 */
#define BETA_SHIFT 2 /* Beta is 1/4 */
void
lsquic_rtt_stats_update (struct lsquic_rtt_stats *stats,
lsquic_time_t send_delta, lsquic_time_t lack_delta)
{
if (send_delta > lack_delta)
send_delta -= lack_delta;
if (stats->srtt) {
stats->rttvar -= stats->rttvar >> BETA_SHIFT;
// FIXED: subtracting unsigned (the (int) cast gets repromoted to uint64_t
// made abs() irrelevant and allowed overflow. instead cast the difference
// to a signed int64 and use labs() to get abs val.
stats->rttvar += (llabs((int64_t) (send_delta - stats->srtt)))
>> BETA_SHIFT;
stats->srtt -= stats->srtt >> ALPHA_SHIFT;
stats->srtt += send_delta >> ALPHA_SHIFT;
} else {
/* First measurement */
stats->srtt = send_delta;
stats->rttvar = send_delta >> 1;
}
}

View file

@ -0,0 +1,26 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_rtt.h -- RTT calculation
*/
#ifndef LSQUIC_RTT_H
#define LSQUIC_RTT_H 1
/* This struct is initialized by setting it to zero */
struct lsquic_rtt_stats {
lsquic_time_t srtt;
lsquic_time_t rttvar;
};
void
lsquic_rtt_stats_update (struct lsquic_rtt_stats *, lsquic_time_t send_delta,
lsquic_time_t lack_delta);
#define lsquic_rtt_stats_get_srtt(stats) ((stats)->srtt)
#define lsquic_rtt_stats_get_rttvar(stats) ((stats)->rttvar)
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,183 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_SEND_CTL_H
#define LSQUIC_SEND_CTL_H 1
#include <sys/queue.h>
#include "lsquic_types.h"
#ifndef LSQUIC_SEND_STATS
# define LSQUIC_SEND_STATS 1
#endif
TAILQ_HEAD(lsquic_packets_tailq, lsquic_packet_out);
struct lsquic_packet_out;
struct ack_info;
struct lsquic_alarmset;
struct lsquic_engine_public;
struct lsquic_conn_public;
struct ver_neg;
typedef struct lsquic_send_ctl {
struct lsquic_packets_tailq sc_scheduled_packets,
sc_unacked_packets,
sc_lost_packets;
struct lsquic_engine_public *sc_enpub;
struct lsquic_alarmset *sc_alset;
const struct ver_neg *sc_ver_neg;
struct lsquic_conn_public *sc_conn_pub;
struct lsquic_cubic sc_cubic;
struct pacer sc_pacer;
lsquic_senhist_t sc_senhist;
lsquic_packno_t sc_cur_packno;
lsquic_packno_t sc_largest_sent_at_cutback;
lsquic_packno_t sc_max_rtt_packno;
lsquic_packno_t sc_largest_acked_packno;
lsquic_time_t sc_largest_acked_sent_time;
/* sc_largest_ack2ed is the packet number sent by peer that we acked and
* we know that our ACK was received by peer. This is used to determine
* the receive history cutoff point for the purposes of generating ACK
* frames in the absense of STOP_WAITING frames. Used when NSTP option
* is set. (The "ack2ed" is odd enough to not be confused with anything
* else and it is not insanely long.)
*/
lsquic_packno_t sc_largest_ack2ed;
lsquic_time_t sc_loss_to;
unsigned sc_n_consec_rtos;
unsigned sc_next_limit;
unsigned sc_n_in_flight; /* Number of packets in flight */
unsigned sc_n_scheduled;
unsigned sc_n_stop_waiting;
unsigned short sc_pack_size;
enum {
SC_TCID0 = (1 << 0),
SC_LOST_ACK = (1 << 1),
SC_NSTP = (1 << 2),
SC_PACE = (1 << 3),
SC_SCHED_TICK = (1 << 4),
} sc_flags:8;
unsigned char sc_n_hsk;
unsigned char sc_n_tlp;
#if LSQUIC_SEND_STATS
struct {
unsigned n_total_sent,
n_resent,
n_delayed;
} sc_stats;
#endif
} lsquic_send_ctl_t;
void
lsquic_send_ctl_init (lsquic_send_ctl_t *, struct lsquic_alarmset *,
struct lsquic_engine_public *, const struct ver_neg *,
struct lsquic_conn_public *, unsigned short max_packet_size);
int
lsquic_send_ctl_sent_packet (lsquic_send_ctl_t *, struct lsquic_packet_out *);
int
lsquic_send_ctl_got_ack (lsquic_send_ctl_t *, const struct ack_info *,
lsquic_time_t);
lsquic_packno_t
lsquic_send_ctl_smallest_unacked (lsquic_send_ctl_t *ctl);
int
lsquic_send_ctl_have_unacked_stream_frames (const lsquic_send_ctl_t *);
void
lsquic_send_ctl_cleanup (lsquic_send_ctl_t *);
int
lsquic_send_ctl_can_send (lsquic_send_ctl_t *ctl);
void
lsquic_send_ctl_scheduled_one (lsquic_send_ctl_t *, lsquic_packet_out_t *);
void
lsquic_send_ctl_delayed_one (lsquic_send_ctl_t *, lsquic_packet_out_t *);
lsquic_packet_out_t *
lsquic_send_ctl_next_packet_to_send (lsquic_send_ctl_t *);
void
lsquic_send_ctl_expire_all (lsquic_send_ctl_t *ctl);
#define lsquic_send_ctl_n_in_flight(ctl) (+(ctl)->sc_n_in_flight)
#define lsquic_send_ctl_n_scheduled(ctl) (+(ctl)->sc_n_scheduled)
#define lsquic_send_ctl_largest_ack2ed(ctl) (+(ctl)->sc_largest_ack2ed)
#ifdef NDEBUG
# define lsquic_send_ctl_sanity_check(ctl)
#else
void
lsquic_send_ctl_sanity_check (const lsquic_send_ctl_t *ctl);
#endif
int
lsquic_send_ctl_have_outgoing_stream_frames (const lsquic_send_ctl_t *);
int
lsquic_send_ctl_have_outgoing_retx_frames (const lsquic_send_ctl_t *);
#define lsquic_send_ctl_last_scheduled(ctl) \
TAILQ_LAST(&(ctl)->sc_scheduled_packets, lsquic_packets_tailq)
lsquic_packet_out_t *
lsquic_send_ctl_new_packet_out (lsquic_send_ctl_t *, unsigned);
lsquic_packet_out_t *
lsquic_send_ctl_get_writeable_packet (lsquic_send_ctl_t *,
unsigned need_at_least, int *is_err);
unsigned
lsquic_send_ctl_reschedule_packets (lsquic_send_ctl_t *);
#define lsquic_send_ctl_lost_ack(ctl) ((ctl)->sc_flags & SC_LOST_ACK)
#define lsquic_send_ctl_scheduled_ack(ctl) do { \
(ctl)->sc_flags &= ~SC_LOST_ACK; \
} while (0)
void
lsquic_send_ctl_set_tcid0 (lsquic_send_ctl_t *, int);
#define lsquic_send_ctl_turn_nstp_on(ctl) ((ctl)->sc_flags |= SC_NSTP)
void
lsquic_send_ctl_reset_stream (lsquic_send_ctl_t *, uint32_t);
int
lsquic_send_ctl_squeeze_sched (lsquic_send_ctl_t *);
/* Same return value as for squeezing, but without actual squeezing. */
int
lsquic_send_ctl_have_delayed_packets (const lsquic_send_ctl_t *ctl);
void
lsquic_send_ctl_reset_packnos (lsquic_send_ctl_t *);
void
lsquic_send_ctl_ack_to_front (lsquic_send_ctl_t *);
#define lsquic_send_ctl_n_stop_waiting(ctl) (+(ctl)->sc_n_stop_waiting)
#define lsquic_send_ctl_n_stop_waiting_reset(ctl) do { \
(ctl)->sc_n_stop_waiting = 0; \
} while (0)
void
lsquic_send_ctl_drop_scheduled (lsquic_send_ctl_t *);
#define lsquic_send_ctl_tick(ctl, now) do { \
if ((ctl)->sc_flags & SC_PACE) \
{ \
(ctl)->sc_flags |= SC_SCHED_TICK; \
pacer_tick(&(ctl)->sc_pacer, now); \
} \
} while (0)
#endif

View file

@ -0,0 +1,150 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_senhist.c -- Sent history implementation
*/
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lsquic_int_types.h"
#include "lsquic_senhist.h"
void
lsquic_senhist_init (lsquic_senhist_t *hist)
{
lsquic_packints_init(&hist->sh_pints);
#ifndef NDEBUG
{
const char *env;
env = getenv("LSQUIC_REORDER_SENT");
if (env && atoi(env))
hist->sh_flags = SH_REORDER;
else
hist->sh_flags = 0;
}
#endif
}
void
lsquic_senhist_cleanup (lsquic_senhist_t *hist)
{
lsquic_packints_cleanup(&hist->sh_pints);
}
/* At the time of this writing, the only reason the sequence of sent
* packet numbers could contain a hole is elision of stream frames from
* scheduled, but delayed packets. If such packet becomes empty after
* elision, it is dropped from the queue.
*/
/* The fast insert is used in the normal case, when packets are sent
* out in the same order in which they are scheduled: that is, their
* packet numbers are always increasing.
*/
static int
senhist_add_fast (lsquic_senhist_t *hist, lsquic_packno_t packno)
{
struct packet_interval *pi;
pi = TAILQ_FIRST(&hist->sh_pints.pk_intervals);
if (pi)
{
/* Check that packet numbers are always increasing */
assert(packno > pi->range.high);
if (packno == pi->range.high + 1)
{
++pi->range.high;
return 0;
}
}
pi = malloc(sizeof(*pi));
if (!pi)
return -1;
pi->range.high = packno;
pi->range.low = packno;
TAILQ_INSERT_HEAD(&hist->sh_pints.pk_intervals, pi, next_pi);
return 0;
}
#ifndef NDEBUG
static int
senhist_add_slow (lsquic_senhist_t *hist, lsquic_packno_t packno)
{
switch (lsquic_packints_add(&hist->sh_pints, packno))
{
case PACKINTS_OK:
return 0;
case PACKINTS_DUP: /* We should not generate duplicate packet numbers! */
default:
assert(0);
case PACKINTS_ERR:
return -1;
}
}
#endif
int
lsquic_senhist_add (lsquic_senhist_t *hist, lsquic_packno_t packno)
{
#ifndef NDEBUG
if (hist->sh_flags & SH_REORDER)
return senhist_add_slow(hist, packno);
else
#endif
return senhist_add_fast(hist, packno);
}
int
lsquic_senhist_sent_range (lsquic_senhist_t *hist, lsquic_packno_t low,
lsquic_packno_t high)
{
const struct lsquic_packno_range *range;
for (range = lsquic_packints_first(&hist->sh_pints); range;
range = lsquic_packints_next(&hist->sh_pints))
if (range->low <= low && range->high >= high)
return 1;
return 0;
}
lsquic_packno_t
lsquic_senhist_largest (lsquic_senhist_t *hist)
{
const struct lsquic_packno_range *range;
range = lsquic_packints_first(&hist->sh_pints);
if (range)
return range->high;
else
return 0;
}
void
lsquic_senhist_tostr (lsquic_senhist_t *hist, char *buf, size_t bufsz)
{
const struct lsquic_packno_range *range;
size_t off;
int n;
for (off = 0, range = lsquic_packints_first(&hist->sh_pints);
range && off < bufsz;
off += n, range = lsquic_packints_next(&hist->sh_pints))
{
n = snprintf(buf + off, bufsz - off, "[%"PRIu64"-%"PRIu64"]",
range->high, range->low);
if (n < 0 || (size_t) n >= bufsz - off)
break;
}
if (bufsz > 0)
buf[off] = '\0';
}

View file

@ -0,0 +1,52 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_senhist.h -- History sent packets.
*
* We only keep track of packet numbers in order to verify ACKs.
*/
#ifndef LSQUIC_SENHIST_H
#define LSQUIC_SENHIST_H 1
#include "lsquic_packints.h"
typedef struct lsquic_senhist {
/* These ranges are ordered from high to low. While searching this
* structure is O(n), I expect that in practice, a very long search
* could only happen once before the connection is terminated,
* because:
* a) either the packet number far away is real, but it was so long
* ago that it would have timed out by now (RTO); or
* b) the peer sends an invalid ACK.
*/
struct packints sh_pints;
#ifndef NDEBUG
enum {
SH_REORDER = (1 << 0),
} sh_flags;
#endif
} lsquic_senhist_t;
void
lsquic_senhist_init (lsquic_senhist_t *);
void
lsquic_senhist_cleanup (lsquic_senhist_t *);
int
lsquic_senhist_add (lsquic_senhist_t *, lsquic_packno_t);
/* Returns true if history contains all packets numbers in this range.
*/
int
lsquic_senhist_sent_range (lsquic_senhist_t *, lsquic_packno_t low,
lsquic_packno_t high);
/* Returns 0 if no packets have been sent yet */
lsquic_packno_t
lsquic_senhist_largest (lsquic_senhist_t *hist);
void
lsquic_senhist_tostr (lsquic_senhist_t *hist, char *buf, size_t bufsz);
#endif

371
src/liblsquic/lsquic_set.c Normal file
View file

@ -0,0 +1,371 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_set.c -- A set implementation.
*
* Deleting from a set is not supported. Implemented as a sorted array.
* Optimized for reading. Insertion may trigger realloc, memmove, or
* both.
*/
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "lsquic_set.h"
struct lsquic_set32_elem
{
uint32_t low, high;
};
void
lsquic_set32_init (struct lsquic_set32 *set)
{
memset(set, 0, sizeof(*set));
}
void
lsquic_set32_cleanup (struct lsquic_set32 *set)
{
free(set->elems);
}
static int
lsquic_set32_insert_set_elem (struct lsquic_set32 *set, int i, uint32_t value)
{
struct lsquic_set32_elem *elems;
if (set->n_elems == INT_MAX)
{
errno = EOVERFLOW;
return -1;
}
if (set->n_alloc == set->n_elems)
{
if (set->n_alloc)
set->n_alloc *= 2;
else
set->n_alloc = 4;
elems = realloc(set->elems, sizeof(set->elems[0]) * set->n_alloc);
if (!elems)
return -1;
set->elems = elems;
}
if (i < set->n_elems)
memmove(&set->elems[i + 1], &set->elems[i],
(set->n_elems - i) * sizeof(set->elems[i]));
set->elems[i].low = set->elems[i].high = value;
++set->n_elems;
return 0;
}
static void
lsquic_set32_merge_set_elems (struct lsquic_set32 *set, int i)
{
assert(i >= 0);
assert(i < set->n_elems - 1);
assert(set->elems[i].high + 1 == set->elems[i + 1].low);
set->elems[i].high = set->elems[i + 1].high;
if (i < set->n_elems - 2)
memmove(&set->elems[i + 1], &set->elems[i + 2],
(set->n_elems - i - 2) * sizeof(set->elems[i]));
--set->n_elems;
}
#ifndef NDEBUG
static void
lsquic_set32_check_elems_sorted (const struct lsquic_set32 *set)
{
int i;
for (i = 0; i < set->n_elems; ++i)
{
assert(set->elems[i].low <= set->elems[i].high);
if (i > 0)
assert(set->elems[i - 1].high + 1 < set->elems[i].low);
}
}
#endif
int
lsquic_set32_add (struct lsquic_set32 *set, uint32_t value)
{
if (value < 64)
{
set->lowset |= 1ULL << value;
return 0;
}
int low, high, i;
if (set->n_elems > 0)
{
low = 0, high = set->n_elems - 1;
do
{
i = low + (high - low) / 2;
if (set->elems[i].low <= value && set->elems[i].high >= value)
return 0;
else if (set->elems[i].high < value)
low = i + 1;
else
high = i - 1;
}
while (low <= high);
if (value < set->elems[i].low)
{
if (set->elems[i].low - 1 == value)
{
set->elems[i].low = value;
if (i > 0 && set->elems[i - 1].high + 1 == value)
lsquic_set32_merge_set_elems(set, i - 1);
}
else if (i > 0 && set->elems[i - 1].high + 1 == value)
set->elems[i - 1].high = value;
else if (0 != lsquic_set32_insert_set_elem(set, i, value))
return -1;
}
else
{
assert(value > set->elems[i].high);
if (set->elems[i].high + 1 == value)
{
set->elems[i].high = value;
if (i + 1 < set->n_elems && set->elems[i + 1].low - 1== value)
lsquic_set32_merge_set_elems(set, i);
}
else if (i + 1 < set->n_elems && set->elems[i + 1].low - 1 == value)
set->elems[i + 1].low = value;
else if (0 != lsquic_set32_insert_set_elem(set, i + 1, value))
return 0;
}
}
else
{
assert(NULL == set->elems);
if (0 != lsquic_set32_insert_set_elem(set, 0, value))
return -1;
}
#ifndef NDEBUG
lsquic_set32_check_elems_sorted(set);
#endif
return 0;
}
int
lsquic_set32_has (const struct lsquic_set32 *set, uint32_t value)
{
if (value < 64)
{
return !!(set->lowset & (1ULL << value));
}
int low, high, i;
low = 0, high = set->n_elems - 1;
while (low <= high)
{
i = low + (high - low) / 2;
if (set->elems[i].low <= value && set->elems[i].high >= value)
return 1;
else if (set->elems[i].high < value)
low = i + 1;
else
high = i - 1;
}
return 0;
}
/* ******* ******* ******** *******
*
* The following code is a set of two replacements:
*
* :.,$s/lsquic_set32/lsquic_set64/g
* :.,$s/uint32_t/uint64_t/g
*/
struct lsquic_set64_elem
{
uint64_t low, high;
};
void
lsquic_set64_init (struct lsquic_set64 *set)
{
memset(set, 0, sizeof(*set));
}
void
lsquic_set64_cleanup (struct lsquic_set64 *set)
{
free(set->elems);
}
static int
lsquic_set64_insert_set_elem (struct lsquic_set64 *set, int i, uint64_t value)
{
struct lsquic_set64_elem *elems;
if (set->n_elems == INT_MAX)
{
errno = EOVERFLOW;
return -1;
}
if (set->n_alloc == set->n_elems)
{
if (set->n_alloc)
set->n_alloc *= 2;
else
set->n_alloc = 4;
elems = realloc(set->elems, sizeof(set->elems[0]) * set->n_alloc);
if (!elems)
return -1;
set->elems = elems;
}
if (i < set->n_elems)
memmove(&set->elems[i + 1], &set->elems[i],
(set->n_elems - i) * sizeof(set->elems[i]));
set->elems[i].low = set->elems[i].high = value;
++set->n_elems;
return 0;
}
static void
lsquic_set64_merge_set_elems (struct lsquic_set64 *set, int i)
{
assert(i >= 0);
assert(i < set->n_elems - 1);
assert(set->elems[i].high + 1 == set->elems[i + 1].low);
set->elems[i].high = set->elems[i + 1].high;
if (i < set->n_elems - 2)
memmove(&set->elems[i + 1], &set->elems[i + 2],
(set->n_elems - i - 2) * sizeof(set->elems[i]));
--set->n_elems;
}
#ifndef NDEBUG
static void
lsquic_set64_check_elems_sorted (const struct lsquic_set64 *set)
{
int i;
for (i = 0; i < set->n_elems; ++i)
{
assert(set->elems[i].low <= set->elems[i].high);
if (i > 0)
assert(set->elems[i - 1].high + 1 < set->elems[i].low);
}
}
#endif
int
lsquic_set64_add (struct lsquic_set64 *set, uint64_t value)
{
if (value < 64)
{
set->lowset |= 1ULL << value;
return 0;
}
int low, high, i;
if (set->n_elems > 0)
{
low = 0, high = set->n_elems - 1;
do
{
i = low + (high - low) / 2;
if (set->elems[i].low <= value && set->elems[i].high >= value)
return 0;
else if (set->elems[i].high < value)
low = i + 1;
else
high = i - 1;
}
while (low <= high);
if (value < set->elems[i].low)
{
if (set->elems[i].low - 1 == value)
{
set->elems[i].low = value;
if (i > 0 && set->elems[i - 1].high + 1 == value)
lsquic_set64_merge_set_elems(set, i - 1);
}
else if (i > 0 && set->elems[i - 1].high + 1 == value)
set->elems[i - 1].high = value;
else if (0 != lsquic_set64_insert_set_elem(set, i, value))
return -1;
}
else
{
assert(value > set->elems[i].high);
if (set->elems[i].high + 1 == value)
{
set->elems[i].high = value;
if (i + 1 < set->n_elems && set->elems[i + 1].low - 1== value)
lsquic_set64_merge_set_elems(set, i);
}
else if (i + 1 < set->n_elems && set->elems[i + 1].low - 1 == value)
set->elems[i + 1].low = value;
else if (0 != lsquic_set64_insert_set_elem(set, i + 1, value))
return 0;
}
}
else
{
assert(NULL == set->elems);
if (0 != lsquic_set64_insert_set_elem(set, 0, value))
return -1;
}
#ifndef NDEBUG
lsquic_set64_check_elems_sorted(set);
#endif
return 0;
}
int
lsquic_set64_has (const struct lsquic_set64 *set, uint64_t value)
{
if (value < 64)
{
return !!(set->lowset & (1ULL << value));
}
int low, high, i;
low = 0, high = set->n_elems - 1;
while (low <= high)
{
i = low + (high - low) / 2;
if (set->elems[i].low <= value && set->elems[i].high >= value)
return 1;
else if (set->elems[i].high < value)
low = i + 1;
else
high = i - 1;
}
return 0;
}

View file

@ -0,0 +1,56 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_set.h -- A set implementation.
*
* There are two sets of APIs: one for four- and the other for eight-byte
* integers.
*/
#ifndef LSQUIC_SET_H
#define LSQUIC_SET_H 1
#include <stdint.h>
struct lsquic_set32_elem;
typedef struct lsquic_set32 {
struct lsquic_set32_elem *elems;
uint64_t lowset; /* Bitmask for values 0 - 63 */
int n_elems, n_alloc;
} lsquic_set32_t;
void
lsquic_set32_init (struct lsquic_set32 *);
void
lsquic_set32_cleanup (struct lsquic_set32 *);
int
lsquic_set32_add (struct lsquic_set32 *, uint32_t value);
/* Returns true if set contaims `value', false otherwise */
int
lsquic_set32_has (const struct lsquic_set32 *, uint32_t value);
struct lsquic_set64_elem;
typedef struct lsquic_set64 {
struct lsquic_set64_elem *elems;
uint64_t lowset; /* Bitmask for values 0 - 63 */
int n_elems, n_alloc;
} lsquic_set64_t;
void
lsquic_set64_init (struct lsquic_set64 *);
void
lsquic_set64_cleanup (struct lsquic_set64 *);
int
lsquic_set64_add (struct lsquic_set64 *, uint64_t value);
/* Returns true if set contaims `value', false otherwise */
int
lsquic_set64_has (const struct lsquic_set64 *, uint64_t value);
#endif

144
src/liblsquic/lsquic_sfcw.c Normal file
View file

@ -0,0 +1,144 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_conn_flow.h"
#include "lsquic_types.h"
#include "lsquic_rtt.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_conn_public.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#include "lsquic_util.h"
#include "lsquic_conn.h"
#include "lsquic_ev_log.h"
#define LSQUIC_LOGGER_MODULE LSQLM_SFCW
#define LSQUIC_LOG_CONN_ID fc->sf_conn_pub->lconn->cn_cid
#define LSQUIC_LOG_STREAM_ID fc->sf_stream_id
#include "lsquic_logger.h"
void
lsquic_sfcw_init (struct lsquic_sfcw *fc, unsigned max_recv_window,
struct lsquic_cfcw *cfcw, struct lsquic_conn_public *cpub,
unsigned stream_id)
{
memset(fc, 0, sizeof(*fc));
fc->sf_max_recv_win = max_recv_window;
fc->sf_cfcw = cfcw;
fc->sf_conn_pub = cpub;
fc->sf_stream_id = stream_id;
(void) lsquic_sfcw_fc_offsets_changed(fc);
}
static void
sfcw_maybe_increase_max_window (struct lsquic_sfcw *fc)
{
unsigned new_max_window, max_conn_window;
new_max_window = fc->sf_max_recv_win * 2;
/* Do not increase past explicitly specified maximum */
if (new_max_window > fc->sf_conn_pub->enpub->enp_settings.es_max_sfcw)
new_max_window = fc->sf_conn_pub->enpub->enp_settings.es_max_sfcw;
/* Do not increase past the connection's maximum window size. The
* connection's window will be increased separately, if possible.
*
* The reference implementation has the logic backwards: Imagine
* several concurrent streams that are not being read from fast
* enough by the user code. Each of them uses only a fraction
* of bandwidth. Does it mean that the connection window must
* increase? No.
*/
max_conn_window = lsquic_cfcw_get_max_recv_window(fc->sf_cfcw);
if (new_max_window > max_conn_window)
new_max_window = max_conn_window;
if (new_max_window > fc->sf_max_recv_win)
{
LSQ_DEBUG("max window increase %u -> %u",
fc->sf_max_recv_win, new_max_window);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID,
"max SFCW increase %u -> %u", fc->sf_max_recv_win,
new_max_window);
fc->sf_max_recv_win = new_max_window;
}
else
LSQ_DEBUG("max window could use an increase, but we're stuck "
"at %u", fc->sf_max_recv_win);
}
int
lsquic_sfcw_fc_offsets_changed (struct lsquic_sfcw *fc)
{
lsquic_time_t since_last_update, srtt, now;
if (fc->sf_recv_off - fc->sf_read_off >= fc->sf_max_recv_win / 2)
{
LSQ_DEBUG("recv_off has not changed, still at %"PRIu64,
fc->sf_recv_off);
return 0;
}
now = lsquic_time_now();
since_last_update = now - fc->sf_last_updated;
fc->sf_last_updated = now;
srtt = lsquic_rtt_stats_get_srtt(&fc->sf_conn_pub->rtt_stats);
if (since_last_update < srtt * 2)
sfcw_maybe_increase_max_window(fc);
fc->sf_recv_off = fc->sf_read_off + fc->sf_max_recv_win;
LSQ_DEBUG("recv_off changed: read_off: %"PRIu64"; "
"recv_off: %"PRIu64, fc->sf_read_off, fc->sf_recv_off);
return 1;
}
int
lsquic_sfcw_set_max_recv_off (struct lsquic_sfcw *fc, uint64_t max_recv_off)
{
if (max_recv_off <= fc->sf_recv_off)
{
if (!fc->sf_cfcw || lsquic_cfcw_incr_max_recv_off(fc->sf_cfcw,
max_recv_off - fc->sf_max_recv_off))
{
LSQ_DEBUG("max_recv_off goes from %"PRIu64" to %"PRIu64,
fc->sf_max_recv_off, max_recv_off);
fc->sf_max_recv_off = max_recv_off;
return 1;
}
else
{
/* cfcw prints its own warning */
return 0;
}
}
else
{
LSQ_WARN("flow control violation: received at offset %"PRIu64", "
"while flow control receive offset is %"PRIu64,
max_recv_off, fc->sf_recv_off);
return 0;
}
}
void
lsquic_sfcw_set_read_off (struct lsquic_sfcw *fc, uint64_t off)
{
if (fc->sf_cfcw)
lsquic_cfcw_incr_read_off(fc->sf_cfcw, off - fc->sf_read_off);
LSQ_DEBUG("read_off goes from %"PRIu64" to %"PRIu64,
fc->sf_read_off, off);
fc->sf_read_off = off;
}

View file

@ -0,0 +1,56 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_sfcw.h -- Stream flow control window functions
*/
#ifndef LSQUIC_SFCW_H
#define LSQUIC_SFCW_H 1
struct lsquic_cfcw;
struct lsquic_conn_public;
typedef struct lsquic_sfcw {
struct lsquic_cfcw *sf_cfcw; /* Connection flow control window,
* NULL for streams 1 and 3.
*/
uint64_t sf_max_recv_off; /* Largest offset observed */
uint64_t sf_recv_off; /* Flow control receive offset */
uint64_t sf_read_off; /* Number of bytes consumed */
lsquic_time_t sf_last_updated; /* Last time window was updated */
struct lsquic_conn_public
*sf_conn_pub;
unsigned sf_max_recv_win; /* Maximum receive window */
unsigned sf_stream_id; /* Used for logging */
} lsquic_sfcw_t;
void
lsquic_sfcw_init (lsquic_sfcw_t *, unsigned initial_max_recv_window,
struct lsquic_cfcw *cfcw, struct lsquic_conn_public *,
unsigned stream_id);
/* If update is to be sent, updates max_recv_off and returns true. Note
* that if you call this function twice, the second call will return false.
*/
int
lsquic_sfcw_fc_offsets_changed (lsquic_sfcw_t *);
#define lsquic_sfcw_get_fc_recv_off(fc) ((fc)->sf_recv_off)
#define lsquic_sfcw_get_max_recv_off(fc) ((fc)->sf_max_recv_off)
/* Returns false if flow control violation is encountered */
int
lsquic_sfcw_set_max_recv_off (lsquic_sfcw_t *, uint64_t);
/* Void because we do not expect the caller to make a mistake.
*/
void
lsquic_sfcw_set_read_off (lsquic_sfcw_t *, uint64_t);
#define lsquic_sfcw_consume_rem(sfcw) do { \
lsquic_sfcw_set_read_off(sfcw, \
lsquic_sfcw_get_max_recv_off(sfcw)); \
} while (0)
#endif

311
src/liblsquic/lsquic_spi.c Normal file
View file

@ -0,0 +1,311 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_spi.c - implementation of Stream Priority Iterator.
*/
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_spi.h"
#define LSQUIC_LOGGER_MODULE LSQLM_SPI
#define LSQUIC_LOG_CONN_ID iter->spi_cid
#include "lsquic_logger.h"
#define SPI_DEBUG(fmt, a...) LSQ_DEBUG("%s: " fmt, iter->spi_name, a)
#define NEXT_STREAM(stream, off) \
(* (struct lsquic_stream **) ((unsigned char *) (stream) + (off)))
static void
add_stream_to_spi (struct stream_prio_iter *iter, lsquic_stream_t *stream)
{
unsigned set, bit;
set = stream->sm_priority >> 6;
bit = stream->sm_priority & 0x3F;
if (!(iter->spi_set[set] & (1ULL << bit)))
{
iter->spi_set[set] |= 1ULL << bit;
TAILQ_INIT(&iter->spi_streams[ stream->sm_priority ]);
}
TAILQ_INSERT_TAIL(&iter->spi_streams[ stream->sm_priority ],
stream, next_prio_stream);
}
void
lsquic_spi_init_ext (struct stream_prio_iter *iter, struct lsquic_stream *first,
struct lsquic_stream *last, uintptr_t next_ptr_offset,
enum stream_flags onlist_mask,
int (*filter)(void *, struct lsquic_stream *),
void *filter_ctx, lsquic_cid_t cid, const char *name)
{
struct lsquic_stream *stream;
unsigned count;
iter->spi_cid = cid;
iter->spi_name = name ? name : "UNSET";
iter->spi_set[0] = 0;
iter->spi_set[1] = 0;
iter->spi_set[2] = 0;
iter->spi_set[3] = 0;
iter->spi_onlist_mask = onlist_mask;
iter->spi_flags = 0;
iter->spi_cur_prio = 0;
iter->spi_prev_stream = NULL;
iter->spi_next_stream = NULL;
stream = first;
count = 0;
if (filter)
while (1)
{
if (filter(filter_ctx, stream))
{
add_stream_to_spi(iter, stream);
++count;
}
if (stream == last)
break;
stream = NEXT_STREAM(stream, next_ptr_offset);
}
else
while (1)
{
add_stream_to_spi(iter, stream);
++count;
if (stream == last)
break;
stream = NEXT_STREAM(stream, next_ptr_offset);
}
if (count > 2)
SPI_DEBUG("initialized; # elems: %u; sets: [ %016"PRIX64", %016"PRIX64
", %016"PRIX64", %016"PRIX64" ]", count, iter->spi_set[0],
iter->spi_set[1], iter->spi_set[2], iter->spi_set[3]);
}
static int
find_and_set_lowest_priority (struct stream_prio_iter *iter)
{
unsigned set, prio;
uint64_t mask;
for (set = 0, prio = 0; set < 4; ++set, prio += 64)
if (iter->spi_set[ set ])
break;
if (set == 4)
{
//SPI_DEBUG("%s: cannot find any", __func__);
return -1;
}
mask = iter->spi_set[set];
if (!(mask & ((1ULL << 32) - 1))) { prio += 32; mask >>= 32; }
if (!(mask & ((1ULL << 16) - 1))) { prio += 16; mask >>= 16; }
if (!(mask & ((1ULL << 8) - 1))) { prio += 8; mask >>= 8; }
if (!(mask & ((1ULL << 4) - 1))) { prio += 4; mask >>= 4; }
if (!(mask & ((1ULL << 2) - 1))) { prio += 2; mask >>= 2; }
if (!(mask & ((1ULL << 1) - 1))) { prio += 1; }
#ifndef NDEBUG
unsigned bit;
set = prio >> 6;
bit = prio & 0x3F;
assert(iter->spi_set[ set ] & (1ULL << bit));
#endif
SPI_DEBUG("%s: prio %u -> %u", __func__, iter->spi_cur_prio, prio);
iter->spi_cur_prio = prio;
return 0;
}
static int
find_and_set_next_priority (struct stream_prio_iter *iter)
{
unsigned set, bit, prio;
uint64_t mask;
/* Examine values in the same set first */
set = iter->spi_cur_prio >> 6;
bit = iter->spi_cur_prio & 0x3F;
prio = 64 * set;
if (bit < 63)
{
mask = iter->spi_set[set];
mask &= ~((1ULL << (bit + 1)) - 1);
if (mask)
goto calc_priority;
}
++set;
prio += 64;
for (; set < 4; ++set, prio += 64)
if (iter->spi_set[ set ])
break;
if (set == 4)
{
//SPI_DEBUG("%s: cannot find any", __func__);
return -1;
}
mask = iter->spi_set[set];
calc_priority:
if (!(mask & ((1ULL << 32) - 1))) { prio += 32; mask >>= 32; }
if (!(mask & ((1ULL << 16) - 1))) { prio += 16; mask >>= 16; }
if (!(mask & ((1ULL << 8) - 1))) { prio += 8; mask >>= 8; }
if (!(mask & ((1ULL << 4) - 1))) { prio += 4; mask >>= 4; }
if (!(mask & ((1ULL << 2) - 1))) { prio += 2; mask >>= 2; }
if (!(mask & ((1ULL << 1) - 1))) { prio += 1; }
#ifndef NDEBUG
set = prio >> 6;
bit = prio & 0x3F;
assert(iter->spi_set[ set ] & (1ULL << bit));
#endif
SPI_DEBUG("%s: prio %u -> %u", __func__, iter->spi_cur_prio, prio);
iter->spi_cur_prio = prio;
return 0;
}
/* Each stream returned by the iterator is processed in some fashion. If,
* as a result of this, the stream gets taken off the original list, we
* have to follow suit and remove it from the iterator's set of streams.
*/
static void
maybe_evict_prev (struct stream_prio_iter *iter)
{
unsigned set, bit;
if (0 == (iter->spi_prev_stream->stream_flags & iter->spi_onlist_mask))
{
SPI_DEBUG("evict stream %u", iter->spi_prev_stream->id);
TAILQ_REMOVE(&iter->spi_streams[ iter->spi_prev_prio ],
iter->spi_prev_stream, next_prio_stream);
if (TAILQ_EMPTY(&iter->spi_streams[ iter->spi_prev_prio ]))
{
set = iter->spi_prev_prio >> 6;
bit = iter->spi_prev_prio & 0x3F;
iter->spi_set[ set ] &= ~(1ULL << bit);
SPI_DEBUG("priority %u now has no elements", iter->spi_prev_prio);
}
iter->spi_prev_stream = NULL;
}
}
lsquic_stream_t *
lsquic_spi_first (struct stream_prio_iter *iter)
{
lsquic_stream_t *stream;
unsigned set, bit;
if (iter->spi_prev_stream)
maybe_evict_prev(iter);
iter->spi_cur_prio = 0;
set = iter->spi_cur_prio >> 6;
bit = iter->spi_cur_prio & 0x3F;
if (!(iter->spi_set[set] & (1ULL << bit)))
{
if (0 != find_and_set_lowest_priority(iter))
{
SPI_DEBUG("%s: return NULL", __func__);
return NULL;
}
}
stream = TAILQ_FIRST(&iter->spi_streams[ iter->spi_cur_prio ]);
iter->spi_prev_prio = iter->spi_cur_prio;
iter->spi_prev_stream = stream;
iter->spi_next_stream = TAILQ_NEXT(stream, next_prio_stream);
if (stream->id != 1 && stream->id != 3)
SPI_DEBUG("%s: return stream %u, priority %u", __func__, stream->id,
iter->spi_cur_prio);
return stream;
}
lsquic_stream_t *
lsquic_spi_next (struct stream_prio_iter *iter)
{
lsquic_stream_t *stream;
if (iter->spi_prev_stream)
maybe_evict_prev(iter);
stream = iter->spi_next_stream;
if (stream)
{
assert(iter->spi_prev_prio == iter->spi_cur_prio);
iter->spi_prev_stream = stream;
iter->spi_next_stream = TAILQ_NEXT(stream, next_prio_stream);
if (stream->id != 1 && stream->id != 3)
SPI_DEBUG("%s: return stream %u, priority %u", __func__, stream->id,
iter->spi_cur_prio);
return stream;
}
if (iter->spi_flags & SPI_EXHAUST_PRIO)
{
stream = TAILQ_FIRST(&iter->spi_streams[ iter->spi_cur_prio ]);
if (stream)
{
iter->spi_prev_stream = stream;
iter->spi_next_stream = TAILQ_NEXT(stream, next_prio_stream);
if (stream->id != 1 && stream->id != 3)
SPI_DEBUG("%s: return stream %u, priority %u", __func__,
stream->id, iter->spi_cur_prio);
return stream;
}
else
{
SPI_DEBUG("%s: priority %u empty, call first again", __func__,
iter->spi_cur_prio);
return lsquic_spi_first(iter);
}
}
if (0 != find_and_set_next_priority(iter))
{
//SPI_DEBUG("%s: return NULL", __func__);
return NULL;
}
stream = TAILQ_FIRST(&iter->spi_streams[ iter->spi_cur_prio ]);
iter->spi_prev_prio = iter->spi_cur_prio;
iter->spi_prev_stream = stream;
iter->spi_next_stream = TAILQ_NEXT(stream, next_prio_stream);
if (stream->id != 1 && stream->id != 3)
SPI_DEBUG("%s: return stream %u, priority %u", __func__, stream->id,
iter->spi_cur_prio);
return stream;
}
void
lsquic_spi_exhaust_on (struct stream_prio_iter *iter)
{
SPI_DEBUG("%s: exhaust %d -> 1", __func__,
!!(iter->spi_flags & SPI_EXHAUST_PRIO));
iter->spi_flags |= SPI_EXHAUST_PRIO;
}

Some files were not shown because too many files have changed in this diff Show more