-
Notifications
You must be signed in to change notification settings - Fork 1
/
esp-seqno.c
141 lines (130 loc) · 4.98 KB
/
esp-seqno.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*
* OpenConnect (SSL + DTLS) VPN client
*
* Copyright © 2008-2015 Intel Corporation.
*
* Author: David Woodhouse <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#include <config.h>
#include "openconnect-internal.h"
#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#define DTLS_EMPTY_BITMAP (0xFFFFFFFFFFFFFFFFULL)
/* Eventually we're going to have to have more than one incoming ESP
context at a time, to allow for the overlap period during a rekey.
So pass the 'esp' even though for now it's redundant. */
int verify_packet_seqno(struct openconnect_info *vpninfo,
struct esp *esp, uint32_t seq)
{
/*
* For incoming, esp->seq is the next *expected* packet, being
* the sequence number *after* the latest we have received.
*
* Since it must always be true that packet esp->seq-1 has been
* received, so there's no need to explicitly record that.
*
* So the backlog bitmap covers the 64 packets prior to that,
* with the LSB representing packet (esp->seq - 2), and the MSB
* representing (esp->seq - 65). A received packet is represented
* by a zero bit, and a missing packet is represented by a one.
*
* Thus we can allow out-of-order reception of packets that are
* within a reasonable interval of the latest packet received.
*/
if (seq == esp->seq) {
/* The common case. This is the packet we expected next. */
esp->seq_backlog <<= 1;
/* This might reach a value higher than the 32-bit ESP sequence
* numbers can actually reach. Which is fine. When that
* happens, we'll do the right thing and just not accept any
* newer packets. Someone needs to start a new epoch. */
esp->seq++;
vpn_progress(vpninfo, PRG_TRACE,
_("Accepting expected ESP packet with seq %u\n"),
seq);
return 0;
} else if (seq > esp->seq) {
/* The packet we were expecting has gone missing; this one is newer.
* We always advance the window to accommodate it. */
uint32_t delta = seq - esp->seq;
if (delta >= 64) {
/* We jumped a long way into the future. We have not seen
* any of the previous 32 packets so set the backlog bitmap
* to all ones. */
esp->seq_backlog = DTLS_EMPTY_BITMAP;
} else if (delta == 63) {
/* Avoid undefined behaviour that shifting by 64 would incur.
* The (clear) top bit represents the packet which is currently
* esp->seq - 1, which we know was already received. */
esp->seq_backlog = DTLS_EMPTY_BITMAP >> 1;
} else {
/* We have missed (delta) packets. Shift the backlog by that
* amount *plus* the one we would have shifted it anyway if
* we'd received the packet we were expecting. The zero bit
* representing the packet which is currently esp->seq - 1,
* which we know has been received, ends up at bit position
* (1<<delta). Then we set all the bits lower than that, which
* represent the missing packets. */
esp->seq_backlog <<= delta + 1;
esp->seq_backlog |= (1ULL << delta) - 1;
}
vpn_progress(vpninfo, PRG_TRACE,
_("Accepting later-than-expected ESP packet with seq %u (expected %" PRIu64 ")\n"),
seq, esp->seq);
esp->seq = (uint64_t)seq + 1;
return 0;
} else {
/* This packet is older than the one we were expecting. By how much...? */
uint32_t delta = esp->seq - seq;
/* delta==0 is the overflow case where esp->seq is 0x100000000 and seq is 0 */
if (delta > 65 || delta == 0) {
/* Too old. We can't know if it's a replay. */
if (vpninfo->esp_replay_protect) {
vpn_progress(vpninfo, PRG_DEBUG,
_("Discarding ancient ESP packet with seq %u (expected %" PRIu64 ")\n"),
seq, esp->seq);
return -EINVAL;
} else {
vpn_progress(vpninfo, PRG_DEBUG,
_("Tolerating ancient ESP packet with seq %u (expected %" PRIu64 ")\n"),
seq, esp->seq);
return 0;
}
} else if (delta == 1) {
/* Not in the bitmask since it is by definition already received. */
replayed:
if (vpninfo->esp_replay_protect) {
vpn_progress(vpninfo, PRG_DEBUG,
_("Discarding replayed ESP packet with seq %u\n"),
seq);
return -EINVAL;
} else {
vpn_progress(vpninfo, PRG_DEBUG,
_("Tolerating replayed ESP packet with seq %u\n"),
seq);
return 0;
}
} else {
/* Within the backlog window, so we remember whether we've seen it or not. */
uint64_t mask = 1ULL << (delta - 2);
if (!(esp->seq_backlog & mask))
goto replayed;
esp->seq_backlog &= ~mask;
vpn_progress(vpninfo, PRG_TRACE,
_("Accepting out-of-order ESP packet with seq %u (expected %" PRIu64 ")\n"),
seq, esp->seq);
return 0;
}
}
}