lsquic_rechist.c revision b1a7c3f9
1/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * lsquic_rechist.c -- History of received packets.
4 */
5
6#include <assert.h>
7#include <errno.h>
8#include <inttypes.h>
9#include <stdlib.h>
10#include <string.h>
11
12#include "lsquic_int_types.h"
13#include "lsquic_types.h"
14#include "lsquic_rechist.h"
15
16#define LSQUIC_LOGGER_MODULE LSQLM_RECHIST
17#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(rechist->rh_conn)
18#include "lsquic_logger.h"
19
20
21void
22lsquic_rechist_init (struct lsquic_rechist *rechist,
23                                    const struct lsquic_conn *conn, int ietf)
24{
25    memset(rechist, 0, sizeof(*rechist));
26    rechist->rh_conn = conn;
27    rechist->rh_cutoff = ietf ? 0 : 1;
28    lsquic_packints_init(&rechist->rh_pints);
29    LSQ_DEBUG("instantiated received packet history");
30#if LSQUIC_ACK_ATTACK
31    const char *s = getenv("LSQUIC_ACK_ATTACK");
32    if (s && atoi(s))
33    {
34        LSQ_NOTICE("ACK attack mode ON!");
35        rechist->rh_flags |= RH_ACK_ATTACK;
36    }
37#endif
38}
39
40
41void
42lsquic_rechist_cleanup (lsquic_rechist_t *rechist)
43{
44    lsquic_packints_cleanup(&rechist->rh_pints);
45    memset(rechist, 0, sizeof(*rechist));
46}
47
48
49enum received_st
50lsquic_rechist_received (lsquic_rechist_t *rechist, lsquic_packno_t packno,
51                         lsquic_time_t now)
52{
53    const struct lsquic_packno_range *first_range;
54
55    LSQ_DEBUG("received %"PRIu64, packno);
56    if (packno < rechist->rh_cutoff)
57    {
58        if (packno)
59            return REC_ST_DUP;
60        else
61            return REC_ST_ERR;
62    }
63
64    first_range = lsquic_packints_first(&rechist->rh_pints);
65    if (!first_range || packno > first_range->high)
66        rechist->rh_largest_acked_received = now;
67
68    switch (lsquic_packints_add(&rechist->rh_pints, packno))
69    {
70    case PACKINTS_OK:
71        ++rechist->rh_n_packets;
72        return REC_ST_OK;
73    case PACKINTS_DUP:
74        return REC_ST_DUP;
75    default:
76        assert(0);
77    case PACKINTS_ERR:
78        return REC_ST_ERR;
79    }
80}
81
82
83void
84lsquic_rechist_stop_wait (lsquic_rechist_t *rechist, lsquic_packno_t cutoff)
85{
86    LSQ_INFO("stop wait: %"PRIu64, cutoff);
87
88    if (rechist->rh_flags & RH_CUTOFF_SET)
89    {
90        assert(cutoff >= rechist->rh_cutoff);  /* Check performed in full_conn */
91        if (cutoff == rechist->rh_cutoff)
92            return;
93    }
94
95    rechist->rh_cutoff = cutoff;
96    rechist->rh_flags |= RH_CUTOFF_SET;
97    struct packet_interval *pi, *next;
98    for (pi = TAILQ_FIRST(&rechist->rh_pints.pk_intervals); pi; pi = next)
99    {
100        next = TAILQ_NEXT(pi, next_pi);
101        if (pi->range.low < cutoff)
102        {
103            if (pi->range.high < cutoff)
104            {
105                rechist->rh_n_packets -= (unsigned)(pi->range.high - pi->range.low + 1);
106                TAILQ_REMOVE(&rechist->rh_pints.pk_intervals, pi, next_pi);
107                free(pi);
108            }
109            else
110            {
111                rechist->rh_n_packets -= (unsigned)(cutoff - pi->range.low);
112                pi->range.low = cutoff;
113            }
114        }
115    }
116    lsquic_packints_sanity_check(&rechist->rh_pints);
117}
118
119
120lsquic_packno_t
121lsquic_rechist_largest_packno (const lsquic_rechist_t *rechist)
122{
123    const struct packet_interval *pi =
124                                TAILQ_FIRST(&rechist->rh_pints.pk_intervals);
125    if (pi)
126        return pi->range.high;
127    else
128        return 0;   /* Don't call this function if history is empty */
129}
130
131
132lsquic_packno_t
133lsquic_rechist_cutoff (const lsquic_rechist_t *rechist)
134{
135    if (rechist->rh_flags & RH_CUTOFF_SET)
136        return rechist->rh_cutoff;
137    else
138        return 0;
139}
140
141
142lsquic_time_t
143lsquic_rechist_largest_recv (const lsquic_rechist_t *rechist)
144{
145    return rechist->rh_largest_acked_received;
146}
147
148
149const struct lsquic_packno_range *
150lsquic_rechist_first (lsquic_rechist_t *rechist)
151{
152#if LSQUIC_ACK_ATTACK
153    if (rechist->rh_flags & RH_ACK_ATTACK)
154    {
155        /* This only performs the lazy variant of the attack.  An aggressive
156         * attack would increase the value of high number.
157         */
158        const struct lsquic_packno_range *range;
159
160        range = lsquic_packints_first(&rechist->rh_pints);
161        if (!range)
162            return NULL;
163        rechist->rh_first = *range;
164        range = &TAILQ_LAST(&rechist->rh_pints.pk_intervals, pinhead)->range;
165        rechist->rh_first.low = range->low;
166        return &rechist->rh_first;
167    }
168#endif
169    return lsquic_packints_first(&rechist->rh_pints);
170}
171
172
173const struct lsquic_packno_range *
174lsquic_rechist_next (lsquic_rechist_t *rechist)
175{
176#if LSQUIC_ACK_ATTACK
177    if (rechist->rh_flags & RH_ACK_ATTACK)
178        return NULL;
179#endif
180    return lsquic_packints_next(&rechist->rh_pints);
181}
182
183
184size_t
185lsquic_rechist_mem_used (const struct lsquic_rechist *rechist)
186{
187    return sizeof(*rechist)
188         - sizeof(rechist->rh_pints)
189         + lsquic_packints_mem_used(&rechist->rh_pints);
190}
191
192
193const struct lsquic_packno_range *
194lsquic_rechist_peek (const struct lsquic_rechist *rechist)
195{
196    const struct packet_interval *pint;
197
198    pint = TAILQ_FIRST(&rechist->rh_pints.pk_intervals);
199    if (pint)
200        return &pint->range;
201    else
202        return NULL;
203}
204