lsquic_trans_params.c revision 03e6b668
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 (need > bufsz || need > UINT16_MAX)
141    {
142        errno = ENOBUFS;
143        return -1;
144    }
145
146    p = buf;
147
148#define WRITE_TO_P(src, len) do {                                       \
149    memcpy(p, src, len);                                                \
150    p += len;                                                           \
151} while (0)
152
153#if __BYTE_ORDER == __LITTLE_ENDIAN
154#define WRITE_UINT_TO_P(val, width) do {                                \
155    u##width = bswap_##width(val);                                      \
156    WRITE_TO_P(&u##width, sizeof(u##width));                            \
157} while (0)
158#else
159#define WRITE_UINT_TO_P(val, width) do {                                \
160    u##width = val;                                                     \
161    WRITE_TO_P(&u##width, sizeof(u##width));                            \
162} while (0)
163#endif
164
165#define WRITE_PARAM_TO_P(tpidx, tpval, width) do {                      \
166    WRITE_UINT_TO_P(tpidx, 16);                                         \
167    WRITE_UINT_TO_P(width / 8, 16);                                     \
168    if (width > 8)                                                      \
169        WRITE_UINT_TO_P(tpval, width);                                  \
170    else if (width)                                                     \
171        *p++ = tpval;                                                   \
172} while (0)
173
174    WRITE_UINT_TO_P(need - 2 + buf - p, 16);
175
176    for (tpi = 0; tpi <= MAX_TPI; ++tpi)
177        if (NUMERIC_TRANS_PARAMS & (1 << tpi))
178        {
179            if (params->tp_numerics_u.a[tpi2idx[tpi]] != def_vals[tpi])
180            {
181                WRITE_UINT_TO_P(tpi, 16);
182                WRITE_UINT_TO_P(1 << bits[tpi], 16);
183                vint_write(p, params->tp_numerics_u.a[tpi2idx[tpi]], bits[tpi],
184                                                                1 << bits[tpi]);
185                p += 1 << bits[tpi];
186            }
187        }
188        else
189            switch (tpi)
190            {
191            case TPI_ORIGINAL_CONNECTION_ID:
192                if (params->tp_flags & TRAPA_ORIGINAL_CID)
193                {
194                    WRITE_UINT_TO_P(TPI_ORIGINAL_CONNECTION_ID, 16);
195                    WRITE_UINT_TO_P(params->tp_original_cid.len, 16);
196                    WRITE_TO_P(params->tp_original_cid.idbuf,
197                                                params->tp_original_cid.len);
198                }
199                break;
200            case TPI_STATELESS_RESET_TOKEN:
201                if (params->tp_flags & TRAPA_RESET_TOKEN)
202                {
203                    WRITE_UINT_TO_P(TPI_STATELESS_RESET_TOKEN, 16);
204                    WRITE_UINT_TO_P(sizeof(params->tp_stateless_reset_token),
205                                                                            16);
206                    WRITE_TO_P(params->tp_stateless_reset_token,
207                                    sizeof(params->tp_stateless_reset_token));
208                }
209                break;
210            case TPI_PREFERRED_ADDRESS:
211                if (params->tp_flags
212                                & (TRAPA_PREFADDR_IPv4|TRAPA_PREFADDR_IPv6))
213                {
214                    WRITE_UINT_TO_P(TPI_PREFERRED_ADDRESS, 16);
215                    WRITE_UINT_TO_P(preferred_address_size(params), 16);
216                    if (params->tp_flags & TRAPA_PREFADDR_IPv4)
217                    {
218                        WRITE_TO_P(&params->tp_preferred_address.ipv4_addr,
219                                sizeof(params->tp_preferred_address.ipv4_addr));
220                        WRITE_UINT_TO_P(params->tp_preferred_address.ipv4_port,
221                                                                            16);
222                    }
223                    else
224                    {
225                        memset(p, 0, 6);
226                        p += 6;
227                    }
228                    if (params->tp_flags & TRAPA_PREFADDR_IPv6)
229                    {
230                        WRITE_TO_P(&params->tp_preferred_address.ipv6_addr,
231                                sizeof(params->tp_preferred_address.ipv6_addr));
232                        WRITE_UINT_TO_P(params->tp_preferred_address.ipv6_port,
233                                                                            16);
234                    }
235                    else
236                    {
237                        memset(p, 0, 18);
238                        p += 18;
239                    }
240                    *p++ = params->tp_preferred_address.cid.len;
241                    WRITE_TO_P(params->tp_preferred_address.cid.idbuf,
242                                        params->tp_preferred_address.cid.len);
243                    WRITE_TO_P(params->tp_preferred_address.srst,
244                                    sizeof(params->tp_preferred_address.srst));
245                }
246                break;
247            case TPI_DISABLE_ACTIVE_MIGRATION:
248                if (params->tp_disable_active_migration != TP_DEF_DISABLE_ACTIVE_MIGRATION)
249                {
250                    WRITE_UINT_TO_P(TPI_DISABLE_ACTIVE_MIGRATION, 16);
251                    WRITE_UINT_TO_P(0, 16);
252                }
253                break;
254            default:
255                assert(0);
256                return -1;
257            }
258
259#if LSQUIC_TEST_QUANTUM_READINESS
260    if (params->tp_flags & TRAPA_QUANTUM_READY)
261    {
262        WRITE_UINT_TO_P(TPI_QUANTUM_READINESS, 16);
263        WRITE_UINT_TO_P(QUANTUM_READY_SZ, 16);
264        memset(p, 'Q', QUANTUM_READY_SZ);
265        p += QUANTUM_READY_SZ;
266    }
267#endif
268
269    assert(buf + need == p);
270    return (int) (p - buf);
271
272#undef WRITE_TO_P
273#undef WRITE_UINT_TO_P
274}
275
276
277int
278lsquic_tp_decode (const unsigned char *const buf, size_t bufsz,
279                  int is_server,
280                  struct transport_params *params)
281{
282    const unsigned char *p, *end, *q;
283    uint16_t len, param_id, tlen;
284    unsigned set_of_ids;
285    int s;
286
287    p = buf;
288    end = buf + bufsz;
289
290    *params = TP_INITIALIZER();
291
292    if (is_server)
293        params->tp_flags |= TRAPA_SERVER;
294
295    if (end - p < 2)
296        return -1;
297    READ_UINT(len, 16, p, 2);
298    p += 2;
299    if (len > end - p)
300        return -1;
301    end = p + len;
302
303#define EXPECT_LEN(expected_len) do {                               \
304    if (expected_len != len)                                        \
305        return -1;                                                  \
306} while (0)
307
308#define EXPECT_AT_LEAST(expected_len) do {                          \
309    if ((expected_len) > (uintptr_t) (p + len - q))                 \
310        return -1;                                                  \
311} while (0)
312
313    set_of_ids = 0;
314    while (p < end)
315    {
316        READ_UINT(param_id, 16, p, 2);
317        p += 2;
318        READ_UINT(len, 16, p, 2);
319        p += 2;
320        if (len > end - p)
321            return -1;
322        /* If we need to support parameter IDs 31 and up, we will need to
323         * change this code:
324         */
325        if (param_id < sizeof(set_of_ids) * 8)
326        {
327            /* Only check duplicates for IDs <= 31: all standard parameters
328             * fit in a bitmask 32 bits wide.
329             */
330            if (set_of_ids & (1 << param_id))
331                return -1;
332            set_of_ids |= 1 << param_id;
333        }
334        else
335            goto unknown;
336        if (NUMERIC_TRANS_PARAMS & (1u << param_id))
337        {
338            switch (len)
339            {
340            case 1:
341            case 2:
342            case 4:
343            case 8:
344                s = vint_read(p, p + len,
345                            &params->tp_numerics_u.a[tpi2idx[param_id]]);
346                if (s == len)
347                {
348                    if (params->tp_numerics_u.a[tpi2idx[param_id]]
349                                                        > max_vals[param_id])
350                    {
351                        LSQ_DEBUG("numeric value of parameter 0x%X is too "
352                            "large (%"PRIu64" vs maximum of %"PRIu64,
353                            param_id,
354                            params->tp_numerics_u.a[tpi2idx[param_id]],
355                            max_vals[param_id]);
356                        return -1;
357                    }
358                    p += s;
359                    break;
360                }
361                else
362                {
363                    LSQ_DEBUG("cannot read the value of numeric transport "
364                                        "param %u of length %u", param_id, len);
365                    return -1;
366                }
367            default:
368                LSQ_DEBUG("invalid length=%u for numeric transport parameter",
369                                                                        len);
370                return -1;
371            }
372        }
373        else
374        {
375            switch (param_id)
376            {
377            case TPI_DISABLE_ACTIVE_MIGRATION:
378                EXPECT_LEN(0);
379                params->tp_disable_active_migration = 1;
380                break;
381            case TPI_STATELESS_RESET_TOKEN:
382                /* Client MUST not include reset token,
383                 * see [draft-ietf-quic-transport-11], Section 6.4.1
384                 */
385                if (!is_server)
386                    return -1;
387                EXPECT_LEN(sizeof(params->tp_stateless_reset_token));
388                memcpy(params->tp_stateless_reset_token, p,
389                                    sizeof(params->tp_stateless_reset_token));
390                params->tp_flags |= TRAPA_RESET_TOKEN;
391                break;
392            case TPI_ORIGINAL_CONNECTION_ID:
393                /* Client MUST not original connecti ID,
394                 * see [draft-ietf-quic-transport-15], Section 6.6.1
395                 */
396                if (!is_server)
397                    return -1;
398                if (len > MAX_CID_LEN)
399                    return -1;
400                memcpy(params->tp_original_cid.idbuf, p, len);
401                params->tp_original_cid.len = len;
402                params->tp_flags |= TRAPA_ORIGINAL_CID;
403                break;
404            case TPI_PREFERRED_ADDRESS:
405                /* Client MUST not include preferred address,
406                 * see [draft-ietf-quic-transport-12], Section 6.4.1
407                 */
408                if (!is_server)
409                    return -1;
410                q = p;
411                EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv4_addr));
412                memcpy(params->tp_preferred_address.ipv4_addr, q,
413                            sizeof(params->tp_preferred_address.ipv4_addr));
414                q += sizeof(params->tp_preferred_address.ipv4_addr);
415                EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv4_port));
416                READ_UINT(params->tp_preferred_address.ipv4_port, 16, q, 2);
417                q += 2;
418                EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv6_addr));
419                memcpy(params->tp_preferred_address.ipv6_addr, q,
420                            sizeof(params->tp_preferred_address.ipv6_addr));
421                q += sizeof(params->tp_preferred_address.ipv6_addr);
422                EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv6_port));
423                READ_UINT(params->tp_preferred_address.ipv6_port, 16, q, 2);
424                q += 2;
425                EXPECT_AT_LEAST(1);
426                tlen = *q;
427                q += 1;
428                if (tlen < 4 || tlen > MAX_CID_LEN)
429                {
430                    LSQ_DEBUG("preferred server address contains invalid "
431                        "CID length of %"PRIu16" bytes", tlen);
432                    return -1;
433                }
434                EXPECT_AT_LEAST(tlen);
435                memcpy(params->tp_preferred_address.cid.idbuf, q, tlen);
436                params->tp_preferred_address.cid.len = tlen;
437                q += tlen;
438                EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.srst));
439                memcpy(params->tp_preferred_address.srst, q,
440                                sizeof(params->tp_preferred_address.srst));
441                q += sizeof(params->tp_preferred_address.srst);
442                if (q != p + len)
443                    return -1;
444                if (params->tp_preferred_address.ipv4_port
445                    && !lsquic_is_zero(params->tp_preferred_address.ipv4_addr,
446                                sizeof(params->tp_preferred_address.ipv4_addr)))
447                    params->tp_flags |= TRAPA_PREFADDR_IPv4;
448                if (params->tp_preferred_address.ipv6_port
449                    && !lsquic_is_zero(params->tp_preferred_address.ipv6_addr,
450                                sizeof(params->tp_preferred_address.ipv6_addr)))
451                    params->tp_flags |= TRAPA_PREFADDR_IPv6;
452                break;
453            }
454  unknown:
455            p += len;
456        }
457    }
458
459    if (p != end)
460        return -1;
461
462    return (int) (end - buf);
463#undef EXPECT_LEN
464}
465
466
467void
468lsquic_tp_to_str (const struct transport_params *params, char *buf, size_t sz)
469{
470    char *const end = buf + sz;
471    int nw;
472    char tok_str[sizeof(params->tp_stateless_reset_token) * 2 + 1];
473    char addr_str[INET6_ADDRSTRLEN];
474
475#define SEMICOLON "; "
476#define WRITE_ONE_PARAM(name, fmt) do {  \
477    nw = snprintf(buf, end - buf, #name ": " fmt SEMICOLON, params->tp_##name); \
478    buf += nw; \
479    if (buf >= end) \
480        return; \
481} while (0)
482
483    WRITE_ONE_PARAM(init_max_stream_data_bidi_local, "%"PRIu64);
484    WRITE_ONE_PARAM(init_max_stream_data_bidi_remote, "%"PRIu64);
485    WRITE_ONE_PARAM(init_max_stream_data_uni, "%"PRIu64);
486    WRITE_ONE_PARAM(init_max_data, "%"PRIu64);
487    WRITE_ONE_PARAM(idle_timeout, "%"PRIu64);
488    WRITE_ONE_PARAM(init_max_streams_bidi, "%"PRIu64);
489    WRITE_ONE_PARAM(init_max_streams_uni, "%"PRIu64);
490    WRITE_ONE_PARAM(max_packet_size, "%"PRIu64);
491    WRITE_ONE_PARAM(ack_delay_exponent, "%"PRIu64);
492    WRITE_ONE_PARAM(active_connection_id_limit, "%"PRIu64);
493    WRITE_ONE_PARAM(disable_active_migration, "%hhd");
494#undef SEMICOLON
495#define SEMICOLON ""
496    WRITE_ONE_PARAM(max_ack_delay, "%"PRIu64);
497    if (params->tp_flags & TRAPA_RESET_TOKEN)
498    {
499        lsquic_hexstr(params->tp_stateless_reset_token,
500            sizeof(params->tp_stateless_reset_token), tok_str, sizeof(tok_str));
501        nw = snprintf(buf, end - buf, "; stateless_reset_token: %s", tok_str);
502        buf += nw;
503        if (buf >= end)
504            return;
505    }
506    if (params->tp_flags & TRAPA_RESET_TOKEN)
507    {
508        char cidbuf_[MAX_CID_LEN * 2 + 1];
509        nw = snprintf(buf, end - buf, "; original DCID (ODCID): %"CID_FMT,
510                                        CID_BITS(&params->tp_original_cid));
511        buf += nw;
512        if (buf >= end)
513            return;
514    }
515    if (params->tp_flags & TRAPA_PREFADDR_IPv4)
516    {
517        if (inet_ntop(AF_INET, params->tp_preferred_address.ipv4_addr,
518                                                addr_str, sizeof(addr_str)))
519        {
520            nw = snprintf(buf, end - buf, "; IPv4 preferred address: %s:%u",
521                            addr_str, params->tp_preferred_address.ipv4_port);
522            buf += nw;
523            if (buf >= end)
524                return;
525        }
526    }
527    if (params->tp_flags & TRAPA_PREFADDR_IPv6)
528    {
529        if (inet_ntop(AF_INET6, params->tp_preferred_address.ipv6_addr,
530                                                addr_str, sizeof(addr_str)))
531        {
532            nw = snprintf(buf, end - buf, "; IPv6 preferred address: %s:%u",
533                            addr_str, params->tp_preferred_address.ipv6_port);
534            buf += nw;
535            if (buf >= end)
536                return;
537        }
538    }
539
540#undef SEMICOLON
541#undef WRITE_ONE_PARAM
542}
543