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