test_common.c revision fb73393f
1/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
2#if __GNUC__
3#define _GNU_SOURCE     /* For struct in6_pktinfo */
4#endif
5#include <assert.h>
6#include <errno.h>
7#include <string.h>
8#include <stdlib.h>
9#include <stdio.h>
10#include <sys/types.h>
11#if defined(__APPLE__)
12#   define __APPLE_USE_RFC_3542 1
13#endif
14#ifndef WIN32
15#include <netinet/in.h>
16#include <netinet/ip.h>
17#include <arpa/inet.h>
18#include <sys/socket.h>
19#include <unistd.h>
20#else
21#include <Windows.h>
22#include <WinSock2.h>
23#include <MSWSock.h>
24#include<io.h>
25#pragma warning(disable:4996)//posix name deprecated
26#define close closesocket
27#endif
28#include <sys/stat.h>
29#include <sys/queue.h>
30#include <fcntl.h>
31
32#include "test_config.h"
33#if HAVE_REGEX
34#include <regex.h>
35#endif
36
37#include <event2/event.h>
38
39#include "test_common.h"
40#include "lsquic.h"
41#include "prog.h"
42
43#include "../src/liblsquic/lsquic_logger.h"
44
45#define MAX(a, b) ((a) > (b) ? (a) : (b))
46#define MIN(a, b) ((a) < (b) ? (a) : (b))
47
48#ifndef LSQUIC_USE_POOLS
49#define LSQUIC_USE_POOLS 1
50#endif
51
52#ifndef WIN32
53#   define SOCKET_TYPE int
54#   define CLOSE_SOCKET close
55#   define CHAR_CAST
56#else
57#   define SOCKET_TYPE SOCKET
58#   define CLOSE_SOCKET closesocket
59#   define CHAR_CAST (char *)
60#endif
61
62#if __linux__
63#   define NDROPPED_SZ CMSG_SPACE(sizeof(uint32_t))  /* SO_RXQ_OVFL */
64#else
65#   define NDROPPED_SZ 0
66#endif
67
68#if __linux__ && defined(IP_RECVORIGDSTADDR)
69#   define DST_MSG_SZ sizeof(struct sockaddr_in)
70#elif WIN32
71#   define DST_MSG_SZ sizeof(struct sockaddr_in)
72#elif __linux__
73#   define DST_MSG_SZ sizeof(struct in_pktinfo)
74#else
75#   define DST_MSG_SZ sizeof(struct sockaddr_in)
76#endif
77
78#if ECN_SUPPORTED
79#define ECN_SZ CMSG_SPACE(sizeof(int))
80#else
81#define ECN_SZ 0
82#endif
83
84#define MAX_PACKET_SZ 0xffff
85
86#define CTL_SZ (CMSG_SPACE(MAX(DST_MSG_SZ, \
87                        sizeof(struct in6_pktinfo))) + NDROPPED_SZ + ECN_SZ)
88
89/* There are `n_alloc' elements in `vecs', `local_addresses', and
90 * `peer_addresses' arrays.  `ctlmsg_data' is n_alloc * CTL_SZ.  Each packets
91 * gets a single `vecs' element that points somewhere into `packet_data'.
92 *
93 * `n_alloc' is calculated at run-time based on the socket's receive buffer
94 * size.
95 */
96struct packets_in
97{
98    unsigned char           *packet_data;
99    unsigned char           *ctlmsg_data;
100#ifndef WIN32
101    struct iovec            *vecs;
102#else
103    WSABUF                  *vecs;
104#endif
105#if ECN_SUPPORTED
106    int                     *ecn;
107#endif
108    struct sockaddr_storage *local_addresses,
109                            *peer_addresses;
110    unsigned                 n_alloc;
111    unsigned                 data_sz;
112};
113
114
115#if WIN32
116LPFN_WSARECVMSG pfnWSARecvMsg;
117GUID recvGuid = WSAID_WSARECVMSG;
118LPFN_WSASENDMSG pfnWSASendMsg;
119GUID sendGuid = WSAID_WSASENDMSG;
120
121CRITICAL_SECTION initLock;
122LONG initialized = 0;
123
124static void getExtensionPtrs()
125{
126    if (InterlockedCompareExchange(&initialized, 1, 0) == 0)
127    {
128        InitializeCriticalSection(&initLock);
129    }
130    EnterCriticalSection(&initLock);
131    if(pfnWSARecvMsg == NULL|| pfnWSASendMsg == NULL)
132    {
133        SOCKET sock= socket(PF_INET, SOCK_DGRAM, 0);
134        DWORD dwBytes;
135        int rc = 0;
136        if (pfnWSARecvMsg == NULL)
137        {
138            rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &recvGuid,
139                    sizeof(recvGuid), &pfnWSARecvMsg, sizeof(pfnWSARecvMsg),
140                    &dwBytes, NULL, NULL);
141        }
142        if (rc != SOCKET_ERROR)
143        {
144            if (pfnWSASendMsg == NULL)
145            {
146                rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
147                        &sendGuid, sizeof(sendGuid), &pfnWSASendMsg,
148                        sizeof(pfnWSASendMsg), &dwBytes, NULL, NULL);
149            }
150        }
151        if (rc == SOCKET_ERROR)
152        {
153            LSQ_ERROR("Can't get extension function pointers: %d",
154                                                        WSAGetLastError());
155        }
156        closesocket(sock);
157    }
158    LeaveCriticalSection(&initLock);
159}
160
161
162#endif
163
164
165static struct packets_in *
166allocate_packets_in (SOCKET_TYPE fd)
167{
168    struct packets_in *packs_in;
169    unsigned n_alloc;
170    socklen_t opt_len;
171    int recvsz;
172
173    opt_len = sizeof(recvsz);
174    if (0 != getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&recvsz, &opt_len))
175    {
176        LSQ_ERROR("getsockopt failed: %s", strerror(errno));
177        return NULL;
178    }
179
180    n_alloc = (unsigned) recvsz / MAX_PACKET_SZ * 2;
181    LSQ_INFO("socket buffer size: %d bytes; max # packets is set to %u",
182        recvsz, n_alloc);
183
184    packs_in = malloc(sizeof(*packs_in));
185    packs_in->data_sz = recvsz;
186    packs_in->n_alloc = n_alloc;
187    packs_in->packet_data = malloc(recvsz);
188    packs_in->ctlmsg_data = malloc(n_alloc * CTL_SZ);
189    packs_in->vecs = malloc(n_alloc * sizeof(packs_in->vecs[0]));
190    packs_in->local_addresses = malloc(n_alloc * sizeof(packs_in->local_addresses[0]));
191    packs_in->peer_addresses = malloc(n_alloc * sizeof(packs_in->peer_addresses[0]));
192#if ECN_SUPPORTED
193    packs_in->ecn = malloc(n_alloc * sizeof(packs_in->ecn[0]));
194#endif
195
196    return packs_in;
197}
198
199
200static void
201free_packets_in (struct packets_in *packs_in)
202{
203#if ECN_SUPPORTED
204    free(packs_in->ecn);
205#endif
206    free(packs_in->peer_addresses);
207    free(packs_in->local_addresses);
208    free(packs_in->ctlmsg_data);
209    free(packs_in->vecs);
210    free(packs_in->packet_data);
211    free(packs_in);
212}
213
214
215void
216sport_destroy (struct service_port *sport)
217{
218    if (sport->ev)
219    {
220        event_del(sport->ev);
221        event_free(sport->ev);
222    }
223    if (sport->fd >= 0)
224        (void) CLOSE_SOCKET(sport->fd);
225    if (sport->packs_in)
226        free_packets_in(sport->packs_in);
227    free(sport->sp_token_buf);
228    free(sport);
229}
230
231
232struct service_port *
233sport_new (const char *optarg, struct prog *prog)
234{
235    struct service_port *const sport = calloc(1, sizeof(*sport));
236#if HAVE_REGEX
237    regex_t re;
238    regmatch_t matches[5];
239    int re_code;
240    const char *port_str;
241    char errbuf[80];
242#else
243    char *port_str;
244#endif
245    int port, e;
246    const char *host;
247    struct addrinfo hints, *res = NULL;
248#if __linux__
249    sport->n_dropped = 0;
250    sport->drop_init = 0;
251#endif
252    sport->ev = NULL;
253    sport->packs_in = NULL;
254    sport->fd = -1;
255    char *const addr = strdup(optarg);
256#if __linux__
257    char *if_name;
258    if_name = strrchr(addr, ',');
259    if (if_name)
260    {
261        strncpy(sport->if_name, if_name + 1, sizeof(sport->if_name) - 1);
262        sport->if_name[ sizeof(sport->if_name) - 1 ] = '\0';
263        *if_name = '\0';
264    }
265    else
266        sport->if_name[0] = '\0';
267#endif
268#if HAVE_REGEX
269    re_code = regcomp(&re, "^(.*):([0-9][0-9]*)$"
270                          "|^([0-9][0-9]*)$"
271                          "|^(..*)$"
272                                                    , REG_EXTENDED);
273    if (re_code != 0)
274    {
275        regerror(re_code, &re, errbuf, sizeof(errbuf));
276        LSQ_ERROR("cannot compile regex: %s", errbuf);
277        goto err;
278    }
279    if (0 != regexec(&re, addr, sizeof(matches) / sizeof(matches[0]),
280                                                            matches, 0))
281    {
282        LSQ_ERROR("Invalid argument `%s'", addr);
283        goto err;
284    }
285    if (matches[1].rm_so >= 0)
286    {
287        addr[ matches[1].rm_so + matches[1].rm_eo ] = '\0';
288        host = addr;
289        port_str = &addr[ matches[2].rm_so ];
290        port = atoi(port_str);
291    }
292    else if (matches[3].rm_so >= 0)
293    {
294        if (!prog->prog_hostname)
295        {
296            LSQ_ERROR("hostname is not specified");
297            goto err;
298        }
299        host = prog->prog_hostname;
300        port_str = &addr[ matches[3].rm_so ];
301        port = atoi(port_str);
302    }
303    else
304    {
305        assert(matches[4].rm_so >= 0);
306        host = addr;
307        port_str = "443";
308        port = 443;
309    }
310#else
311    host = addr;
312    port_str = strrchr(addr, ':');
313    if (port_str)
314    {
315        *port_str++ = '\0';
316        port = atoi(port_str);
317    }
318    else
319    {
320        port_str = "443";
321        port = 443;
322    }
323#endif
324    assert(host);
325    LSQ_DEBUG("host: %s; port: %d", host, port);
326    if (strlen(host) > sizeof(sport->host) - 1)
327    {
328        LSQ_ERROR("argument `%s' too long", host);
329        goto err;
330    }
331    strcpy(sport->host, host);
332
333    struct sockaddr_in  *const sa4 = (void *) &sport->sas;
334    struct sockaddr_in6 *const sa6 = (void *) &sport->sas;
335    if        (inet_pton(AF_INET, host, &sa4->sin_addr)) {
336        sa4->sin_family = AF_INET;
337        sa4->sin_port   = htons(port);
338    } else if (memset(sa6, 0, sizeof(*sa6)),
339                    inet_pton(AF_INET6, host, &sa6->sin6_addr)) {
340        sa6->sin6_family = AF_INET6;
341        sa6->sin6_port   = htons(port);
342    } else
343    {
344        memset(&hints, 0, sizeof(hints));
345        hints.ai_flags = AI_NUMERICSERV;
346        if (prog->prog_ipver == 4)
347            hints.ai_family = AF_INET;
348        else if (prog->prog_ipver == 6)
349            hints.ai_family = AF_INET6;
350        e = getaddrinfo(host, port_str, &hints, &res);
351        if (e != 0)
352        {
353            LSQ_ERROR("could not resolve %s:%s: %s", host, port_str,
354                                                        gai_strerror(e));
355            goto err;
356        }
357        if (res->ai_addrlen > sizeof(sport->sas))
358        {
359            LSQ_ERROR("resolved socket length is too long");
360            goto err;
361        }
362        memcpy(&sport->sas, res->ai_addr, res->ai_addrlen);
363        if (!prog->prog_hostname)
364            prog->prog_hostname = sport->host;
365    }
366
367#if HAVE_REGEX
368    if (0 == re_code)
369        regfree(&re);
370#endif
371    if (res)
372        freeaddrinfo(res);
373    free(addr);
374    sport->sp_prog = prog;
375    return sport;
376
377  err:
378#if HAVE_REGEX
379    if (0 == re_code)
380        regfree(&re);
381#endif
382    if (res)
383        freeaddrinfo(res);
384    free(sport);
385    free(addr);
386    return NULL;
387}
388
389
390/* Replace IP address part of `sa' with that provided in ancillary messages
391 * in `msg'.
392 */
393static void
394proc_ancillary (
395#ifndef WIN32
396                struct msghdr
397#else
398                WSAMSG
399#endif
400                              *msg, struct sockaddr_storage *storage
401#if __linux__
402                , uint32_t *n_dropped
403#endif
404#if ECN_SUPPORTED
405                , int *ecn
406#endif
407                )
408{
409    const struct in6_pktinfo *in6_pkt;
410    struct cmsghdr *cmsg;
411
412    for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg))
413    {
414        if (cmsg->cmsg_level == IPPROTO_IP &&
415            cmsg->cmsg_type  ==
416#if __linux__ && defined(IP_RECVORIGDSTADDR)
417                                IP_ORIGDSTADDR
418#elif __linux__ || WIN32 || __APPLE__
419                                IP_PKTINFO
420#else
421                                IP_RECVDSTADDR
422#endif
423                                              )
424        {
425#if __linux__ && defined(IP_RECVORIGDSTADDR)
426            memcpy(storage, CMSG_DATA(cmsg), sizeof(struct sockaddr_in));
427#elif WIN32
428            const struct in_pktinfo *in_pkt;
429            in_pkt = (void *) WSA_CMSG_DATA(cmsg);
430            ((struct sockaddr_in *) storage)->sin_addr = in_pkt->ipi_addr;
431#elif __linux__ || __APPLE__
432            const struct in_pktinfo *in_pkt;
433            in_pkt = (void *) CMSG_DATA(cmsg);
434            ((struct sockaddr_in *) storage)->sin_addr = in_pkt->ipi_addr;
435#else
436            memcpy(&((struct sockaddr_in *) storage)->sin_addr,
437                            CMSG_DATA(cmsg), sizeof(struct in_addr));
438#endif
439        }
440        else if (cmsg->cmsg_level == IPPROTO_IPV6 &&
441                 cmsg->cmsg_type  == IPV6_PKTINFO)
442        {
443#ifndef WIN32
444            in6_pkt = (void *) CMSG_DATA(cmsg);
445#else
446            in6_pkt = (void *) WSA_CMSG_DATA(cmsg);
447#endif
448            ((struct sockaddr_in6 *) storage)->sin6_addr =
449                                                    in6_pkt->ipi6_addr;
450        }
451#if __linux__
452        else if (cmsg->cmsg_level == SOL_SOCKET &&
453                 cmsg->cmsg_type  == SO_RXQ_OVFL)
454            memcpy(n_dropped, CMSG_DATA(cmsg), sizeof(*n_dropped));
455#endif
456#if ECN_SUPPORTED
457        else if ((cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS)
458                 || (cmsg->cmsg_level == IPPROTO_IPV6
459                                            && cmsg->cmsg_type == IPV6_TCLASS))
460        {
461            memcpy(ecn, CMSG_DATA(cmsg), sizeof(*ecn));
462            *ecn &= IPTOS_ECN_MASK;
463        }
464#ifdef __FreeBSD__
465        else if (cmsg->cmsg_level == IPPROTO_IP
466                                            && cmsg->cmsg_type == IP_RECVTOS)
467        {
468            unsigned char tos;
469            memcpy(&tos, CMSG_DATA(cmsg), sizeof(tos));
470            *ecn = tos & IPTOS_ECN_MASK;
471        }
472#endif
473#endif
474    }
475}
476
477
478struct read_iter
479{
480    struct service_port     *ri_sport;
481    unsigned                 ri_idx;    /* Current element */
482    unsigned                 ri_off;    /* Offset into packet_data */
483};
484
485
486enum rop { ROP_OK, ROP_NOROOM, ROP_ERROR, };
487
488static enum rop
489read_one_packet (struct read_iter *iter)
490{
491    unsigned char *ctl_buf;
492    struct packets_in *packs_in;
493#if __linux__
494    uint32_t n_dropped;
495#endif
496#ifndef WIN32
497    ssize_t nread;
498#else
499    DWORD nread;
500    int socket_ret;
501#endif
502    struct sockaddr_storage *local_addr;
503    struct service_port *sport;
504
505    sport = iter->ri_sport;
506    packs_in = sport->packs_in;
507
508    if (iter->ri_idx >= packs_in->n_alloc ||
509        iter->ri_off + MAX_PACKET_SZ > packs_in->data_sz)
510    {
511        LSQ_DEBUG("out of room in packets_in");
512        return ROP_NOROOM;
513    }
514
515#ifndef WIN32
516    packs_in->vecs[iter->ri_idx].iov_base = packs_in->packet_data + iter->ri_off;
517    packs_in->vecs[iter->ri_idx].iov_len  = MAX_PACKET_SZ;
518#else
519    packs_in->vecs[iter->ri_idx].buf = (char*)packs_in->packet_data + iter->ri_off;
520    packs_in->vecs[iter->ri_idx].len = MAX_PACKET_SZ;
521#endif
522
523  top:
524    ctl_buf = packs_in->ctlmsg_data + iter->ri_idx * CTL_SZ;
525
526#ifndef WIN32
527    struct msghdr msg = {
528        .msg_name       = &packs_in->peer_addresses[iter->ri_idx],
529        .msg_namelen    = sizeof(packs_in->peer_addresses[iter->ri_idx]),
530        .msg_iov        = &packs_in->vecs[iter->ri_idx],
531        .msg_iovlen     = 1,
532        .msg_control    = ctl_buf,
533        .msg_controllen = CTL_SZ,
534    };
535    nread = recvmsg(sport->fd, &msg, 0);
536    if (-1 == nread) {
537        if (!(EAGAIN == errno || EWOULDBLOCK == errno))
538            LSQ_ERROR("recvmsg: %s", strerror(errno));
539        return ROP_ERROR;
540    }
541    if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC))
542    {
543        if (msg.msg_flags & MSG_TRUNC)
544            LSQ_INFO("packet truncated - drop it");
545        if (msg.msg_flags & MSG_CTRUNC)
546            LSQ_WARN("packet's auxilicary data truncated - drop it");
547        goto top;
548    }
549#else
550    WSAMSG msg = {
551        .name       = (LPSOCKADDR)&packs_in->peer_addresses[iter->ri_idx],
552        .namelen    = sizeof(packs_in->peer_addresses[iter->ri_idx]),
553        .lpBuffers        = &packs_in->vecs[iter->ri_idx],
554        .dwBufferCount     = 1,
555        .Control = {CTL_SZ,(char*)ctl_buf}
556    };
557    socket_ret = pfnWSARecvMsg(sport->fd, &msg, &nread, NULL, NULL);
558    if (SOCKET_ERROR == socket_ret) {
559        if (WSAEWOULDBLOCK != WSAGetLastError())
560            LSQ_ERROR("recvmsg: %d", WSAGetLastError());
561	return ROP_ERROR;
562    }
563#endif
564
565    local_addr = &packs_in->local_addresses[iter->ri_idx];
566    memcpy(local_addr, &sport->sp_local_addr, sizeof(*local_addr));
567#if __linux__
568    n_dropped = 0;
569#endif
570#if ECN_SUPPORTED
571    packs_in->ecn[iter->ri_idx] = 0;
572#endif
573    proc_ancillary(&msg, local_addr
574#if __linux__
575        , &n_dropped
576#endif
577#if ECN_SUPPORTED
578        , &packs_in->ecn[iter->ri_idx]
579#endif
580    );
581#if LSQUIC_ECN_BLACK_HOLE && ECN_SUPPORTED
582    {
583        const char *s;
584        s = getenv("LSQUIC_ECN_BLACK_HOLE");
585        if (s && atoi(s) && packs_in->ecn[iter->ri_idx])
586        {
587            LSQ_NOTICE("ECN blackhole: drop packet");
588            return ROP_OK;
589        }
590    }
591#endif
592#if __linux__
593    if (sport->drop_init)
594    {
595        if (sport->n_dropped < n_dropped)
596            LSQ_INFO("dropped %u packets", n_dropped - sport->n_dropped);
597    }
598    else
599        sport->drop_init = 1;
600    sport->n_dropped = n_dropped;
601#endif
602
603#ifndef WIN32
604    packs_in->vecs[iter->ri_idx].iov_len = nread;
605#else
606    packs_in->vecs[iter->ri_idx].len = nread;
607#endif
608    iter->ri_off += nread;
609    iter->ri_idx += 1;
610
611    return ROP_OK;
612}
613
614
615#if HAVE_RECVMMSG
616static enum rop
617read_using_recvmmsg (struct read_iter *iter)
618{
619#if __linux__
620    uint32_t n_dropped;
621#endif
622    int s;
623    unsigned n;
624    struct sockaddr_storage *local_addr;
625    struct service_port *const sport = iter->ri_sport;
626    struct packets_in *const packs_in = sport->packs_in;
627    /* XXX TODO We allocate this array on the stack and initialize the
628     * headers each time the function is invoked.  This is suboptimal.
629     * What we should really be doing is allocate mmsghdrs as part of
630     * packs_in and initialize it there.  While we are at it, we should
631     * make packs_in shared between all service ports.
632     */
633    struct mmsghdr mmsghdrs[ packs_in->n_alloc  ];
634
635    /* Sanity check: we assume that the iterator is reset */
636    assert(iter->ri_off == 0 && iter->ri_idx == 0);
637
638    /* Initialize mmsghdrs */
639    for (n = 0; n < sizeof(mmsghdrs) / sizeof(mmsghdrs[0]); ++n)
640    {
641        packs_in->vecs[n].iov_base = packs_in->packet_data + MAX_PACKET_SZ * n;
642        packs_in->vecs[n].iov_len  = MAX_PACKET_SZ;
643        mmsghdrs[n].msg_hdr = (struct msghdr) {
644            .msg_name       = &packs_in->peer_addresses[n],
645            .msg_namelen    = sizeof(packs_in->peer_addresses[n]),
646            .msg_iov        = &packs_in->vecs[n],
647            .msg_iovlen     = 1,
648            .msg_control    = packs_in->ctlmsg_data + CTL_SZ * n,
649            .msg_controllen = CTL_SZ,
650        };
651    }
652
653    /* Read packets */
654    s = recvmmsg(sport->fd, mmsghdrs, n, 0, NULL);
655    if (s < 0)
656    {
657        if (!(EAGAIN == errno || EWOULDBLOCK == errno))
658            LSQ_ERROR("recvmmsg: %s", strerror(errno));
659        return ROP_ERROR;
660    }
661
662    /* Process ancillary data and update vecs */
663    for (n = 0; n < (unsigned) s; ++n)
664    {
665        local_addr = &packs_in->local_addresses[n];
666        memcpy(local_addr, &sport->sp_local_addr, sizeof(*local_addr));
667#if __linux__
668        n_dropped = 0;
669#endif
670#if ECN_SUPPORTED
671        packs_in->ecn[n] = 0;
672#endif
673        proc_ancillary(&mmsghdrs[n].msg_hdr, local_addr
674#if __linux__
675            , &n_dropped
676#endif
677#if ECN_SUPPORTED
678            , &packs_in->ecn[n]
679#endif
680        );
681#if __linux__
682        if (sport->drop_init)
683        {
684            if (sport->n_dropped < n_dropped)
685                LSQ_INFO("dropped %u packets", n_dropped - sport->n_dropped);
686        }
687        else
688            sport->drop_init = 1;
689        sport->n_dropped = n_dropped;
690#endif
691        packs_in->vecs[n].iov_len = mmsghdrs[n].msg_len;
692    }
693
694    iter->ri_idx = n;
695
696    return n == sizeof(mmsghdrs) / sizeof(mmsghdrs[0]) ? ROP_NOROOM : ROP_OK;
697}
698
699
700#endif
701
702
703#if __GNUC__
704#   define UNLIKELY(cond) __builtin_expect(cond, 0)
705#else
706#   define UNLIKELY(cond) cond
707#endif
708
709
710static void
711read_handler (evutil_socket_t fd, short flags, void *ctx)
712{
713    struct service_port *sport = ctx;
714    lsquic_engine_t *const engine = sport->engine;
715    struct packets_in *packs_in = sport->packs_in;
716    struct read_iter iter;
717    unsigned n, n_batches;
718    /* Save the value in case program is stopped packs_in is freed: */
719    const unsigned n_alloc = packs_in->n_alloc;
720    enum rop rop;
721
722    n_batches = 0;
723    iter.ri_sport = sport;
724
725    sport->sp_prog->prog_read_count += 1;
726    do
727    {
728        iter.ri_off = 0;
729        iter.ri_idx = 0;
730
731#if HAVE_RECVMMSG
732        if (sport->sp_prog->prog_use_recvmmsg)
733            rop = read_using_recvmmsg(&iter);
734        else
735#endif
736            do
737                rop = read_one_packet(&iter);
738            while (ROP_OK == rop);
739
740        if (UNLIKELY(ROP_ERROR == rop && (sport->sp_flags & SPORT_CONNECT)
741                                                    && errno == ECONNREFUSED))
742        {
743            LSQ_ERROR("connection refused: exit program");
744            prog_cleanup(sport->sp_prog);
745            exit(1);
746        }
747
748        n_batches += iter.ri_idx > 0;
749
750        for (n = 0; n < iter.ri_idx; ++n)
751            if (0 > lsquic_engine_packet_in(engine,
752#ifndef WIN32
753                        packs_in->vecs[n].iov_base,
754                        packs_in->vecs[n].iov_len,
755#else
756                        (const unsigned char *) packs_in->vecs[n].buf,
757                        packs_in->vecs[n].len,
758#endif
759                        (struct sockaddr *) &packs_in->local_addresses[n],
760                        (struct sockaddr *) &packs_in->peer_addresses[n],
761                        sport,
762#if ECN_SUPPORTED
763                        packs_in->ecn[n]
764#else
765                        0
766#endif
767                        ))
768                break;
769
770        if (n > 0)
771            prog_process_conns(sport->sp_prog);
772    }
773    while (ROP_NOROOM == rop && !prog_is_stopped());
774
775    if (n_batches)
776        n += n_alloc * (n_batches - 1);
777
778    LSQ_DEBUG("read %u packet%.*s in %u batch%s", n, n != 1, "s", n_batches, n_batches != 1 ? "es" : "");
779}
780
781
782static int
783add_to_event_loop (struct service_port *sport, struct event_base *eb)
784{
785    sport->ev = event_new(eb, sport->fd, EV_READ|EV_PERSIST, read_handler,
786                                                                    sport);
787    if (sport->ev)
788    {
789        event_add(sport->ev, NULL);
790        return 0;
791    }
792    else
793        return -1;
794}
795
796
797int
798sport_init_server (struct service_port *sport, struct lsquic_engine *engine,
799                   struct event_base *eb)
800{
801    const struct sockaddr *sa_local = (struct sockaddr *) &sport->sas;
802    int sockfd, saved_errno, flags, s, on;
803    socklen_t socklen;
804    char addr_str[0x20];
805
806    switch (sa_local->sa_family)
807    {
808    case AF_INET:
809        socklen = sizeof(struct sockaddr_in);
810        break;
811    case AF_INET6:
812        socklen = sizeof(struct sockaddr_in6);
813        break;
814    default:
815        errno = EINVAL;
816        return -1;
817    }
818
819    sockfd = socket(sa_local->sa_family, SOCK_DGRAM, 0);
820    if (-1 == sockfd)
821        return -1;
822
823    if (0 != bind(sockfd, sa_local, socklen)) {
824        saved_errno = errno;
825        LSQ_WARN("bind failed: %s", strerror(errno));
826        close(sockfd);
827        errno = saved_errno;
828        return -1;
829    }
830
831    /* Make socket non-blocking */
832    flags = fcntl(sockfd, F_GETFL);
833    if (-1 == flags) {
834        saved_errno = errno;
835        close(sockfd);
836        errno = saved_errno;
837        return -1;
838    }
839    flags |= O_NONBLOCK;
840    if (0 != fcntl(sockfd, F_SETFL, flags)) {
841        saved_errno = errno;
842        close(sockfd);
843        errno = saved_errno;
844        return -1;
845    }
846
847    on = 1;
848    if (AF_INET == sa_local->sa_family)
849        s = setsockopt(sockfd, IPPROTO_IP,
850#if __linux__ && defined(IP_RECVORIGDSTADDR)
851                                           IP_RECVORIGDSTADDR,
852#elif __linux__ || __APPLE__
853                                           IP_PKTINFO,
854#else
855                                           IP_RECVDSTADDR,
856#endif
857                                                               &on, sizeof(on));
858    else
859        s = setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
860    if (0 != s)
861    {
862        saved_errno = errno;
863        close(sockfd);
864        errno = saved_errno;
865        return -1;
866    }
867
868#if (__linux__ && !defined(IP_RECVORIGDSTADDR)) || __APPLE__
869    /* Need to set IP_PKTINFO for sending */
870    if (AF_INET == sa_local->sa_family)
871    {
872        on = 1;
873        s = setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
874        if (0 != s)
875        {
876            saved_errno = errno;
877            close(sockfd);
878            errno = saved_errno;
879            return -1;
880        }
881    }
882#elif IP_RECVDSTADDR != IP_SENDSRCADDR
883    /* On FreeBSD, IP_RECVDSTADDR is the same as IP_SENDSRCADDR, but I do not
884     * know about other BSD systems.
885     */
886    if (AF_INET == sa_local->sa_family)
887    {
888        on = 1;
889        s = setsockopt(sockfd, IPPROTO_IP, IP_SENDSRCADDR, &on, sizeof(on));
890        if (0 != s)
891        {
892            saved_errno = errno;
893            close(sockfd);
894            errno = saved_errno;
895            return -1;
896        }
897    }
898#endif
899
900#if __linux__ && defined(SO_RXQ_OVFL)
901    on = 1;
902    s = setsockopt(sockfd, SOL_SOCKET, SO_RXQ_OVFL, &on, sizeof(on));
903    if (0 != s)
904    {
905        saved_errno = errno;
906        close(sockfd);
907        errno = saved_errno;
908        return -1;
909    }
910#endif
911
912#if __linux__
913    if (sport->if_name[0] &&
914        0 != setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, sport->if_name,
915                                                               IFNAMSIZ))
916    {
917        saved_errno = errno;
918        close(sockfd);
919        errno = saved_errno;
920        return -1;
921    }
922#endif
923
924#if LSQUIC_DONTFRAG_SUPPORTED
925    if (!(sport->sp_flags & SPORT_FRAGMENT_OK))
926    {
927        if (AF_INET == sa_local->sa_family)
928        {
929#if __linux__
930            on = IP_PMTUDISC_DO;
931            s = setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &on,
932                                                                sizeof(on));
933#else
934            on = 1;
935            s = setsockopt(sockfd, IPPROTO_IP, IP_DONTFRAG, &on, sizeof(on));
936#endif
937            if (0 != s)
938            {
939                saved_errno = errno;
940                close(sockfd);
941                errno = saved_errno;
942                return -1;
943            }
944        }
945    }
946#endif
947
948#if ECN_SUPPORTED
949    on = 1;
950    if (AF_INET == sa_local->sa_family)
951        s = setsockopt(sockfd, IPPROTO_IP, IP_RECVTOS, &on, sizeof(on));
952    else
953        s = setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVTCLASS, &on, sizeof(on));
954    if (0 != s)
955    {
956        saved_errno = errno;
957        close(sockfd);
958        errno = saved_errno;
959        return -1;
960    }
961#endif
962
963    if (sport->sp_flags & SPORT_SET_SNDBUF)
964    {
965        s = setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sport->sp_sndbuf,
966                                                    sizeof(sport->sp_sndbuf));
967        if (0 != s)
968        {
969            saved_errno = errno;
970            close(sockfd);
971            errno = saved_errno;
972            return -1;
973        }
974    }
975
976    if (sport->sp_flags & SPORT_SET_RCVBUF)
977    {
978        s = setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &sport->sp_rcvbuf,
979                                                    sizeof(sport->sp_rcvbuf));
980        if (0 != s)
981        {
982            saved_errno = errno;
983            close(sockfd);
984            errno = saved_errno;
985            return -1;
986        }
987    }
988
989    if (0 != getsockname(sockfd, (struct sockaddr *) sa_local, &socklen))
990    {
991        saved_errno = errno;
992        close(sockfd);
993        errno = saved_errno;
994        return -1;
995    }
996
997    sport->packs_in = allocate_packets_in(sockfd);
998    if (!sport->packs_in)
999    {
1000        saved_errno = errno;
1001        close(sockfd);
1002        errno = saved_errno;
1003        return -1;
1004    }
1005
1006    memcpy((void *) &sport->sp_local_addr, sa_local,
1007        sa_local->sa_family == AF_INET ?
1008        sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
1009    switch (sa_local->sa_family) {
1010    case AF_INET:
1011        LSQ_DEBUG("local address: %s:%d",
1012            inet_ntop(AF_INET, &((struct sockaddr_in *) sa_local)->sin_addr,
1013            addr_str, sizeof(addr_str)),
1014            ntohs(((struct sockaddr_in *) sa_local)->sin_port));
1015        break;
1016    }
1017
1018    sport->engine = engine;
1019    sport->fd = sockfd;
1020    sport->sp_flags |= SPORT_SERVER;
1021
1022    return add_to_event_loop(sport, eb);
1023}
1024
1025
1026int
1027sport_init_client (struct service_port *sport, struct lsquic_engine *engine,
1028                   struct event_base *eb)
1029{
1030    const struct sockaddr *sa_peer = (struct sockaddr *) &sport->sas;
1031    int saved_errno, s;
1032#ifndef WIN32
1033    int flags;
1034#endif
1035    SOCKET_TYPE sockfd;
1036    socklen_t socklen, peer_socklen;
1037    union {
1038        struct sockaddr_in  sin;
1039        struct sockaddr_in6 sin6;
1040    } u;
1041    struct sockaddr *sa_local = (struct sockaddr *) &u;
1042    char addr_str[0x20];
1043
1044    switch (sa_peer->sa_family)
1045    {
1046    case AF_INET:
1047        socklen = sizeof(struct sockaddr_in);
1048        u.sin.sin_family      = AF_INET;
1049        u.sin.sin_addr.s_addr = INADDR_ANY;
1050        u.sin.sin_port        = 0;
1051        break;
1052    case AF_INET6:
1053        socklen = sizeof(struct sockaddr_in6);
1054        memset(&u.sin6, 0, sizeof(u.sin6));
1055        u.sin6.sin6_family = AF_INET6;
1056        break;
1057    default:
1058        errno = EINVAL;
1059        return -1;
1060    }
1061
1062#if WIN32
1063    getExtensionPtrs();
1064#endif
1065    sockfd = socket(sa_peer->sa_family, SOCK_DGRAM, 0);
1066    if (-1 == sockfd)
1067        return -1;
1068
1069    if (0 != bind(sockfd, sa_local, socklen)) {
1070        saved_errno = errno;
1071        CLOSE_SOCKET(sockfd);
1072        errno = saved_errno;
1073        return -1;
1074    }
1075
1076    if (sport->sp_flags & SPORT_CONNECT)
1077    {
1078        peer_socklen = AF_INET == sa_peer->sa_family
1079                    ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
1080        if (0 != connect(sockfd, sa_peer, peer_socklen))
1081        {
1082            saved_errno = errno;
1083            CLOSE_SOCKET(sockfd);
1084            errno = saved_errno;
1085            return -1;
1086        }
1087    }
1088
1089    /* Make socket non-blocking */
1090#ifndef WIN32
1091    flags = fcntl(sockfd, F_GETFL);
1092    if (-1 == flags) {
1093        saved_errno = errno;
1094        CLOSE_SOCKET(sockfd);
1095        errno = saved_errno;
1096        return -1;
1097    }
1098    flags |= O_NONBLOCK;
1099    if (0 != fcntl(sockfd, F_SETFL, flags)) {
1100        saved_errno = errno;
1101        CLOSE_SOCKET(sockfd);
1102        errno = saved_errno;
1103        return -1;
1104    }
1105#else
1106    {
1107        u_long on = 1;
1108        ioctlsocket(sockfd, FIONBIO, &on);
1109    }
1110#endif
1111
1112#if LSQUIC_DONTFRAG_SUPPORTED
1113    if (!(sport->sp_flags & SPORT_FRAGMENT_OK))
1114    {
1115        if (AF_INET == sa_local->sa_family)
1116        {
1117        int on;
1118#if __linux__
1119            on = IP_PMTUDISC_DO;
1120            s = setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &on,
1121                                                                sizeof(on));
1122#elif WIN32
1123            on = 1;
1124            s = setsockopt(sockfd, IPPROTO_IP, IP_DONTFRAGMENT, (char*)&on, sizeof(on));
1125#else
1126            on = 1;
1127            s = setsockopt(sockfd, IPPROTO_IP, IP_DONTFRAG, &on, sizeof(on));
1128#endif
1129            if (0 != s)
1130            {
1131                saved_errno = errno;
1132                CLOSE_SOCKET(sockfd);
1133                errno = saved_errno;
1134                return -1;
1135            }
1136        }
1137    }
1138#endif
1139
1140#if ECN_SUPPORTED
1141    {
1142        int on = 1;
1143        if (AF_INET == sa_local->sa_family)
1144            s = setsockopt(sockfd, IPPROTO_IP, IP_RECVTOS, &on, sizeof(on));
1145        else
1146            s = setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVTCLASS, &on,
1147                                                                sizeof(on));
1148        if (0 != s)
1149        {
1150            saved_errno = errno;
1151            close(sockfd);
1152            errno = saved_errno;
1153            return -1;
1154        }
1155    }
1156#endif
1157
1158    if (sport->sp_flags & SPORT_SET_SNDBUF)
1159    {
1160        s = setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF,
1161                       CHAR_CAST &sport->sp_sndbuf, sizeof(sport->sp_sndbuf));
1162        if (0 != s)
1163        {
1164            saved_errno = errno;
1165            CLOSE_SOCKET(sockfd);
1166            errno = saved_errno;
1167            return -1;
1168        }
1169    }
1170
1171    if (sport->sp_flags & SPORT_SET_RCVBUF)
1172    {
1173        s = setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
1174                       CHAR_CAST &sport->sp_rcvbuf, sizeof(sport->sp_rcvbuf));
1175        if (0 != s)
1176        {
1177            saved_errno = errno;
1178            CLOSE_SOCKET(sockfd);
1179            errno = saved_errno;
1180            return -1;
1181        }
1182    }
1183
1184    if (0 != getsockname(sockfd, sa_local, &socklen))
1185    {
1186        saved_errno = errno;
1187        CLOSE_SOCKET(sockfd);
1188        errno = saved_errno;
1189        return -1;
1190    }
1191
1192    sport->packs_in = allocate_packets_in(sockfd);
1193    if (!sport->packs_in)
1194    {
1195        saved_errno = errno;
1196        CLOSE_SOCKET(sockfd);
1197        errno = saved_errno;
1198        return -1;
1199    }
1200
1201    memcpy((void *) &sport->sp_local_addr, sa_local,
1202        sa_local->sa_family == AF_INET ?
1203        sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
1204    switch (sa_local->sa_family) {
1205    case AF_INET:
1206        LSQ_DEBUG("local address: %s:%d",
1207            inet_ntop(AF_INET, &u.sin.sin_addr, addr_str, sizeof(addr_str)),
1208            ntohs(u.sin.sin_port));
1209        break;
1210    }
1211
1212    sport->engine = engine;
1213    sport->fd = sockfd;
1214
1215    return add_to_event_loop(sport, eb);
1216}
1217
1218
1219/* Sometimes it is useful to impose an artificial limit for testing */
1220static unsigned
1221packet_out_limit (void)
1222{
1223    const char *env = getenv("LSQUIC_PACKET_OUT_LIMIT");
1224    if (env)
1225        return atoi(env);
1226    else
1227        return 0;
1228}
1229
1230
1231enum ctl_what
1232{
1233    CW_SENDADDR     = 1 << 0,
1234#if ECN_SUPPORTED
1235    CW_ECN          = 1 << 1,
1236#endif
1237};
1238
1239static void
1240setup_control_msg (
1241#ifndef WIN32
1242                   struct msghdr
1243#else
1244                   WSAMSG
1245#endif
1246                                 *msg, enum ctl_what cw,
1247        const struct lsquic_out_spec *spec, unsigned char *buf, size_t bufsz)
1248{
1249    struct cmsghdr *cmsg;
1250    struct sockaddr_in *local_sa;
1251    struct sockaddr_in6 *local_sa6;
1252#if __linux__ || __APPLE__ || WIN32
1253    struct in_pktinfo info;
1254#endif
1255    struct in6_pktinfo info6;
1256    size_t ctl_len;
1257
1258#ifndef WIN32
1259    msg->msg_control    = buf;
1260    msg->msg_controllen = bufsz;
1261#else
1262    msg->Control.buf    = (char*)buf;
1263    msg->Control.len = bufsz;
1264#endif
1265
1266    /* Need to zero the buffer due to a bug(?) in CMSG_NXTHDR.  See
1267     * https://stackoverflow.com/questions/27601849/cmsg-nxthdr-returns-null-even-though-there-are-more-cmsghdr-objects
1268     */
1269    memset(buf, 0, bufsz);
1270
1271    ctl_len = 0;
1272    for (cmsg = CMSG_FIRSTHDR(msg); cw && cmsg; cmsg = CMSG_NXTHDR(msg, cmsg))
1273    {
1274        if (cw & CW_SENDADDR)
1275        {
1276            if (AF_INET == spec->dest_sa->sa_family)
1277            {
1278                local_sa = (struct sockaddr_in *) spec->local_sa;
1279#if __linux__ || __APPLE__
1280                memset(&info, 0, sizeof(info));
1281                info.ipi_spec_dst = local_sa->sin_addr;
1282                cmsg->cmsg_level    = IPPROTO_IP;
1283                cmsg->cmsg_type     = IP_PKTINFO;
1284                cmsg->cmsg_len      = CMSG_LEN(sizeof(info));
1285                ctl_len += CMSG_SPACE(sizeof(info));
1286                memcpy(CMSG_DATA(cmsg), &info, sizeof(info));
1287#elif WIN32
1288                memset(&info, 0, sizeof(info));
1289                info.ipi_addr = local_sa->sin_addr;
1290                cmsg->cmsg_level    = IPPROTO_IP;
1291                cmsg->cmsg_type     = IP_PKTINFO;
1292                cmsg->cmsg_len      = CMSG_LEN(sizeof(info));
1293                ctl_len += CMSG_SPACE(sizeof(info));
1294                memcpy(WSA_CMSG_DATA(cmsg), &info, sizeof(info));
1295#else
1296                cmsg->cmsg_level    = IPPROTO_IP;
1297                cmsg->cmsg_type     = IP_SENDSRCADDR;
1298                cmsg->cmsg_len      = CMSG_LEN(sizeof(local_sa->sin_addr));
1299                ctl_len += CMSG_SPACE(sizeof(local_sa->sin_addr));
1300                memcpy(CMSG_DATA(cmsg), &local_sa->sin_addr,
1301                                                    sizeof(local_sa->sin_addr));
1302#endif
1303            }
1304            else
1305            {
1306                local_sa6 = (struct sockaddr_in6 *) spec->local_sa;
1307                memset(&info6, 0, sizeof(info6));
1308                info6.ipi6_addr = local_sa6->sin6_addr;
1309                cmsg->cmsg_level    = IPPROTO_IPV6;
1310                cmsg->cmsg_type     = IPV6_PKTINFO;
1311                cmsg->cmsg_len      = CMSG_LEN(sizeof(info6));
1312#ifndef WIN32
1313                memcpy(CMSG_DATA(cmsg), &info6, sizeof(info6));
1314#else
1315                memcpy(WSA_CMSG_DATA(cmsg), &info6, sizeof(info6));
1316#endif
1317                ctl_len += CMSG_SPACE(sizeof(info6));
1318            }
1319            cw &= ~CW_SENDADDR;
1320        }
1321#if ECN_SUPPORTED
1322        else if (cw & CW_ECN)
1323        {
1324            if (AF_INET == spec->dest_sa->sa_family)
1325            {
1326                const
1327#if defined(__FreeBSD__)
1328                      unsigned char
1329#else
1330                      int
1331#endif
1332                                    tos = spec->ecn;
1333                cmsg->cmsg_level = IPPROTO_IP;
1334                cmsg->cmsg_type  = IP_TOS;
1335                cmsg->cmsg_len   = CMSG_LEN(sizeof(tos));
1336                memcpy(CMSG_DATA(cmsg), &tos, sizeof(tos));
1337                ctl_len += CMSG_SPACE(sizeof(tos));
1338            }
1339            else
1340            {
1341                const int tos = spec->ecn;
1342                cmsg->cmsg_level = IPPROTO_IPV6;
1343                cmsg->cmsg_type  = IPV6_TCLASS;
1344                cmsg->cmsg_len   = CMSG_LEN(sizeof(tos));
1345                memcpy(CMSG_DATA(cmsg), &tos, sizeof(tos));
1346                ctl_len += CMSG_SPACE(sizeof(tos));
1347            }
1348            cw &= ~CW_ECN;
1349        }
1350#endif
1351        else
1352            assert(0);
1353    }
1354
1355#ifndef WIN32
1356    msg->msg_controllen = ctl_len;
1357#else
1358    msg->Control.len = ctl_len;
1359#endif
1360}
1361
1362
1363#if HAVE_SENDMMSG
1364static int
1365send_packets_using_sendmmsg (const struct lsquic_out_spec *specs,
1366                                                        unsigned count)
1367{
1368#ifndef NDEBUG
1369    {
1370        /* This only works for a single port!  If the specs contain more
1371         * than one socket, this function does *NOT* work.  We check it
1372         * here just in case:
1373         */
1374        void *ctx;
1375        unsigned i;
1376        for (i = 1, ctx = specs[i].peer_ctx;
1377                i < count;
1378                    ctx = specs[i].peer_ctx, ++i)
1379            assert(ctx == specs[i - 1].peer_ctx);
1380    }
1381#endif
1382
1383    const struct service_port *const sport = specs[0].peer_ctx;
1384    const int fd = sport->fd;
1385    enum ctl_what cw;
1386    unsigned i;
1387    int s, saved_errno;
1388    uintptr_t ancil_key, prev_ancil_key;
1389    struct mmsghdr mmsgs[1024];
1390    union {
1391        /* cmsg(3) recommends union for proper alignment */
1392        unsigned char buf[ CMSG_SPACE(
1393            MAX(
1394#if __linux__
1395                                        sizeof(struct in_pktinfo)
1396#else
1397                                        sizeof(struct in_addr)
1398#endif
1399                                        , sizeof(struct in6_pktinfo))
1400                                                                  )
1401#if ECN_SUPPORTED
1402            + CMSG_SPACE(sizeof(int))
1403#endif
1404                                                                    ];
1405        struct cmsghdr cmsg;
1406    } ancil [ sizeof(mmsgs) / sizeof(mmsgs[0]) ];
1407
1408    prev_ancil_key = 0;
1409    for (i = 0; i < count && i < sizeof(mmsgs) / sizeof(mmsgs[0]); ++i)
1410    {
1411        mmsgs[i].msg_hdr.msg_name       = (void *) specs[i].dest_sa;
1412        mmsgs[i].msg_hdr.msg_namelen    = (AF_INET == specs[i].dest_sa->sa_family ?
1413                                            sizeof(struct sockaddr_in) :
1414                                            sizeof(struct sockaddr_in6)),
1415        mmsgs[i].msg_hdr.msg_iov        = specs[i].iov;
1416        mmsgs[i].msg_hdr.msg_iovlen     = specs[i].iovlen;
1417        mmsgs[i].msg_hdr.msg_flags      = 0;
1418        if ((sport->sp_flags & SPORT_SERVER) && specs[i].local_sa->sa_family)
1419        {
1420            cw = CW_SENDADDR;
1421            ancil_key = (uintptr_t) specs[i].local_sa;
1422            assert(0 == (ancil_key & 3));
1423        }
1424        else
1425        {
1426            cw = 0;
1427            ancil_key = 0;
1428        }
1429#if ECN_SUPPORTED
1430        if (sport->sp_prog->prog_api.ea_settings->es_ecn && specs[i].ecn)
1431        {
1432            cw |= CW_ECN;
1433            ancil_key |= specs[i].ecn;
1434        }
1435#endif
1436        if (cw && prev_ancil_key == ancil_key)
1437        {
1438            /* Reuse previous ancillary message */
1439            assert(i > 0);
1440#ifndef WIN32
1441            mmsgs[i].msg_hdr.msg_control    = mmsgs[i - 1].msg_hdr.msg_control;
1442            mmsgs[i].msg_hdr.msg_controllen = mmsgs[i - 1].msg_hdr.msg_controllen;
1443#else
1444            mmsgs[i].msg_hdr.Control.buf    = mmsgs[i - 1].msg_hdr.Control.buf;
1445            mmsgs[i].msg_hdr.Control.len    = mmsgs[i - 1].msg_hdr.Control.len;
1446#endif
1447        }
1448        else if (cw)
1449        {
1450            prev_ancil_key = ancil_key;
1451            setup_control_msg(&mmsgs[i].msg_hdr, cw, &specs[i], ancil[i].buf,
1452                                                    sizeof(ancil[i].buf));
1453        }
1454        else
1455        {
1456            prev_ancil_key = 0;
1457#ifndef WIN32
1458            mmsgs[i].msg_hdr.msg_control    = NULL;
1459            mmsgs[i].msg_hdr.msg_controllen = 0;
1460#else
1461            mmsgs[i].msg_hdr.Control.buf    = NULL;
1462            mmsgs[i].msg_hdr.Control.len    = 0;
1463#endif
1464        }
1465    }
1466
1467    s = sendmmsg(fd, mmsgs, count, 0);
1468    if (s < (int) count)
1469    {
1470        saved_errno = errno;
1471        prog_sport_cant_send(sport->sp_prog, sport->fd);
1472        if (s < 0)
1473        {
1474            LSQ_WARN("sendmmsg failed: %s", strerror(saved_errno));
1475            errno = saved_errno;
1476        }
1477        else if (s > 0)
1478            errno = EAGAIN;
1479        else
1480            errno = saved_errno;
1481    }
1482
1483    return s;
1484}
1485
1486
1487#endif
1488
1489
1490#if LSQUIC_PREFERRED_ADDR
1491static const struct service_port *
1492find_sport (struct prog *prog, const struct sockaddr *local_sa)
1493{
1494    const struct service_port *sport;
1495    const struct sockaddr *addr;
1496    size_t len;
1497
1498    TAILQ_FOREACH(sport, prog->prog_sports, next_sport)
1499    {
1500        addr = (struct sockaddr *) &sport->sp_local_addr;
1501        if (addr->sa_family == local_sa->sa_family)
1502        {
1503            len = addr->sa_family == AF_INET ? sizeof(struct sockaddr_in)
1504                                             : sizeof(struct sockaddr_in6);
1505            if (0 == memcmp(addr, local_sa, len))
1506                return sport;
1507        }
1508    }
1509
1510    assert(0);
1511    return NULL;
1512}
1513
1514
1515#endif
1516
1517
1518static int
1519send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count)
1520{
1521    const struct service_port *sport;
1522    enum ctl_what cw;
1523    unsigned n;
1524    int s = 0;
1525#ifndef WIN32
1526    struct msghdr msg;
1527#else
1528    DWORD bytes;
1529    WSAMSG msg;
1530#endif
1531    union {
1532        /* cmsg(3) recommends union for proper alignment */
1533#if __linux__ || WIN32
1534#	define SIZE1 sizeof(struct in_pktinfo)
1535#else
1536#	define SIZE1 sizeof(struct in_addr)
1537#endif
1538        unsigned char buf[
1539            CMSG_SPACE(MAX(SIZE1, sizeof(struct in6_pktinfo)))
1540#if ECN_SUPPORTED
1541            + CMSG_SPACE(sizeof(int))
1542#endif
1543        ];
1544        struct cmsghdr cmsg;
1545    } ancil;
1546    uintptr_t ancil_key, prev_ancil_key;
1547
1548    if (0 == count)
1549        return 0;
1550
1551    const unsigned orig_count = count;
1552    const unsigned out_limit = packet_out_limit();
1553    if (out_limit && count > out_limit)
1554        count = out_limit;
1555#if LSQUIC_RANDOM_SEND_FAILURE
1556    {
1557        const char *freq_str = getenv("LSQUIC_RANDOM_SEND_FAILURE");
1558        int freq;
1559        if (freq_str)
1560            freq = atoi(freq_str);
1561        else
1562            freq = 10;
1563        if (rand() % freq == 0)
1564        {
1565            assert(count > 0);
1566            sport = specs[0].peer_ctx;
1567            LSQ_NOTICE("sending \"randomly\" fails");
1568            prog_sport_cant_send(sport->sp_prog, sport->fd);
1569            goto random_send_failure;
1570        }
1571    }
1572#endif
1573
1574    n = 0;
1575    prev_ancil_key = 0;
1576    do
1577    {
1578        sport = specs[n].peer_ctx;
1579#if LSQUIC_PREFERRED_ADDR
1580        if (sport->sp_prog->prog_flags & PROG_SEARCH_ADDRS)
1581            sport = find_sport(sport->sp_prog, specs[n].local_sa);
1582#endif
1583#ifndef WIN32
1584        msg.msg_name       = (void *) specs[n].dest_sa;
1585        msg.msg_namelen    = (AF_INET == specs[n].dest_sa->sa_family ?
1586                                            sizeof(struct sockaddr_in) :
1587                                            sizeof(struct sockaddr_in6)),
1588        msg.msg_iov        = specs[n].iov;
1589        msg.msg_iovlen     = specs[n].iovlen;
1590        msg.msg_flags      = 0;
1591#else
1592        msg.name           = (void *) specs[n].dest_sa;
1593        msg.namelen        = (AF_INET == specs[n].dest_sa->sa_family ?
1594                                            sizeof(struct sockaddr_in) :
1595                                            sizeof(struct sockaddr_in6)),
1596        msg.lpBuffers      = specs[n].iov;
1597        msg.dwBufferCount  = specs[n].iovlen;
1598        msg.dwFlags        = 0;
1599#endif
1600        if ((sport->sp_flags & SPORT_SERVER) && specs[n].local_sa->sa_family)
1601        {
1602            cw = CW_SENDADDR;
1603            ancil_key = (uintptr_t) specs[n].local_sa;
1604            assert(0 == (ancil_key & 3));
1605        }
1606        else
1607        {
1608            cw = 0;
1609            ancil_key = 0;
1610        }
1611#if ECN_SUPPORTED
1612        if (sport->sp_prog->prog_api.ea_settings->es_ecn && specs[n].ecn)
1613        {
1614            cw |= CW_ECN;
1615            ancil_key |= specs[n].ecn;
1616        }
1617#endif
1618        if (cw && prev_ancil_key == ancil_key)
1619        {
1620            /* Reuse previous ancillary message */
1621            ;
1622        }
1623        else if (cw)
1624        {
1625            prev_ancil_key = ancil_key;
1626            setup_control_msg(&msg, cw, &specs[n], ancil.buf, sizeof(ancil.buf));
1627        }
1628        else
1629        {
1630            prev_ancil_key = 0;
1631#ifndef WIN32
1632            msg.msg_control = NULL;
1633            msg.msg_controllen = 0;
1634#else
1635            msg.Control.buf = NULL;
1636            msg.Control.len = 0;
1637#endif
1638        }
1639#ifndef WIN32
1640        s = sendmsg(sport->fd, &msg, 0);
1641#else
1642        s = pfnWSASendMsg(sport->fd, &msg, 0, &bytes, NULL, NULL);
1643#endif
1644        if (s < 0)
1645        {
1646#ifndef WIN32
1647            LSQ_INFO("sendto failed: %s", strerror(errno));
1648#else
1649            LSQ_INFO("sendto failed: %s", WSAGetLastError());
1650#endif
1651            break;
1652        }
1653        ++n;
1654    }
1655    while (n < count);
1656
1657    if (n < orig_count)
1658        prog_sport_cant_send(sport->sp_prog, sport->fd);
1659
1660    if (n > 0)
1661    {
1662        if (n < orig_count && out_limit)
1663            errno = EAGAIN;
1664        return n;
1665    }
1666    else
1667    {
1668        assert(s < 0);
1669#if LSQUIC_RANDOM_SEND_FAILURE
1670  random_send_failure:
1671#endif
1672        return -1;
1673    }
1674}
1675
1676
1677int
1678sport_packets_out (void *ctx, const struct lsquic_out_spec *specs,
1679                   unsigned count)
1680{
1681#if HAVE_SENDMMSG
1682    const struct prog *prog = ctx;
1683    if (prog->prog_use_sendmmsg)
1684        return send_packets_using_sendmmsg(specs, count);
1685    else
1686#endif
1687        return send_packets_one_by_one(specs, count);
1688}
1689
1690
1691int
1692set_engine_option (struct lsquic_engine_settings *settings,
1693                   int *version_cleared, const char *name)
1694{
1695    int len;
1696    const char *val = strchr(name, '=');
1697    if (!val)
1698        return -1;
1699    len = val - name;
1700    ++val;
1701
1702    switch (len)
1703    {
1704    case 2:
1705        if (0 == strncmp(name, "ua", 2))
1706        {
1707            settings->es_ua = val;
1708            return 0;
1709        }
1710        break;
1711    case 3:
1712        if (0 == strncmp(name, "ecn", 1))
1713        {
1714            settings->es_ecn = atoi(val);
1715#if !ECN_SUPPORTED
1716            if (settings->es_ecn)
1717            {
1718                LSQ_ERROR("ECN is not supported on this platform");
1719                break;
1720            }
1721#endif
1722            return 0;
1723        }
1724        break;
1725    case 4:
1726        if (0 == strncmp(name, "cfcw", 4))
1727        {
1728            settings->es_cfcw = atoi(val);
1729            return 0;
1730        }
1731        if (0 == strncmp(name, "sfcw", 4))
1732        {
1733            settings->es_sfcw = atoi(val);
1734            return 0;
1735        }
1736        if (0 == strncmp(name, "spin", 4))
1737        {
1738            settings->es_spin = atoi(val);
1739            return 0;
1740        }
1741        break;
1742    case 7:
1743        if (0 == strncmp(name, "version", 7))
1744        {
1745            if (!*version_cleared)
1746            {
1747                *version_cleared = 1;
1748                settings->es_versions = 0;
1749            }
1750            enum lsquic_version ver = lsquic_str2ver(val, strlen(val));
1751            if (ver < N_LSQVER)
1752            {
1753                settings->es_versions |= 1 << ver;
1754                return 0;
1755            }
1756            ver = lsquic_alpn2ver(val, strlen(val));
1757            if (ver < N_LSQVER)
1758            {
1759                settings->es_versions |= 1 << ver;
1760                return 0;
1761            }
1762        }
1763        else if (0 == strncmp(name, "rw_once", 7))
1764        {
1765            settings->es_rw_once = atoi(val);
1766            return 0;
1767        }
1768        else if (0 == strncmp(name, "cc_algo", 7))
1769        {
1770            settings->es_cc_algo = atoi(val);
1771            return 0;
1772        }
1773        else if (0 == strncmp(name, "ql_bits", 7))
1774        {
1775            settings->es_ql_bits = atoi(val);
1776            return 0;
1777        }
1778        break;
1779    case 8:
1780        if (0 == strncmp(name, "max_cfcw", 8))
1781        {
1782            settings->es_max_cfcw = atoi(val);
1783            return 0;
1784        }
1785        if (0 == strncmp(name, "max_sfcw", 8))
1786        {
1787            settings->es_max_sfcw = atoi(val);
1788            return 0;
1789        }
1790        if (0 == strncmp(name, "scid_len", 8))
1791        {
1792            settings->es_scid_len = atoi(val);
1793            return 0;
1794        }
1795        break;
1796    case 9:
1797        if (0 == strncmp(name, "send_prst", 9))
1798        {
1799            settings->es_send_prst = atoi(val);
1800            return 0;
1801        }
1802        break;
1803    case 10:
1804        if (0 == strncmp(name, "honor_prst", 10))
1805        {
1806            settings->es_honor_prst = atoi(val);
1807            return 0;
1808        }
1809        if (0 == strncmp(name, "timestamps", 10))
1810        {
1811            settings->es_timestamps = atoi(val);
1812            return 0;
1813        }
1814        break;
1815    case 11:
1816        if (0 == strncmp(name, "ping_period", 11))
1817        {
1818            settings->es_ping_period = atoi(val);
1819            return 0;
1820        }
1821        break;
1822    case 12:
1823        if (0 == strncmp(name, "idle_conn_to", 12))
1824        {
1825            settings->es_idle_conn_to = atoi(val);
1826            return 0;
1827        }
1828        if (0 == strncmp(name, "idle_timeout", 12))
1829        {
1830            settings->es_idle_timeout = atoi(val);
1831            return 0;
1832        }
1833        if (0 == strncmp(name, "silent_close", 12))
1834        {
1835            settings->es_silent_close = atoi(val);
1836            return 0;
1837        }
1838        if (0 == strncmp(name, "support_push", 12))
1839        {
1840            settings->es_support_push = atoi(val);
1841            return 0;
1842        }
1843        if (0 == strncmp(name, "support_nstp", 12))
1844        {
1845            settings->es_support_nstp = atoi(val);
1846            return 0;
1847        }
1848        if (0 == strncmp(name, "pace_packets", 12))
1849        {
1850            settings->es_pace_packets = atoi(val);
1851            return 0;
1852        }
1853        if (0 == strncmp(name, "handshake_to", 12))
1854        {
1855            settings->es_handshake_to = atoi(val);
1856            return 0;
1857        }
1858        if (0 == strncmp(name, "delayed_acks", 12))
1859        {
1860            settings->es_delayed_acks = atoi(val);
1861            return 0;
1862        }
1863        break;
1864    case 13:
1865        if (0 == strncmp(name, "support_tcid0", 13))
1866        {
1867            settings->es_support_tcid0 = atoi(val);
1868            return 0;
1869        }
1870        if (0 == strncmp(name, "init_max_data", 13))
1871        {
1872            settings->es_init_max_data = atoi(val);
1873            return 0;
1874        }
1875        if (0 == strncmp(name, "scid_iss_rate", 13))
1876        {
1877            settings->es_scid_iss_rate = atoi(val);
1878            return 0;
1879        }
1880        break;
1881    case 14:
1882        if (0 == strncmp(name, "max_streams_in", 14))
1883        {
1884            settings->es_max_streams_in = atoi(val);
1885            return 0;
1886        }
1887        if (0 == strncmp(name, "progress_check", 14))
1888        {
1889            settings->es_progress_check = atoi(val);
1890            return 0;
1891        }
1892        break;
1893    case 15:
1894        if (0 == strncmp(name, "allow_migration", 15))
1895        {
1896            settings->es_allow_migration = atoi(val);
1897            return 0;
1898        }
1899        break;
1900    case 16:
1901        if (0 == strncmp(name, "proc_time_thresh", 16))
1902        {
1903            settings->es_proc_time_thresh = atoi(val);
1904            return 0;
1905        }
1906        break;
1907    case 18:
1908        if (0 == strncmp(name, "qpack_enc_max_size", 18))
1909        {
1910            settings->es_qpack_enc_max_size = atoi(val);
1911            return 0;
1912        }
1913        if (0 == strncmp(name, "qpack_dec_max_size", 18))
1914        {
1915            settings->es_qpack_dec_max_size = atoi(val);
1916            return 0;
1917        }
1918        break;
1919    case 20:
1920        if (0 == strncmp(name, "max_header_list_size", 20))
1921        {
1922            settings->es_max_header_list_size = atoi(val);
1923            return 0;
1924        }
1925        if (0 == strncmp(name, "init_max_streams_uni", 20))
1926        {
1927            settings->es_init_max_streams_uni = atoi(val);
1928            return 0;
1929        }
1930        break;
1931    case 21:
1932        if (0 == strncmp(name, "qpack_enc_max_blocked", 21))
1933        {
1934            settings->es_qpack_enc_max_blocked = atoi(val);
1935            return 0;
1936        }
1937        if (0 == strncmp(name, "qpack_dec_max_blocked", 21))
1938        {
1939            settings->es_qpack_dec_max_blocked = atoi(val);
1940            return 0;
1941        }
1942        break;
1943    case 23:
1944        if (0 == strncmp(name, "max_udp_payload_size_rx", 18))
1945        {
1946            settings->es_max_udp_payload_size_rx = atoi(val);
1947            return 0;
1948        }
1949        break;
1950    case 24:
1951        if (0 == strncmp(name, "init_max_stream_data_uni", 24))
1952        {
1953            settings->es_init_max_stream_data_uni = atoi(val);
1954            return 0;
1955        }
1956        break;
1957    case 31:
1958        if (0 == strncmp(name, "init_max_stream_data_bidi_local", 31))
1959        {
1960            settings->es_init_max_stream_data_bidi_local = atoi(val);
1961            return 0;
1962        }
1963        break;
1964    case 32:
1965        if (0 == strncmp(name, "init_max_stream_data_bidi_remote", 32))
1966        {
1967            settings->es_init_max_stream_data_bidi_remote = atoi(val);
1968            return 0;
1969        }
1970        break;
1971    }
1972
1973    return -1;
1974}
1975
1976
1977#define MAX_PACKOUT_BUF_SZ 1370
1978
1979struct packout_buf
1980{
1981    SLIST_ENTRY(packout_buf)    next_free_pb;
1982};
1983
1984
1985void
1986pba_init (struct packout_buf_allocator *pba, unsigned max)
1987{
1988    SLIST_INIT(&pba->free_packout_bufs);
1989    pba->max   = max;
1990    pba->n_out = 0;
1991}
1992
1993
1994void *
1995pba_allocate (void *packout_buf_allocator, void *peer_ctx, unsigned short size,
1996                                                                char is_ipv6)
1997{
1998    struct packout_buf_allocator *const pba = packout_buf_allocator;
1999    struct packout_buf *pb;
2000
2001    if (size > MAX_PACKOUT_BUF_SZ)
2002    {
2003        fprintf(stderr, "packout buf size too large: %hu", size);
2004        abort();
2005    }
2006
2007    if (pba->max && pba->n_out >= pba->max)
2008    {
2009        LSQ_DEBUG("# outstanding packout bufs reached the limit of %u, "
2010            "returning NULL", pba->max);
2011        return NULL;
2012    }
2013
2014#if LSQUIC_USE_POOLS
2015    pb = SLIST_FIRST(&pba->free_packout_bufs);
2016    if (pb)
2017        SLIST_REMOVE_HEAD(&pba->free_packout_bufs, next_free_pb);
2018    else
2019#endif
2020        pb = malloc(MAX_PACKOUT_BUF_SZ);
2021
2022    if (pb)
2023        ++pba->n_out;
2024
2025    return pb;
2026}
2027
2028
2029void
2030pba_release (void *packout_buf_allocator, void *peer_ctx, void *obj, char ipv6)
2031{
2032    struct packout_buf_allocator *const pba = packout_buf_allocator;
2033#if LSQUIC_USE_POOLS
2034    struct packout_buf *const pb = obj;
2035    SLIST_INSERT_HEAD(&pba->free_packout_bufs, pb, next_free_pb);
2036#else
2037    free(obj);
2038#endif
2039    --pba->n_out;
2040}
2041
2042
2043void
2044pba_cleanup (struct packout_buf_allocator *pba)
2045{
2046#if LSQUIC_USE_POOLS
2047    unsigned n = 0;
2048    struct packout_buf *pb;
2049#endif
2050
2051    if (pba->n_out)
2052        LSQ_WARN("%u packout bufs outstanding at deinit", pba->n_out);
2053
2054#if LSQUIC_USE_POOLS
2055    while ((pb = SLIST_FIRST(&pba->free_packout_bufs)))
2056    {
2057        SLIST_REMOVE_HEAD(&pba->free_packout_bufs, next_free_pb);
2058        free(pb);
2059        ++n;
2060    }
2061
2062    LSQ_INFO("pba deinitialized, freed %u packout bufs", n);
2063#endif
2064}
2065
2066
2067void
2068print_conn_info (const lsquic_conn_t *conn)
2069{
2070    const char *cipher;
2071
2072    cipher = lsquic_conn_crypto_cipher(conn);
2073
2074    LSQ_INFO("Connection info: version: %u; cipher: %s; key size: %d, alg key size: %d",
2075        lsquic_conn_quic_version(conn),
2076        cipher ? cipher : "<null>",
2077        lsquic_conn_crypto_keysize(conn),
2078        lsquic_conn_crypto_alg_keysize(conn)
2079    );
2080}
2081
2082
2083struct reader_ctx
2084{
2085    size_t  file_size;
2086    size_t  nread;
2087    int     fd;
2088};
2089
2090
2091size_t
2092test_reader_size (void *void_ctx)
2093{
2094    struct reader_ctx *const ctx = void_ctx;
2095    return ctx->file_size - ctx->nread;
2096}
2097
2098
2099size_t
2100test_reader_read (void *void_ctx, void *buf, size_t count)
2101{
2102    struct reader_ctx *const ctx = void_ctx;
2103    ssize_t nread;
2104
2105    if (count > test_reader_size(ctx))
2106        count = test_reader_size(ctx);
2107
2108#ifndef WIN32
2109    nread = read(ctx->fd, buf, count);
2110#else
2111    nread = _read(ctx->fd, buf, count);
2112#endif
2113    if (nread >= 0)
2114    {
2115        ctx->nread += nread;
2116        return nread;
2117    }
2118    else
2119    {
2120        LSQ_WARN("%s: error reading from file: %s", __func__, strerror(errno));
2121        ctx->nread = ctx->file_size = 0;
2122        return 0;
2123    }
2124}
2125
2126
2127struct reader_ctx *
2128create_lsquic_reader_ctx (const char *filename)
2129{
2130    int fd;
2131    struct stat st;
2132
2133#ifndef WIN32
2134    fd = open(filename, O_RDONLY);
2135#else
2136    fd = _open(filename, _O_RDONLY);
2137#endif
2138    if (fd < 0)
2139    {
2140        LSQ_ERROR("cannot open %s for reading: %s", filename, strerror(errno));
2141        return NULL;
2142    }
2143
2144    if (0 != fstat(fd, &st))
2145    {
2146        LSQ_ERROR("cannot fstat(%s) failed: %s", filename, strerror(errno));
2147        (void) close(fd);
2148        return NULL;
2149    }
2150    struct reader_ctx *ctx = malloc(sizeof(*ctx));
2151    ctx->file_size = st.st_size;
2152    ctx->nread = 0;
2153    ctx->fd = fd;
2154    return ctx;
2155}
2156
2157
2158void
2159destroy_lsquic_reader_ctx (struct reader_ctx *ctx)
2160{
2161    (void) close(ctx->fd);
2162    free(ctx);
2163}
2164
2165
2166int
2167sport_set_token (struct service_port *sport, const char *token_str)
2168{
2169    static const unsigned char c2b[0x100] =
2170    {
2171        [(int)'0'] = 0,
2172        [(int)'1'] = 1,
2173        [(int)'2'] = 2,
2174        [(int)'3'] = 3,
2175        [(int)'4'] = 4,
2176        [(int)'5'] = 5,
2177        [(int)'6'] = 6,
2178        [(int)'7'] = 7,
2179        [(int)'8'] = 8,
2180        [(int)'9'] = 9,
2181        [(int)'A'] = 0xA,
2182        [(int)'B'] = 0xB,
2183        [(int)'C'] = 0xC,
2184        [(int)'D'] = 0xD,
2185        [(int)'E'] = 0xE,
2186        [(int)'F'] = 0xF,
2187        [(int)'a'] = 0xA,
2188        [(int)'b'] = 0xB,
2189        [(int)'c'] = 0xC,
2190        [(int)'d'] = 0xD,
2191        [(int)'e'] = 0xE,
2192        [(int)'f'] = 0xF,
2193    };
2194    unsigned char *token;
2195    int len, i;
2196
2197    len = strlen(token_str);
2198    token = malloc(len / 2);
2199    if (!token)
2200        return -1;
2201    for (i = 0; i < len / 2; ++i)
2202        token[i] = (c2b[ (int) token_str[i * 2] ] << 4)
2203                 |  c2b[ (int) token_str[i * 2 + 1] ];
2204
2205    free(sport->sp_token_buf);
2206    sport->sp_token_buf = token;
2207    sport->sp_token_sz = len / 2;
2208    return 0;
2209}
2210