litespeed-quic/src/liblsquic/lsquic_trans_params.c

1051 lines
39 KiB
C

/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_trans_params.c
*/
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include "lsquic_byteswap.h"
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_version.h"
#include "lsquic_sizes.h"
#include "lsquic_trans_params.h"
#include "lsquic_util.h"
#include "lsquic_varint.h"
#define LSQUIC_LOGGER_MODULE LSQLM_TRAPA
#include "lsquic_logger.h"
static enum transport_param_id
tpi_val_2_enum (uint64_t tpi_val)
{
switch (tpi_val)
{
case 0: return TPI_ORIGINAL_CONNECTION_ID;
case 1: return TPI_MAX_IDLE_TIMEOUT;
case 2: return TPI_STATELESS_RESET_TOKEN;
case 3: return TPI_MAX_PACKET_SIZE;
case 4: return TPI_INIT_MAX_DATA;
case 5: return TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL;
case 6: return TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE;
case 7: return TPI_INIT_MAX_STREAM_DATA_UNI;
case 8: return TPI_INIT_MAX_STREAMS_BIDI;
case 9: return TPI_INIT_MAX_STREAMS_UNI;
case 10: return TPI_ACK_DELAY_EXPONENT;
case 11: return TPI_MAX_ACK_DELAY;
case 12: return TPI_DISABLE_ACTIVE_MIGRATION;
case 13: return TPI_PREFERRED_ADDRESS;
case 14: return TPI_ACTIVE_CONNECTION_ID_LIMIT;
#if LSQUIC_TEST_QUANTUM_READINESS
case 0xC37: return TPI_QUANTUM_READINESS;
#endif
case 0x1057: return TPI_LOSS_BITS;
case 0xDE1A: return TPI_MIN_ACK_DELAY;
case 0x7157: return TPI_TIMESTAMPS;
default: return INT_MAX;
}
}
static const unsigned enum_2_tpi_val[LAST_TPI + 1] =
{
[TPI_ORIGINAL_CONNECTION_ID] = 0x0,
[TPI_MAX_IDLE_TIMEOUT] = 0x1,
[TPI_STATELESS_RESET_TOKEN] = 0x2,
[TPI_MAX_PACKET_SIZE] = 0x3,
[TPI_INIT_MAX_DATA] = 0x4,
[TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL] = 0x5,
[TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE] = 0x6,
[TPI_INIT_MAX_STREAM_DATA_UNI] = 0x7,
[TPI_INIT_MAX_STREAMS_BIDI] = 0x8,
[TPI_INIT_MAX_STREAMS_UNI] = 0x9,
[TPI_ACK_DELAY_EXPONENT] = 0xA,
[TPI_MAX_ACK_DELAY] = 0xB,
[TPI_DISABLE_ACTIVE_MIGRATION] = 0xC,
[TPI_PREFERRED_ADDRESS] = 0xD,
[TPI_ACTIVE_CONNECTION_ID_LIMIT] = 0xE,
#if LSQUIC_TEST_QUANTUM_READINESS
[TPI_QUANTUM_READINESS] = 0xC37,
#endif
[TPI_LOSS_BITS] = 0x1057,
[TPI_MIN_ACK_DELAY] = 0xDE1A,
[TPI_TIMESTAMPS] = 0x7157,
};
static const char * const tpi2str[LAST_TPI + 1] =
{
[TPI_ORIGINAL_CONNECTION_ID] = "original_connection_id",
[TPI_MAX_IDLE_TIMEOUT] = "max_idle_timeout",
[TPI_STATELESS_RESET_TOKEN] = "stateless_reset_token",
[TPI_MAX_PACKET_SIZE] = "max_packet_size",
[TPI_INIT_MAX_DATA] = "init_max_data",
[TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL] = "init_max_stream_data_bidi_local",
[TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE] = "init_max_stream_data_bidi_remote",
[TPI_INIT_MAX_STREAM_DATA_UNI] = "init_max_stream_data_uni",
[TPI_INIT_MAX_STREAMS_BIDI] = "init_max_streams_bidi",
[TPI_INIT_MAX_STREAMS_UNI] = "init_max_streams_uni",
[TPI_ACK_DELAY_EXPONENT] = "ack_delay_exponent",
[TPI_MAX_ACK_DELAY] = "max_ack_delay",
[TPI_DISABLE_ACTIVE_MIGRATION] = "disable_active_migration",
[TPI_PREFERRED_ADDRESS] = "preferred_address",
[TPI_ACTIVE_CONNECTION_ID_LIMIT] = "active_connection_id_limit",
#if LSQUIC_TEST_QUANTUM_READINESS
[TPI_QUANTUM_READINESS] = "quantum_readiness",
#endif
[TPI_LOSS_BITS] = "loss_bits",
[TPI_MIN_ACK_DELAY] = "min_ack_delay",
[TPI_TIMESTAMPS] = "timestamps",
};
static const uint64_t def_vals[MAX_NUM_WITH_DEF_TPI + 1] =
{
[TPI_MAX_PACKET_SIZE] = TP_DEF_MAX_PACKET_SIZE,
[TPI_ACK_DELAY_EXPONENT] = TP_DEF_ACK_DELAY_EXP,
[TPI_INIT_MAX_STREAMS_UNI] = TP_DEF_INIT_MAX_STREAMS_UNI,
[TPI_INIT_MAX_STREAMS_BIDI] = TP_DEF_INIT_MAX_STREAMS_BIDI,
[TPI_INIT_MAX_DATA] = TP_DEF_INIT_MAX_DATA,
[TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL] = TP_DEF_INIT_MAX_STREAM_DATA_BIDI_LOCAL,
[TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE] = TP_DEF_INIT_MAX_STREAM_DATA_BIDI_REMOTE,
[TPI_INIT_MAX_STREAM_DATA_UNI] = TP_DEF_INIT_MAX_STREAM_DATA_UNI,
[TPI_MAX_IDLE_TIMEOUT] = TP_DEF_MAX_IDLE_TIMEOUT,
[TPI_MAX_ACK_DELAY] = TP_DEF_MAX_ACK_DELAY,
[TPI_ACTIVE_CONNECTION_ID_LIMIT] = TP_DEF_ACTIVE_CONNECTION_ID_LIMIT,
};
static const uint64_t max_vals[MAX_NUMERIC_TPI + 1] =
{
[TPI_MAX_PACKET_SIZE] = VINT_MAX_VALUE,
[TPI_ACK_DELAY_EXPONENT] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAMS_UNI] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAMS_BIDI] = VINT_MAX_VALUE,
[TPI_INIT_MAX_DATA] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAM_DATA_UNI] = VINT_MAX_VALUE,
[TPI_MAX_IDLE_TIMEOUT] = VINT_MAX_VALUE,
[TPI_MAX_ACK_DELAY] = TP_MAX_MAX_ACK_DELAY,
[TPI_ACTIVE_CONNECTION_ID_LIMIT] = VINT_MAX_VALUE,
[TPI_LOSS_BITS] = 1,
[TPI_MIN_ACK_DELAY] = (1u << 24) - 1u,
};
static const uint64_t min_vals[MAX_NUMERIC_TPI + 1] =
{
[TPI_MIN_ACK_DELAY] = 1,
};
static size_t
preferred_address_size (const struct transport_params *params)
{
return sizeof(params->tp_preferred_address.ipv4_addr)
+ sizeof(params->tp_preferred_address.ipv4_port)
+ sizeof(params->tp_preferred_address.ipv6_addr)
+ sizeof(params->tp_preferred_address.ipv6_port)
+ 1 + params->tp_preferred_address.cid.len
+ sizeof(params->tp_preferred_address.srst)
;
}
int
lsquic_tp_has_pref_ipv4 (const struct transport_params *params)
{
return (params->tp_set & (1 << TPI_PREFERRED_ADDRESS))
&& params->tp_preferred_address.ipv4_port
&& !lsquic_is_zero(params->tp_preferred_address.ipv4_addr,
sizeof(params->tp_preferred_address.ipv4_addr));
}
int
lsquic_tp_has_pref_ipv6 (const struct transport_params *params)
{
return (params->tp_set & (1 << TPI_PREFERRED_ADDRESS))
&& params->tp_preferred_address.ipv6_port
&& !lsquic_is_zero(params->tp_preferred_address.ipv6_addr,
sizeof(params->tp_preferred_address.ipv6_addr));
}
int
lsquic_tp_encode (const struct transport_params *params, int is_server,
unsigned char *const buf, size_t bufsz)
{
unsigned char *p;
size_t need;
uint16_t u16;
enum transport_param_id tpi;
unsigned set;
unsigned bits[LAST_TPI + 1][3 /* ID, length, value */];
need = 0;
set = params->tp_set; /* Will turn bits off for default values */
if (is_server)
{
if (set & (1 << TPI_ORIGINAL_CONNECTION_ID))
{
bits[TPI_ORIGINAL_CONNECTION_ID][0]
= vint_val2bits(enum_2_tpi_val[TPI_ORIGINAL_CONNECTION_ID]);
#if __GNUC__
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#if __clang__
#pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"
#else
#pragma GCC diagnostic ignored "-Wtype-limits"
#endif
#endif
bits[TPI_ORIGINAL_CONNECTION_ID][1]
= vint_val2bits(params->tp_original_cid.len);
#if __GNUC__
#pragma GCC diagnostic pop
#pragma GCC diagnostic pop
#endif
need += (1 << bits[TPI_ORIGINAL_CONNECTION_ID][0])
+ (1 << bits[TPI_ORIGINAL_CONNECTION_ID][1])
+ params->tp_original_cid.len;
}
if (set & (1 << TPI_STATELESS_RESET_TOKEN))
{
bits[TPI_STATELESS_RESET_TOKEN][0]
= vint_val2bits(enum_2_tpi_val[TPI_STATELESS_RESET_TOKEN]);
bits[TPI_STATELESS_RESET_TOKEN][1]
= vint_val2bits(sizeof(params->tp_stateless_reset_token));
need += (1 << bits[TPI_STATELESS_RESET_TOKEN][0])
+ (1 << bits[TPI_STATELESS_RESET_TOKEN][1])
+ sizeof(params->tp_stateless_reset_token);
}
if (set & (1 << TPI_PREFERRED_ADDRESS))
{
bits[TPI_PREFERRED_ADDRESS][0]
= vint_val2bits(enum_2_tpi_val[TPI_PREFERRED_ADDRESS]);
bits[TPI_PREFERRED_ADDRESS][1] = vint_val2bits(
preferred_address_size(params));
need += (1 << bits[TPI_PREFERRED_ADDRESS][0])
+ (1 << bits[TPI_PREFERRED_ADDRESS][1])
+ preferred_address_size(params);
}
}
#if LSQUIC_TEST_QUANTUM_READINESS
else if (set & (1 << TPI_QUANTUM_READINESS))
{
bits[TPI_QUANTUM_READINESS][0]
= vint_val2bits(enum_2_tpi_val[TPI_QUANTUM_READINESS]);
bits[TPI_QUANTUM_READINESS][1] = vint_val2bits(QUANTUM_READY_SZ);
need += (1 << bits[TPI_QUANTUM_READINESS][0])
+ (1 << bits[TPI_QUANTUM_READINESS][1])
+ QUANTUM_READY_SZ;
}
#endif
for (tpi = 0; tpi <= MAX_NUMERIC_TPI; ++tpi)
if (set & (1 << tpi))
{
if (tpi > MAX_NUM_WITH_DEF_TPI
|| params->tp_numerics[tpi] != def_vals[tpi])
{
if (params->tp_numerics[tpi] >= min_vals[tpi]
&& params->tp_numerics[tpi] <= max_vals[tpi])
{
bits[tpi][0] = vint_val2bits(enum_2_tpi_val[tpi]);
bits[tpi][2] = vint_val2bits(params->tp_numerics[tpi]);
bits[tpi][1] = vint_val2bits(bits[tpi][2]);
need += (1 << bits[tpi][0])
+ (1 << bits[tpi][1])
+ (1 << bits[tpi][2]);
}
else if (params->tp_numerics[tpi] > max_vals[tpi])
{
LSQ_DEBUG("numeric value of %s is too large (%"PRIu64" vs "
"maximum of %"PRIu64")", tpi2str[tpi],
params->tp_numerics[tpi], max_vals[tpi]);
return -1;
}
else
{
LSQ_DEBUG("numeric value of %s is too small (%"PRIu64" vs "
"minimum " "of %"PRIu64")",
tpi2str[tpi], params->tp_numerics[tpi], min_vals[tpi]);
return -1;
}
}
else
set &= ~(1 << tpi); /* Don't write default value */
}
for (; tpi <= MAX_EMPTY_TPI; ++tpi)
if (set & (1 << tpi))
{
bits[tpi][0] = vint_val2bits(enum_2_tpi_val[tpi]);
need += (1 << bits[tpi][0]) + 1 /* Zero length byte */;
}
if (need > bufsz || need > UINT16_MAX)
{
errno = ENOBUFS;
return -1;
}
p = buf;
#define WRITE_TO_P(src, len) do { \
memcpy(p, src, len); \
p += len; \
} while (0)
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define WRITE_UINT_TO_P(val, width) do { \
u##width = bswap_##width(val); \
WRITE_TO_P(&u##width, sizeof(u##width)); \
} while (0)
#else
#define WRITE_UINT_TO_P(val, width) do { \
u##width = val; \
WRITE_TO_P(&u##width, sizeof(u##width)); \
} while (0)
#endif
for (tpi = 0; tpi <= LAST_TPI; ++tpi)
if (set & (1 << tpi))
{
vint_write(p, enum_2_tpi_val[tpi], bits[tpi][0],
1 << bits[tpi][0]);
p += 1 << bits[tpi][0];
switch (tpi)
{
case TPI_MAX_IDLE_TIMEOUT:
case TPI_MAX_PACKET_SIZE:
case TPI_INIT_MAX_DATA:
case TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL:
case TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE:
case TPI_INIT_MAX_STREAM_DATA_UNI:
case TPI_INIT_MAX_STREAMS_BIDI:
case TPI_INIT_MAX_STREAMS_UNI:
case TPI_ACK_DELAY_EXPONENT:
case TPI_MAX_ACK_DELAY:
case TPI_ACTIVE_CONNECTION_ID_LIMIT:
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
vint_write(p, 1 << bits[tpi][2], bits[tpi][1],
1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
vint_write(p, params->tp_numerics[tpi], bits[tpi][2],
1 << bits[tpi][2]);
p += 1 << bits[tpi][2];
break;
case TPI_ORIGINAL_CONNECTION_ID:
vint_write(p, params->tp_original_cid.len, bits[tpi][1],
1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
WRITE_TO_P(params->tp_original_cid.idbuf,
params->tp_original_cid.len);
break;
case TPI_STATELESS_RESET_TOKEN:
vint_write(p, sizeof(params->tp_stateless_reset_token),
bits[tpi][1], 1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
WRITE_TO_P(params->tp_stateless_reset_token,
sizeof(params->tp_stateless_reset_token));
break;
case TPI_PREFERRED_ADDRESS:
vint_write(p, preferred_address_size(params),
bits[tpi][1], 1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
WRITE_UINT_TO_P(preferred_address_size(params), 16);
WRITE_TO_P(&params->tp_preferred_address.ipv4_addr,
sizeof(params->tp_preferred_address.ipv4_addr));
WRITE_UINT_TO_P(params->tp_preferred_address.ipv4_port, 16);
WRITE_TO_P(&params->tp_preferred_address.ipv6_addr,
sizeof(params->tp_preferred_address.ipv6_addr));
WRITE_UINT_TO_P(params->tp_preferred_address.ipv6_port, 16);
*p++ = params->tp_preferred_address.cid.len;
WRITE_TO_P(params->tp_preferred_address.cid.idbuf,
params->tp_preferred_address.cid.len);
WRITE_TO_P(params->tp_preferred_address.srst,
sizeof(params->tp_preferred_address.srst));
break;
case TPI_DISABLE_ACTIVE_MIGRATION:
case TPI_TIMESTAMPS:
*p++ = 0;
break;
#if LSQUIC_TEST_QUANTUM_READINESS
case TPI_QUANTUM_READINESS:
vint_write(p, QUANTUM_READY_SZ,
bits[tpi][1], 1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
memset(p, 'Q', QUANTUM_READY_SZ);
p += QUANTUM_READY_SZ;
break;
#endif
}
}
assert(buf + need == p);
return (int) (p - buf);
#undef WRITE_TO_P
#undef WRITE_UINT_TO_P
}
int
lsquic_tp_decode (const unsigned char *const buf, size_t bufsz,
int is_server,
struct transport_params *params)
{
const unsigned char *p, *end, *q;
uint64_t len, param_id;
uint16_t tlen;
enum transport_param_id tpi;
unsigned set_of_ids;
int s;
p = buf;
end = buf + bufsz;
*params = TP_INITIALIZER();
#define EXPECT_LEN(expected_len) do { \
if (expected_len != len) \
return -1; \
} while (0)
#define EXPECT_AT_LEAST(expected_len) do { \
if ((expected_len) > (uintptr_t) (p + len - q)) \
return -1; \
} while (0)
set_of_ids = 0;
while (p < end)
{
s = vint_read(p, end, &param_id);
if (s < 0)
return -1;
p += s;
s = vint_read(p, end, &len);
if (s < 0)
return -1;
p += s;
if ((ptrdiff_t) len > end - p)
return -1;
tpi = tpi_val_2_enum(param_id);
if (tpi <= LAST_TPI)
{
if (set_of_ids & (1 << tpi))
return -1;
set_of_ids |= 1 << tpi;
}
switch (tpi)
{
case TPI_MAX_IDLE_TIMEOUT:
case TPI_MAX_PACKET_SIZE:
case TPI_INIT_MAX_DATA:
case TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL:
case TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE:
case TPI_INIT_MAX_STREAM_DATA_UNI:
case TPI_INIT_MAX_STREAMS_BIDI:
case TPI_INIT_MAX_STREAMS_UNI:
case TPI_ACK_DELAY_EXPONENT:
case TPI_MAX_ACK_DELAY:
case TPI_ACTIVE_CONNECTION_ID_LIMIT:
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
switch (len)
{
case 1:
case 2:
case 4:
case 8:
s = vint_read(p, p + len, &params->tp_numerics[tpi]);
if (s == (int) len)
{
if (params->tp_numerics[tpi] > max_vals[tpi])
{
LSQ_DEBUG("numeric value of %s is too large "
"(%"PRIu64" vs maximum of %"PRIu64, tpi2str[tpi],
params->tp_numerics[tpi], max_vals[tpi]);
return -1;
}
else if (params->tp_numerics[tpi] < min_vals[tpi])
{
LSQ_DEBUG("numeric value of %s is too small "
"(%"PRIu64" vs minimum of %"PRIu64, tpi2str[tpi],
params->tp_numerics[tpi], min_vals[tpi]);
return -1;
}
break;
}
else
{
LSQ_DEBUG("cannot read the value of numeric transport "
"param %s of length %"PRIu64, tpi2str[tpi], len);
return -1;
}
default:
LSQ_DEBUG("invalid length=%"PRIu64" for numeric transport "
"parameter %s", len, tpi2str[tpi]);
return -1;
}
break;
case TPI_DISABLE_ACTIVE_MIGRATION:
case TPI_TIMESTAMPS:
EXPECT_LEN(0);
break;
case TPI_STATELESS_RESET_TOKEN:
/* Client MUST not include reset token,
* see [draft-ietf-quic-transport-11], Section 6.4.1
*/
if (!is_server)
return -1;
EXPECT_LEN(sizeof(params->tp_stateless_reset_token));
memcpy(params->tp_stateless_reset_token, p,
sizeof(params->tp_stateless_reset_token));
break;
case TPI_ORIGINAL_CONNECTION_ID:
/* Client MUST not original connecti ID,
* see [draft-ietf-quic-transport-15], Section 6.6.1
*/
if (!is_server)
return -1;
if (len > MAX_CID_LEN)
return -1;
memcpy(params->tp_original_cid.idbuf, p, len);
params->tp_original_cid.len = len;
break;
case TPI_PREFERRED_ADDRESS:
/* Client MUST not include preferred address,
* see [draft-ietf-quic-transport-12], Section 6.4.1
*/
if (!is_server)
return -1;
q = p;
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv4_addr));
memcpy(params->tp_preferred_address.ipv4_addr, q,
sizeof(params->tp_preferred_address.ipv4_addr));
q += sizeof(params->tp_preferred_address.ipv4_addr);
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv4_port));
READ_UINT(params->tp_preferred_address.ipv4_port, 16, q, 2);
q += 2;
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv6_addr));
memcpy(params->tp_preferred_address.ipv6_addr, q,
sizeof(params->tp_preferred_address.ipv6_addr));
q += sizeof(params->tp_preferred_address.ipv6_addr);
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv6_port));
READ_UINT(params->tp_preferred_address.ipv6_port, 16, q, 2);
q += 2;
EXPECT_AT_LEAST(1);
tlen = *q;
q += 1;
if (tlen < 4 || tlen > MAX_CID_LEN)
{
LSQ_DEBUG("preferred server address contains invalid "
"CID length of %"PRIu16" bytes", tlen);
return -1;
}
EXPECT_AT_LEAST(tlen);
memcpy(params->tp_preferred_address.cid.idbuf, q, tlen);
params->tp_preferred_address.cid.len = tlen;
q += tlen;
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.srst));
memcpy(params->tp_preferred_address.srst, q,
sizeof(params->tp_preferred_address.srst));
q += sizeof(params->tp_preferred_address.srst);
if (q != p + len)
return -1;
break;
default:
/* Do nothing: skip this transport parameter */
break;
}
p += len;
if (tpi <= LAST_TPI)
{
params->tp_set |= 1 << tpi;
params->tp_decoded |= 1 << tpi;
}
}
if (p != end)
return -1;
if ((params->tp_set & (1 << TPI_MIN_ACK_DELAY))
&& params->tp_numerics[TPI_MIN_ACK_DELAY]
> params->tp_numerics[TPI_MAX_ACK_DELAY] * 1000)
{
LSQ_DEBUG("min_ack_delay (%"PRIu64" usec) is larger than "
"max_ack_delay (%"PRIu64" ms)",
params->tp_numerics[TPI_MIN_ACK_DELAY],
params->tp_numerics[TPI_MAX_ACK_DELAY]);
return -1;
}
return (int) (end - buf);
#undef EXPECT_LEN
}
void
lsquic_tp_to_str (const struct transport_params *params, char *buf, size_t sz)
{
char *const end = buf + sz;
int nw;
enum transport_param_id tpi;
char tok_str[sizeof(params->tp_stateless_reset_token) * 2 + 1];
char addr_str[INET6_ADDRSTRLEN];
for (tpi = 0; tpi <= MAX_NUMERIC_TPI; ++tpi)
if (params->tp_set & (1 << tpi))
{
nw = snprintf(buf, end - buf, "%.*s%s: %"PRIu64,
(buf + sz > end) << 1, "; ", tpi2str[tpi],
params->tp_numerics[tpi]);
buf += nw;
if (buf >= end)
return;
}
for (; tpi <= MAX_EMPTY_TPI; ++tpi)
if (params->tp_set & (1 << tpi))
{
nw = snprintf(buf, end - buf, "%.*s%s",
(buf + sz > end) << 1, "; ", tpi2str[tpi]);
buf += nw;
if (buf >= end)
return;
}
#if LSQUIC_TEST_QUANTUM_READINESS
if (params->tp_set & (1 << TPI_QUANTUM_READINESS))
{
nw = snprintf(buf, end - buf, "%.*s%s",
(buf + sz > end) << 1, "; ", tpi2str[TPI_QUANTUM_READINESS]);
buf += nw;
if (buf >= end)
return;
}
#endif
if (params->tp_set & (1 << TPI_STATELESS_RESET_TOKEN))
{
lsquic_hexstr(params->tp_stateless_reset_token,
sizeof(params->tp_stateless_reset_token), tok_str, sizeof(tok_str));
nw = snprintf(buf, end - buf, "; stateless_reset_token: %s", tok_str);
buf += nw;
if (buf >= end)
return;
}
if (params->tp_set & (1 << TPI_ORIGINAL_CONNECTION_ID))
{
char cidbuf_[MAX_CID_LEN * 2 + 1];
nw = snprintf(buf, end - buf, "; original DCID (ODCID): %"CID_FMT,
CID_BITS(&params->tp_original_cid));
buf += nw;
if (buf >= end)
return;
}
if (lsquic_tp_has_pref_ipv4(params))
{
if (inet_ntop(AF_INET, params->tp_preferred_address.ipv4_addr,
addr_str, sizeof(addr_str)))
{
nw = snprintf(buf, end - buf, "; IPv4 preferred address: %s:%u",
addr_str, params->tp_preferred_address.ipv4_port);
buf += nw;
if (buf >= end)
return;
}
}
if (lsquic_tp_has_pref_ipv6(params))
{
if (inet_ntop(AF_INET6, params->tp_preferred_address.ipv6_addr,
addr_str, sizeof(addr_str)))
{
nw = snprintf(buf, end - buf, "; IPv6 preferred address: %s:%u",
addr_str, params->tp_preferred_address.ipv6_port);
buf += nw;
if (buf >= end)
return;
}
}
}
int
lsquic_tp_encode_id25 (const struct transport_params *params, int is_server,
unsigned char *const buf, size_t bufsz)
{
unsigned char *p;
size_t need = 2;
uint16_t u16;
enum transport_param_id tpi;
unsigned set;
unsigned bits[MAX_NUMERIC_TPI + 1];
set = params->tp_set; /* Will turn bits off for default values */
if (is_server)
{
if (set & (1 << TPI_ORIGINAL_CONNECTION_ID))
need += 4 + params->tp_original_cid.len;
if (set & (1 << TPI_STATELESS_RESET_TOKEN))
need += 4 + sizeof(params->tp_stateless_reset_token);
if (set & (1 << TPI_PREFERRED_ADDRESS))
need += 4 + preferred_address_size(params);
}
#if LSQUIC_TEST_QUANTUM_READINESS
else if (set & (1 << TPI_QUANTUM_READINESS))
need += 4 + QUANTUM_READY_SZ;
#endif
for (tpi = 0; tpi <= MAX_NUMERIC_TPI; ++tpi)
if (set & (1 << tpi))
{
if (tpi > MAX_NUM_WITH_DEF_TPI
|| params->tp_numerics[tpi] != def_vals[tpi])
{
if (params->tp_numerics[tpi] >= min_vals[tpi]
&& params->tp_numerics[tpi] <= max_vals[tpi])
{
bits[tpi] = vint_val2bits(params->tp_numerics[tpi]);
need += 4 + (1 << bits[tpi]);
}
else if (params->tp_numerics[tpi] > max_vals[tpi])
{
LSQ_DEBUG("numeric value of %s is too large (%"PRIu64" vs "
"maximum of %"PRIu64")", tpi2str[tpi],
params->tp_numerics[tpi], max_vals[tpi]);
return -1;
}
else
{
LSQ_DEBUG("numeric value of %s is too small (%"PRIu64" vs "
"minimum " "of %"PRIu64")",
tpi2str[tpi], params->tp_numerics[tpi], min_vals[tpi]);
return -1;
}
}
else
set &= ~(1 << tpi); /* Don't write default value */
}
for (; tpi <= MAX_EMPTY_TPI; ++tpi)
if (set & (1 << tpi))
need += 4 + 0;
if (need > bufsz || need > UINT16_MAX)
{
errno = ENOBUFS;
return -1;
}
p = buf;
#define WRITE_TO_P(src, len) do { \
memcpy(p, src, len); \
p += len; \
} while (0)
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define WRITE_UINT_TO_P(val, width) do { \
u##width = bswap_##width(val); \
WRITE_TO_P(&u##width, sizeof(u##width)); \
} while (0)
#else
#define WRITE_UINT_TO_P(val, width) do { \
u##width = val; \
WRITE_TO_P(&u##width, sizeof(u##width)); \
} while (0)
#endif
#define WRITE_PARAM_TO_P(tpidx, tpval, width) do { \
WRITE_UINT_TO_P(tpidx, 16); \
WRITE_UINT_TO_P(width / 8, 16); \
if (width > 8) \
WRITE_UINT_TO_P(tpval, width); \
else if (width) \
*p++ = tpval; \
} while (0)
WRITE_UINT_TO_P(need - 2 + buf - p, 16);
for (tpi = 0; tpi <= LAST_TPI; ++tpi)
if (set & (1 << tpi))
{
WRITE_UINT_TO_P(enum_2_tpi_val[tpi], 16);
switch (tpi)
{
case TPI_MAX_IDLE_TIMEOUT:
case TPI_MAX_PACKET_SIZE:
case TPI_INIT_MAX_DATA:
case TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL:
case TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE:
case TPI_INIT_MAX_STREAM_DATA_UNI:
case TPI_INIT_MAX_STREAMS_BIDI:
case TPI_INIT_MAX_STREAMS_UNI:
case TPI_ACK_DELAY_EXPONENT:
case TPI_MAX_ACK_DELAY:
case TPI_ACTIVE_CONNECTION_ID_LIMIT:
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
WRITE_UINT_TO_P(1 << bits[tpi], 16);
vint_write(p, params->tp_numerics[tpi], bits[tpi],
1 << bits[tpi]);
p += 1 << bits[tpi];
break;
case TPI_ORIGINAL_CONNECTION_ID:
WRITE_UINT_TO_P(params->tp_original_cid.len, 16);
WRITE_TO_P(params->tp_original_cid.idbuf,
params->tp_original_cid.len);
break;
case TPI_STATELESS_RESET_TOKEN:
WRITE_UINT_TO_P(sizeof(params->tp_stateless_reset_token), 16);
WRITE_TO_P(params->tp_stateless_reset_token,
sizeof(params->tp_stateless_reset_token));
break;
case TPI_PREFERRED_ADDRESS:
WRITE_UINT_TO_P(preferred_address_size(params), 16);
WRITE_TO_P(&params->tp_preferred_address.ipv4_addr,
sizeof(params->tp_preferred_address.ipv4_addr));
WRITE_UINT_TO_P(params->tp_preferred_address.ipv4_port, 16);
WRITE_TO_P(&params->tp_preferred_address.ipv6_addr,
sizeof(params->tp_preferred_address.ipv6_addr));
WRITE_UINT_TO_P(params->tp_preferred_address.ipv6_port, 16);
*p++ = params->tp_preferred_address.cid.len;
WRITE_TO_P(params->tp_preferred_address.cid.idbuf,
params->tp_preferred_address.cid.len);
WRITE_TO_P(params->tp_preferred_address.srst,
sizeof(params->tp_preferred_address.srst));
break;
case TPI_DISABLE_ACTIVE_MIGRATION:
case TPI_TIMESTAMPS:
WRITE_UINT_TO_P(0, 16);
break;
#if LSQUIC_TEST_QUANTUM_READINESS
case TPI_QUANTUM_READINESS:
WRITE_UINT_TO_P(QUANTUM_READY_SZ, 16);
memset(p, 'Q', QUANTUM_READY_SZ);
p += QUANTUM_READY_SZ;
break;
#endif
}
}
assert(buf + need == p);
return (int) (p - buf);
#undef WRITE_TO_P
#undef WRITE_UINT_TO_P
}
int
lsquic_tp_decode_id25 (const unsigned char *const buf, size_t bufsz,
int is_server,
struct transport_params *params)
{
const unsigned char *p, *end, *q;
uint16_t len, param_id, tlen;
enum transport_param_id tpi;
unsigned set_of_ids;
int s;
p = buf;
end = buf + bufsz;
*params = TP_INITIALIZER();
if (end - p < 2)
return -1;
READ_UINT(len, 16, p, 2);
p += 2;
if (len > end - p)
return -1;
end = p + len;
#define EXPECT_LEN(expected_len) do { \
if (expected_len != len) \
return -1; \
} while (0)
#define EXPECT_AT_LEAST(expected_len) do { \
if ((expected_len) > (uintptr_t) (p + len - q)) \
return -1; \
} while (0)
set_of_ids = 0;
while (p + 4 <= end)
{
READ_UINT(param_id, 16, p, 2);
p += 2;
READ_UINT(len, 16, p, 2);
p += 2;
if (len > end - p)
return -1;
tpi = tpi_val_2_enum(param_id);
if (tpi <= LAST_TPI)
{
if (set_of_ids & (1 << tpi))
return -1;
set_of_ids |= 1 << tpi;
}
switch (tpi)
{
case TPI_MAX_IDLE_TIMEOUT:
case TPI_MAX_PACKET_SIZE:
case TPI_INIT_MAX_DATA:
case TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL:
case TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE:
case TPI_INIT_MAX_STREAM_DATA_UNI:
case TPI_INIT_MAX_STREAMS_BIDI:
case TPI_INIT_MAX_STREAMS_UNI:
case TPI_ACK_DELAY_EXPONENT:
case TPI_MAX_ACK_DELAY:
case TPI_ACTIVE_CONNECTION_ID_LIMIT:
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
switch (len)
{
case 1:
case 2:
case 4:
case 8:
s = vint_read(p, p + len, &params->tp_numerics[tpi]);
if (s == len)
{
if (params->tp_numerics[tpi] > max_vals[tpi])
{
LSQ_DEBUG("numeric value of %s is too large "
"(%"PRIu64" vs maximum of %"PRIu64, tpi2str[tpi],
params->tp_numerics[tpi], max_vals[tpi]);
return -1;
}
else if (params->tp_numerics[tpi] < min_vals[tpi])
{
LSQ_DEBUG("numeric value of %s is too small "
"(%"PRIu64" vs minimum of %"PRIu64, tpi2str[tpi],
params->tp_numerics[tpi], min_vals[tpi]);
return -1;
}
break;
}
else
{
LSQ_DEBUG("cannot read the value of numeric transport "
"param %u of length %u", param_id, len);
return -1;
}
default:
LSQ_DEBUG("invalid length=%u for numeric transport "
"parameter 0x%X", len, param_id);
return -1;
}
break;
case TPI_DISABLE_ACTIVE_MIGRATION:
case TPI_TIMESTAMPS:
EXPECT_LEN(0);
break;
case TPI_STATELESS_RESET_TOKEN:
/* Client MUST not include reset token,
* see [draft-ietf-quic-transport-11], Section 6.4.1
*/
if (!is_server)
return -1;
EXPECT_LEN(sizeof(params->tp_stateless_reset_token));
memcpy(params->tp_stateless_reset_token, p,
sizeof(params->tp_stateless_reset_token));
break;
case TPI_ORIGINAL_CONNECTION_ID:
/* Client MUST not original connecti ID,
* see [draft-ietf-quic-transport-15], Section 6.6.1
*/
if (!is_server)
return -1;
if (len > MAX_CID_LEN)
return -1;
memcpy(params->tp_original_cid.idbuf, p, len);
params->tp_original_cid.len = len;
break;
case TPI_PREFERRED_ADDRESS:
/* Client MUST not include preferred address,
* see [draft-ietf-quic-transport-12], Section 6.4.1
*/
if (!is_server)
return -1;
q = p;
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv4_addr));
memcpy(params->tp_preferred_address.ipv4_addr, q,
sizeof(params->tp_preferred_address.ipv4_addr));
q += sizeof(params->tp_preferred_address.ipv4_addr);
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv4_port));
READ_UINT(params->tp_preferred_address.ipv4_port, 16, q, 2);
q += 2;
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv6_addr));
memcpy(params->tp_preferred_address.ipv6_addr, q,
sizeof(params->tp_preferred_address.ipv6_addr));
q += sizeof(params->tp_preferred_address.ipv6_addr);
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv6_port));
READ_UINT(params->tp_preferred_address.ipv6_port, 16, q, 2);
q += 2;
EXPECT_AT_LEAST(1);
tlen = *q;
q += 1;
if (tlen < 4 || tlen > MAX_CID_LEN)
{
LSQ_DEBUG("preferred server address contains invalid "
"CID length of %"PRIu16" bytes", tlen);
return -1;
}
EXPECT_AT_LEAST(tlen);
memcpy(params->tp_preferred_address.cid.idbuf, q, tlen);
params->tp_preferred_address.cid.len = tlen;
q += tlen;
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.srst));
memcpy(params->tp_preferred_address.srst, q,
sizeof(params->tp_preferred_address.srst));
q += sizeof(params->tp_preferred_address.srst);
if (q != p + len)
return -1;
break;
default:
/* Do nothing: skip this transport parameter */
break;
}
p += len;
if (tpi <= LAST_TPI)
{
params->tp_set |= 1 << tpi;
params->tp_decoded |= 1 << tpi;
}
}
if (p != end)
return -1;
if ((params->tp_set & (1 << TPI_MIN_ACK_DELAY))
&& params->tp_numerics[TPI_MIN_ACK_DELAY]
> params->tp_numerics[TPI_MAX_ACK_DELAY] * 1000)
{
LSQ_DEBUG("min_ack_delay (%"PRIu64" usec) is larger than "
"max_ack_delay (%"PRIu64" ms)",
params->tp_numerics[TPI_MIN_ACK_DELAY],
params->tp_numerics[TPI_MAX_ACK_DELAY]);
return -1;
}
return (int) (end - buf);
#undef EXPECT_LEN
}