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