-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathpacket.go
267 lines (245 loc) · 7.1 KB
/
packet.go
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package quic
import (
"encoding/binary"
"fmt"
"golang.org/x/net/internal/quic/quicwire"
)
// packetType is a QUIC packet type.
// https://www.rfc-editor.org/rfc/rfc9000.html#section-17
type packetType byte
const (
packetTypeInvalid = packetType(iota)
packetTypeInitial
packetType0RTT
packetTypeHandshake
packetTypeRetry
packetType1RTT
packetTypeVersionNegotiation
)
func (p packetType) String() string {
switch p {
case packetTypeInitial:
return "Initial"
case packetType0RTT:
return "0-RTT"
case packetTypeHandshake:
return "Handshake"
case packetTypeRetry:
return "Retry"
case packetType1RTT:
return "1-RTT"
}
return fmt.Sprintf("unknown packet type %v", byte(p))
}
func (p packetType) qlogString() string {
switch p {
case packetTypeInitial:
return "initial"
case packetType0RTT:
return "0RTT"
case packetTypeHandshake:
return "handshake"
case packetTypeRetry:
return "retry"
case packetType1RTT:
return "1RTT"
}
return "unknown"
}
// Bits set in the first byte of a packet.
const (
headerFormLong = 0x80 // https://www.rfc-editor.org/rfc/rfc9000.html#section-17.2-3.2.1
headerFormShort = 0x00 // https://www.rfc-editor.org/rfc/rfc9000.html#section-17.3.1-4.2.1
fixedBit = 0x40 // https://www.rfc-editor.org/rfc/rfc9000.html#section-17.2-3.4.1
reservedLongBits = 0x0c // https://www.rfc-editor.org/rfc/rfc9000#section-17.2-8.2.1
reserved1RTTBits = 0x18 // https://www.rfc-editor.org/rfc/rfc9000#section-17.3.1-4.8.1
keyPhaseBit = 0x04 // https://www.rfc-editor.org/rfc/rfc9000#section-17.3.1-4.10.1
)
// Long Packet Type bits.
// https://www.rfc-editor.org/rfc/rfc9000.html#section-17.2-3.6.1
const (
longPacketTypeInitial = 0 << 4
longPacketType0RTT = 1 << 4
longPacketTypeHandshake = 2 << 4
longPacketTypeRetry = 3 << 4
)
// Frame types.
// https://www.rfc-editor.org/rfc/rfc9000.html#section-19
const (
frameTypePadding = 0x00
frameTypePing = 0x01
frameTypeAck = 0x02
frameTypeAckECN = 0x03
frameTypeResetStream = 0x04
frameTypeStopSending = 0x05
frameTypeCrypto = 0x06
frameTypeNewToken = 0x07
frameTypeStreamBase = 0x08 // low three bits carry stream flags
frameTypeMaxData = 0x10
frameTypeMaxStreamData = 0x11
frameTypeMaxStreamsBidi = 0x12
frameTypeMaxStreamsUni = 0x13
frameTypeDataBlocked = 0x14
frameTypeStreamDataBlocked = 0x15
frameTypeStreamsBlockedBidi = 0x16
frameTypeStreamsBlockedUni = 0x17
frameTypeNewConnectionID = 0x18
frameTypeRetireConnectionID = 0x19
frameTypePathChallenge = 0x1a
frameTypePathResponse = 0x1b
frameTypeConnectionCloseTransport = 0x1c
frameTypeConnectionCloseApplication = 0x1d
frameTypeHandshakeDone = 0x1e
)
// The low three bits of STREAM frames.
// https://www.rfc-editor.org/rfc/rfc9000.html#section-19.8
const (
streamOffBit = 0x04
streamLenBit = 0x02
streamFinBit = 0x01
)
// Maximum length of a connection ID.
const maxConnIDLen = 20
// isLongHeader returns true if b is the first byte of a long header.
func isLongHeader(b byte) bool {
return b&headerFormLong == headerFormLong
}
// getPacketType returns the type of a packet.
func getPacketType(b []byte) packetType {
if len(b) == 0 {
return packetTypeInvalid
}
if !isLongHeader(b[0]) {
if b[0]&fixedBit != fixedBit {
return packetTypeInvalid
}
return packetType1RTT
}
if len(b) < 5 {
return packetTypeInvalid
}
if b[1] == 0 && b[2] == 0 && b[3] == 0 && b[4] == 0 {
// Version Negotiation packets don't necessarily set the fixed bit.
return packetTypeVersionNegotiation
}
if b[0]&fixedBit != fixedBit {
return packetTypeInvalid
}
switch b[0] & 0x30 {
case longPacketTypeInitial:
return packetTypeInitial
case longPacketType0RTT:
return packetType0RTT
case longPacketTypeHandshake:
return packetTypeHandshake
case longPacketTypeRetry:
return packetTypeRetry
}
return packetTypeInvalid
}
// dstConnIDForDatagram returns the destination connection ID field of the
// first QUIC packet in a datagram.
func dstConnIDForDatagram(pkt []byte) (id []byte, ok bool) {
if len(pkt) < 1 {
return nil, false
}
var n int
var b []byte
if isLongHeader(pkt[0]) {
if len(pkt) < 6 {
return nil, false
}
n = int(pkt[5])
b = pkt[6:]
} else {
n = connIDLen
b = pkt[1:]
}
if len(b) < n {
return nil, false
}
return b[:n], true
}
// parseVersionNegotiation parses a Version Negotiation packet.
// The returned versions is a slice of big-endian uint32s.
// It returns (nil, nil, nil) for an invalid packet.
func parseVersionNegotiation(pkt []byte) (dstConnID, srcConnID, versions []byte) {
p, ok := parseGenericLongHeaderPacket(pkt)
if !ok {
return nil, nil, nil
}
if len(p.data)%4 != 0 {
return nil, nil, nil
}
return p.dstConnID, p.srcConnID, p.data
}
// appendVersionNegotiation appends a Version Negotiation packet to pkt,
// returning the result.
func appendVersionNegotiation(pkt, dstConnID, srcConnID []byte, versions ...uint32) []byte {
pkt = append(pkt, headerFormLong|fixedBit) // header byte
pkt = append(pkt, 0, 0, 0, 0) // Version (0 for Version Negotiation)
pkt = quicwire.AppendUint8Bytes(pkt, dstConnID) // Destination Connection ID
pkt = quicwire.AppendUint8Bytes(pkt, srcConnID) // Source Connection ID
for _, v := range versions {
pkt = binary.BigEndian.AppendUint32(pkt, v) // Supported Version
}
return pkt
}
// A longPacket is a long header packet.
type longPacket struct {
ptype packetType
version uint32
num packetNumber
dstConnID []byte
srcConnID []byte
payload []byte
// The extra data depends on the packet type:
// Initial: Token.
// Retry: Retry token and integrity tag.
extra []byte
}
// A shortPacket is a short header (1-RTT) packet.
type shortPacket struct {
num packetNumber
payload []byte
}
// A genericLongPacket is a long header packet of an arbitrary QUIC version.
// https://www.rfc-editor.org/rfc/rfc8999#section-5.1
type genericLongPacket struct {
version uint32
dstConnID []byte
srcConnID []byte
data []byte
}
func parseGenericLongHeaderPacket(b []byte) (p genericLongPacket, ok bool) {
if len(b) < 5 || !isLongHeader(b[0]) {
return genericLongPacket{}, false
}
b = b[1:]
// Version (32),
var n int
p.version, n = quicwire.ConsumeUint32(b)
if n < 0 {
return genericLongPacket{}, false
}
b = b[n:]
// Destination Connection ID Length (8),
// Destination Connection ID (0..2048),
p.dstConnID, n = quicwire.ConsumeUint8Bytes(b)
if n < 0 || len(p.dstConnID) > 2048/8 {
return genericLongPacket{}, false
}
b = b[n:]
// Source Connection ID Length (8),
// Source Connection ID (0..2048),
p.srcConnID, n = quicwire.ConsumeUint8Bytes(b)
if n < 0 || len(p.dstConnID) > 2048/8 {
return genericLongPacket{}, false
}
b = b[n:]
p.data = b
return p, true
}