lsquic_trans_params.c revision 02b6086d
1/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * lsquic_trans_params.c
4 */
5
6#include <assert.h>
7#include <errno.h>
8#include <inttypes.h>
9#include <stddef.h>
10#include <stdint.h>
11#include <string.h>
12
13#include <arpa/inet.h>
14#include <sys/socket.h>
15
16#include "lsquic_byteswap.h"
17#include "lsquic_int_types.h"
18#include "lsquic_types.h"
19#include "lsquic_version.h"
20#include "lsquic_sizes.h"
21#include "lsquic_trans_params.h"
22#include "lsquic_util.h"
23#include "lsquic_varint.h"
24
25#define LSQUIC_LOGGER_MODULE LSQLM_TRAPA
26#include "lsquic_logger.h"
27
28
29static const uint64_t def_vals[MAX_TPI + 1] =
30{
31    [TPI_MAX_PACKET_SIZE]                   =  TP_DEF_MAX_PACKET_SIZE,
32    [TPI_ACK_DELAY_EXPONENT]                =  TP_DEF_ACK_DELAY_EXP,
33    [TPI_INIT_MAX_STREAMS_UNI]              =  TP_DEF_INIT_MAX_STREAMS_UNI,
34    [TPI_INIT_MAX_STREAMS_BIDI]             =  TP_DEF_INIT_MAX_STREAMS_BIDI,
35    [TPI_INIT_MAX_DATA]                     =  TP_DEF_INIT_MAX_DATA,
36    [TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL]   =  TP_DEF_INIT_MAX_STREAM_DATA_BIDI_LOCAL,
37    [TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE]  =  TP_DEF_INIT_MAX_STREAM_DATA_BIDI_REMOTE,
38    [TPI_INIT_MAX_STREAM_DATA_UNI]          =  TP_DEF_INIT_MAX_STREAM_DATA_UNI,
39    [TPI_IDLE_TIMEOUT]                      =  TP_DEF_IDLE_TIMEOUT,
40    [TPI_MAX_ACK_DELAY]                     =  TP_DEF_MAX_ACK_DELAY,
41    [TPI_ACTIVE_CONNECTION_ID_LIMIT]        =  TP_DEF_ACTIVE_CONNECTION_ID_LIMIT,
42};
43
44
45static const uint64_t max_vals[MAX_TPI + 1] =
46{
47    [TPI_MAX_PACKET_SIZE]                   =  VINT_MAX_VALUE,
48    [TPI_ACK_DELAY_EXPONENT]                =  VINT_MAX_VALUE,
49    [TPI_INIT_MAX_STREAMS_UNI]              =  VINT_MAX_VALUE,
50    [TPI_INIT_MAX_STREAMS_BIDI]             =  VINT_MAX_VALUE,
51    [TPI_INIT_MAX_DATA]                     =  VINT_MAX_VALUE,
52    [TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL]   =  VINT_MAX_VALUE,
53    [TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE]  =  VINT_MAX_VALUE,
54    [TPI_INIT_MAX_STREAM_DATA_UNI]          =  VINT_MAX_VALUE,
55    [TPI_IDLE_TIMEOUT]                      =  VINT_MAX_VALUE,
56    [TPI_MAX_ACK_DELAY]                     =  TP_MAX_MAX_ACK_DELAY,
57    [TPI_ACTIVE_CONNECTION_ID_LIMIT]        =  VINT_MAX_VALUE,
58};
59
60
61#define TP_OFF(name_) ((uint64_t *) &((struct transport_params *) 0 \
62    )->tp_numerics_u.s.name_ - (uint64_t *) &((struct transport_params *) \
63    0)->tp_numerics_u.s)
64
65/* Map enum transport_params to index of tp_numerics_u.a; for numeric values only */
66static const unsigned tpi2idx[MAX_TPI + 1] =
67{
68    [TPI_MAX_PACKET_SIZE]                   =  TP_OFF(max_packet_size),
69    [TPI_ACK_DELAY_EXPONENT]                =  TP_OFF(ack_delay_exponent),
70    [TPI_INIT_MAX_STREAMS_UNI]              =  TP_OFF(init_max_streams_uni),
71    [TPI_INIT_MAX_STREAMS_BIDI]             =  TP_OFF(init_max_streams_bidi),
72    [TPI_INIT_MAX_DATA]                     =  TP_OFF(init_max_data),
73    [TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL]   =  TP_OFF(init_max_stream_data_bidi_local),
74    [TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE]  =  TP_OFF(init_max_stream_data_bidi_remote),
75    [TPI_INIT_MAX_STREAM_DATA_UNI]          =  TP_OFF(init_max_stream_data_uni),
76    [TPI_IDLE_TIMEOUT]                      =  TP_OFF(idle_timeout),
77    [TPI_MAX_ACK_DELAY]                     =  TP_OFF(max_ack_delay),
78    [TPI_ACTIVE_CONNECTION_ID_LIMIT]        =  TP_OFF(active_connection_id_limit),
79};
80
81
82static size_t
83preferred_address_size (const struct transport_params *params)
84{
85    return sizeof(params->tp_preferred_address.ipv4_addr)
86         + sizeof(params->tp_preferred_address.ipv4_port)
87         + sizeof(params->tp_preferred_address.ipv6_addr)
88         + sizeof(params->tp_preferred_address.ipv6_port)
89         + 1 + params->tp_preferred_address.cid.len
90         + sizeof(params->tp_preferred_address.srst)
91         ;
92}
93
94
95int
96lsquic_tp_encode (const struct transport_params *params,
97                  unsigned char *const buf, size_t bufsz)
98{
99    unsigned char *p;
100    size_t need = 2;
101    uint16_t u16;
102    enum transport_param_id tpi;
103    unsigned bits[MAX_TPI + 1];
104
105    if (params->tp_flags & TRAPA_SERVER)
106    {
107        if (params->tp_flags & TRAPA_ORIGINAL_CID)
108            need += 4 + params->tp_original_cid.len;
109        if (params->tp_flags & TRAPA_RESET_TOKEN)
110            need += 4 + sizeof(params->tp_stateless_reset_token);
111        if (params->tp_flags & (TRAPA_PREFADDR_IPv4|TRAPA_PREFADDR_IPv6))
112            need += 4 + preferred_address_size(params);
113    }
114#if LSQUIC_TEST_QUANTUM_READINESS
115    else if (params->tp_flags & TRAPA_QUANTUM_READY)
116        need += 4 + QUANTUM_READY_SZ;
117#endif
118
119    for (tpi = 0; tpi <= MAX_TPI; ++tpi)
120        if ((NUMERIC_TRANS_PARAMS & (1 << tpi))
121                    && params->tp_numerics_u.a[tpi2idx[tpi]] != def_vals[tpi])
122        {
123            if (params->tp_numerics_u.a[tpi2idx[tpi]] < max_vals[tpi])
124            {
125                bits[tpi] = vint_val2bits(params->tp_numerics_u.a[tpi2idx[tpi]]);
126                need += 4 + (1 << bits[tpi]);
127            }
128            else
129            {
130                LSQ_DEBUG("numeric value is too large (%"PRIu64" vs maximum "
131                    "of %"PRIu64, params->tp_numerics_u.a[tpi2idx[tpi]],
132                    max_vals[tpi]);
133                return -1;
134            }
135        }
136
137    if (params->tp_disable_active_migration != TP_DEF_DISABLE_ACTIVE_MIGRATION)
138        need += 4 + 0;
139
140    if (params->tp_flags & TRAPA_QL_BITS)
141        need += 4 + 0;
142
143    if (need > bufsz || need > UINT16_MAX)
144    {
145        errno = ENOBUFS;
146        return -1;
147    }
148
149    p = buf;
150
151#define WRITE_TO_P(src, len) do {                                       \
152    memcpy(p, src, len);                                                \
153    p += len;                                                           \
154} while (0)
155
156#if __BYTE_ORDER == __LITTLE_ENDIAN
157#define WRITE_UINT_TO_P(val, width) do {                                \
158    u##width = bswap_##width(val);                                      \
159    WRITE_TO_P(&u##width, sizeof(u##width));                            \
160} while (0)
161#else
162#define WRITE_UINT_TO_P(val, width) do {                                \
163    u##width = val;                                                     \
164    WRITE_TO_P(&u##width, sizeof(u##width));                            \
165} while (0)
166#endif
167
168#define WRITE_PARAM_TO_P(tpidx, tpval, width) do {                      \
169    WRITE_UINT_TO_P(tpidx, 16);                                         \
170    WRITE_UINT_TO_P(width / 8, 16);                                     \
171    if (width > 8)                                                      \
172        WRITE_UINT_TO_P(tpval, width);                                  \
173    else if (width)                                                     \
174        *p++ = tpval;                                                   \
175} while (0)
176
177    WRITE_UINT_TO_P(need - 2 + buf - p, 16);
178
179    for (tpi = 0; tpi <= MAX_TPI; ++tpi)
180        if (NUMERIC_TRANS_PARAMS & (1 << tpi))
181        {
182            if (params->tp_numerics_u.a[tpi2idx[tpi]] != def_vals[tpi])
183            {
184                WRITE_UINT_TO_P(tpi, 16);
185                WRITE_UINT_TO_P(1 << bits[tpi], 16);
186                vint_write(p, params->tp_numerics_u.a[tpi2idx[tpi]], bits[tpi],
187                                                                1 << bits[tpi]);
188                p += 1 << bits[tpi];
189            }
190        }
191        else
192            switch (tpi)
193            {
194            case TPI_ORIGINAL_CONNECTION_ID:
195                if (params->tp_flags & TRAPA_ORIGINAL_CID)
196                {
197                    WRITE_UINT_TO_P(TPI_ORIGINAL_CONNECTION_ID, 16);
198                    WRITE_UINT_TO_P(params->tp_original_cid.len, 16);
199                    WRITE_TO_P(params->tp_original_cid.idbuf,
200                                                params->tp_original_cid.len);
201                }
202                break;
203            case TPI_STATELESS_RESET_TOKEN:
204                if (params->tp_flags & TRAPA_RESET_TOKEN)
205                {
206                    WRITE_UINT_TO_P(TPI_STATELESS_RESET_TOKEN, 16);
207                    WRITE_UINT_TO_P(sizeof(params->tp_stateless_reset_token),
208                                                                            16);
209                    WRITE_TO_P(params->tp_stateless_reset_token,
210                                    sizeof(params->tp_stateless_reset_token));
211                }
212                break;
213            case TPI_PREFERRED_ADDRESS:
214                if (params->tp_flags
215                                & (TRAPA_PREFADDR_IPv4|TRAPA_PREFADDR_IPv6))
216                {
217                    WRITE_UINT_TO_P(TPI_PREFERRED_ADDRESS, 16);
218                    WRITE_UINT_TO_P(preferred_address_size(params), 16);
219                    if (params->tp_flags & TRAPA_PREFADDR_IPv4)
220                    {
221                        WRITE_TO_P(&params->tp_preferred_address.ipv4_addr,
222                                sizeof(params->tp_preferred_address.ipv4_addr));
223                        WRITE_UINT_TO_P(params->tp_preferred_address.ipv4_port,
224                                                                            16);
225                    }
226                    else
227                    {
228                        memset(p, 0, 6);
229                        p += 6;
230                    }
231                    if (params->tp_flags & TRAPA_PREFADDR_IPv6)
232                    {
233                        WRITE_TO_P(&params->tp_preferred_address.ipv6_addr,
234                                sizeof(params->tp_preferred_address.ipv6_addr));
235                        WRITE_UINT_TO_P(params->tp_preferred_address.ipv6_port,
236                                                                            16);
237                    }
238                    else
239                    {
240                        memset(p, 0, 18);
241                        p += 18;
242                    }
243                    *p++ = params->tp_preferred_address.cid.len;
244                    WRITE_TO_P(params->tp_preferred_address.cid.idbuf,
245                                        params->tp_preferred_address.cid.len);
246                    WRITE_TO_P(params->tp_preferred_address.srst,
247                                    sizeof(params->tp_preferred_address.srst));
248                }
249                break;
250            case TPI_DISABLE_ACTIVE_MIGRATION:
251                if (params->tp_disable_active_migration != TP_DEF_DISABLE_ACTIVE_MIGRATION)
252                {
253                    WRITE_UINT_TO_P(TPI_DISABLE_ACTIVE_MIGRATION, 16);
254                    WRITE_UINT_TO_P(0, 16);
255                }
256                break;
257            default:
258                assert(0);
259                return -1;
260            }
261
262    if (params->tp_flags & TRAPA_QL_BITS)
263    {
264        WRITE_UINT_TO_P(TPI_QL_BITS, 16);
265        WRITE_UINT_TO_P(0, 16);
266    }
267
268#if LSQUIC_TEST_QUANTUM_READINESS
269    if (params->tp_flags & TRAPA_QUANTUM_READY)
270    {
271        WRITE_UINT_TO_P(TPI_QUANTUM_READINESS, 16);
272        WRITE_UINT_TO_P(QUANTUM_READY_SZ, 16);
273        memset(p, 'Q', QUANTUM_READY_SZ);
274        p += QUANTUM_READY_SZ;
275    }
276#endif
277
278    assert(buf + need == p);
279    return (int) (p - buf);
280
281#undef WRITE_TO_P
282#undef WRITE_UINT_TO_P
283}
284
285
286int
287lsquic_tp_decode (const unsigned char *const buf, size_t bufsz,
288                  int is_server,
289                  struct transport_params *params)
290{
291    const unsigned char *p, *end, *q;
292    uint16_t len, param_id, tlen;
293    unsigned set_of_ids;
294    int s;
295
296    p = buf;
297    end = buf + bufsz;
298
299    *params = TP_INITIALIZER();
300
301    if (is_server)
302        params->tp_flags |= TRAPA_SERVER;
303
304    if (end - p < 2)
305        return -1;
306    READ_UINT(len, 16, p, 2);
307    p += 2;
308    if (len > end - p)
309        return -1;
310    end = p + len;
311
312#define EXPECT_LEN(expected_len) do {                               \
313    if (expected_len != len)                                        \
314        return -1;                                                  \
315} while (0)
316
317#define EXPECT_AT_LEAST(expected_len) do {                          \
318    if ((expected_len) > (uintptr_t) (p + len - q))                 \
319        return -1;                                                  \
320} while (0)
321
322    set_of_ids = 0;
323    while (p < end)
324    {
325        READ_UINT(param_id, 16, p, 2);
326        p += 2;
327        READ_UINT(len, 16, p, 2);
328        p += 2;
329        if (len > end - p)
330            return -1;
331        /* If we need to support parameter IDs 31 and up, we will need to
332         * change this code:
333         */
334        if (param_id < sizeof(set_of_ids) * 8)
335        {
336            /* Only check duplicates for IDs <= 31: all standard parameters
337             * fit in a bitmask 32 bits wide.
338             */
339            if (set_of_ids & (1 << param_id))
340                return -1;
341            set_of_ids |= 1 << param_id;
342        }
343        else
344            goto gt32;
345        if (NUMERIC_TRANS_PARAMS & (1u << param_id))
346        {
347            switch (len)
348            {
349            case 1:
350            case 2:
351            case 4:
352            case 8:
353                s = vint_read(p, p + len,
354                            &params->tp_numerics_u.a[tpi2idx[param_id]]);
355                if (s == len)
356                {
357                    if (params->tp_numerics_u.a[tpi2idx[param_id]]
358                                                        > max_vals[param_id])
359                    {
360                        LSQ_DEBUG("numeric value of parameter 0x%X is too "
361                            "large (%"PRIu64" vs maximum of %"PRIu64,
362                            param_id,
363                            params->tp_numerics_u.a[tpi2idx[param_id]],
364                            max_vals[param_id]);
365                        return -1;
366                    }
367                    p += s;
368                    break;
369                }
370                else
371                {
372                    LSQ_DEBUG("cannot read the value of numeric transport "
373                                        "param %u of length %u", param_id, len);
374                    return -1;
375                }
376            default:
377                LSQ_DEBUG("invalid length=%u for numeric transport parameter",
378                                                                        len);
379                return -1;
380            }
381        }
382        else
383        {
384  gt32:     switch (param_id)
385            {
386            case TPI_DISABLE_ACTIVE_MIGRATION:
387                EXPECT_LEN(0);
388                params->tp_disable_active_migration = 1;
389                break;
390            case TPI_STATELESS_RESET_TOKEN:
391                /* Client MUST not include reset token,
392                 * see [draft-ietf-quic-transport-11], Section 6.4.1
393                 */
394                if (!is_server)
395                    return -1;
396                EXPECT_LEN(sizeof(params->tp_stateless_reset_token));
397                memcpy(params->tp_stateless_reset_token, p,
398                                    sizeof(params->tp_stateless_reset_token));
399                params->tp_flags |= TRAPA_RESET_TOKEN;
400                break;
401            case TPI_ORIGINAL_CONNECTION_ID:
402                /* Client MUST not original connecti ID,
403                 * see [draft-ietf-quic-transport-15], Section 6.6.1
404                 */
405                if (!is_server)
406                    return -1;
407                if (len > MAX_CID_LEN)
408                    return -1;
409                memcpy(params->tp_original_cid.idbuf, p, len);
410                params->tp_original_cid.len = len;
411                params->tp_flags |= TRAPA_ORIGINAL_CID;
412                break;
413            case TPI_PREFERRED_ADDRESS:
414                /* Client MUST not include preferred address,
415                 * see [draft-ietf-quic-transport-12], Section 6.4.1
416                 */
417                if (!is_server)
418                    return -1;
419                q = p;
420                EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv4_addr));
421                memcpy(params->tp_preferred_address.ipv4_addr, q,
422                            sizeof(params->tp_preferred_address.ipv4_addr));
423                q += sizeof(params->tp_preferred_address.ipv4_addr);
424                EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv4_port));
425                READ_UINT(params->tp_preferred_address.ipv4_port, 16, q, 2);
426                q += 2;
427                EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv6_addr));
428                memcpy(params->tp_preferred_address.ipv6_addr, q,
429                            sizeof(params->tp_preferred_address.ipv6_addr));
430                q += sizeof(params->tp_preferred_address.ipv6_addr);
431                EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv6_port));
432                READ_UINT(params->tp_preferred_address.ipv6_port, 16, q, 2);
433                q += 2;
434                EXPECT_AT_LEAST(1);
435                tlen = *q;
436                q += 1;
437                if (tlen < 4 || tlen > MAX_CID_LEN)
438                {
439                    LSQ_DEBUG("preferred server address contains invalid "
440                        "CID length of %"PRIu16" bytes", tlen);
441                    return -1;
442                }
443                EXPECT_AT_LEAST(tlen);
444                memcpy(params->tp_preferred_address.cid.idbuf, q, tlen);
445                params->tp_preferred_address.cid.len = tlen;
446                q += tlen;
447                EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.srst));
448                memcpy(params->tp_preferred_address.srst, q,
449                                sizeof(params->tp_preferred_address.srst));
450                q += sizeof(params->tp_preferred_address.srst);
451                if (q != p + len)
452                    return -1;
453                if (params->tp_preferred_address.ipv4_port
454                    && !lsquic_is_zero(params->tp_preferred_address.ipv4_addr,
455                                sizeof(params->tp_preferred_address.ipv4_addr)))
456                    params->tp_flags |= TRAPA_PREFADDR_IPv4;
457                if (params->tp_preferred_address.ipv6_port
458                    && !lsquic_is_zero(params->tp_preferred_address.ipv6_addr,
459                                sizeof(params->tp_preferred_address.ipv6_addr)))
460                    params->tp_flags |= TRAPA_PREFADDR_IPv6;
461                break;
462            case TPI_QL_BITS:
463                EXPECT_LEN(0);
464                params->tp_flags |= TRAPA_QL_BITS;
465                break;
466            }
467            p += len;
468        }
469    }
470
471    if (p != end)
472        return -1;
473
474    return (int) (end - buf);
475#undef EXPECT_LEN
476}
477
478
479void
480lsquic_tp_to_str (const struct transport_params *params, char *buf, size_t sz)
481{
482    char *const end = buf + sz;
483    int nw;
484    char tok_str[sizeof(params->tp_stateless_reset_token) * 2 + 1];
485    char addr_str[INET6_ADDRSTRLEN];
486
487#define SEMICOLON "; "
488#define WRITE_ONE_PARAM(name, fmt) do {  \
489    nw = snprintf(buf, end - buf, #name ": " fmt SEMICOLON, params->tp_##name); \
490    buf += nw; \
491    if (buf >= end) \
492        return; \
493} while (0)
494
495    WRITE_ONE_PARAM(init_max_stream_data_bidi_local, "%"PRIu64);
496    WRITE_ONE_PARAM(init_max_stream_data_bidi_remote, "%"PRIu64);
497    WRITE_ONE_PARAM(init_max_stream_data_uni, "%"PRIu64);
498    WRITE_ONE_PARAM(init_max_data, "%"PRIu64);
499    WRITE_ONE_PARAM(idle_timeout, "%"PRIu64);
500    WRITE_ONE_PARAM(init_max_streams_bidi, "%"PRIu64);
501    WRITE_ONE_PARAM(init_max_streams_uni, "%"PRIu64);
502    WRITE_ONE_PARAM(max_packet_size, "%"PRIu64);
503    WRITE_ONE_PARAM(ack_delay_exponent, "%"PRIu64);
504    WRITE_ONE_PARAM(active_connection_id_limit, "%"PRIu64);
505    WRITE_ONE_PARAM(disable_active_migration, "%hhd");
506#undef SEMICOLON
507#define SEMICOLON ""
508    WRITE_ONE_PARAM(max_ack_delay, "%"PRIu64);
509    if (params->tp_flags & TRAPA_RESET_TOKEN)
510    {
511        lsquic_hexstr(params->tp_stateless_reset_token,
512            sizeof(params->tp_stateless_reset_token), tok_str, sizeof(tok_str));
513        nw = snprintf(buf, end - buf, "; stateless_reset_token: %s", tok_str);
514        buf += nw;
515        if (buf >= end)
516            return;
517    }
518    if (params->tp_flags & TRAPA_RESET_TOKEN)
519    {
520        char cidbuf_[MAX_CID_LEN * 2 + 1];
521        nw = snprintf(buf, end - buf, "; original DCID (ODCID): %"CID_FMT,
522                                        CID_BITS(&params->tp_original_cid));
523        buf += nw;
524        if (buf >= end)
525            return;
526    }
527    if (params->tp_flags & TRAPA_PREFADDR_IPv4)
528    {
529        if (inet_ntop(AF_INET, params->tp_preferred_address.ipv4_addr,
530                                                addr_str, sizeof(addr_str)))
531        {
532            nw = snprintf(buf, end - buf, "; IPv4 preferred address: %s:%u",
533                            addr_str, params->tp_preferred_address.ipv4_port);
534            buf += nw;
535            if (buf >= end)
536                return;
537        }
538    }
539    if (params->tp_flags & TRAPA_PREFADDR_IPv6)
540    {
541        if (inet_ntop(AF_INET6, params->tp_preferred_address.ipv6_addr,
542                                                addr_str, sizeof(addr_str)))
543        {
544            nw = snprintf(buf, end - buf, "; IPv6 preferred address: %s:%u",
545                            addr_str, params->tp_preferred_address.ipv6_port);
546            buf += nw;
547            if (buf >= end)
548                return;
549        }
550    }
551    if (params->tp_flags & TRAPA_QL_BITS)
552    {
553        nw = snprintf(buf, end - buf, "; QL loss bits");
554        buf += nw;
555        if (buf >= end)
556            return;
557    }
558
559#undef SEMICOLON
560#undef WRITE_ONE_PARAM
561}
562