forked from google/gopacket
-
Notifications
You must be signed in to change notification settings - Fork 0
/
defrag.go
357 lines (323 loc) · 10.3 KB
/
defrag.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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
// Copyright 2013 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
// Package ip4defrag implements a IPv4 defragmenter
package ip4defrag
import (
"container/list"
"errors"
"fmt"
"log"
"sync"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
// Quick and Easy to use debug code to trace
// how defrag works.
var debug debugging = false // or flip to true
type debugging bool
func (d debugging) Printf(format string, args ...interface{}) {
if d {
log.Printf(format, args...)
}
}
// Constants determining how to handle fragments.
// Reference RFC 791, page 25
const (
IPv4MinimumFragmentSize = 8 // Minimum size of a single fragment
IPv4MaximumSize = 65535 // Maximum size of a fragment (2^16)
IPv4MaximumFragmentOffset = 8183 // Maximum offset of a fragment
IPv4MaximumFragmentListLen = 8192 // Back out if we get more than this many fragments
)
// DefragIPv4 takes in an IPv4 packet with a fragment payload.
//
// It do not modify the IPv4 layer in place, 'in' remains untouched
// It returns a ready-to be used IPv4 layer.
//
// If the passed-in IPv4 layer is NOT fragmented, it will
// immediately return it without modifying the layer.
//
// If the IPv4 layer is a fragment and we don't have all
// fragments, it will return nil and store whatever internal
// information it needs to eventually defrag the packet.
//
// If the IPv4 layer is the last fragment needed to reconstruct
// the packet, a new IPv4 layer will be returned, and will be set to
// the entire defragmented packet,
//
// It use a map of all the running flows
//
// Usage example:
//
// func HandlePacket(in *layers.IPv4) err {
// defragger := ip4defrag.NewIPv4Defragmenter()
// in, err := defragger.DefragIPv4(in)
// if err != nil {
// return err
// } else if in == nil {
// return nil // packet fragment, we don't have whole packet yet.
// }
// // At this point, we know that 'in' is defragmented.
// //It may be the same 'in' passed to
// // HandlePacket, or it may not, but we don't really care :)
// ... do stuff to 'in' ...
//}
//
func (d *IPv4Defragmenter) DefragIPv4(in *layers.IPv4) (*layers.IPv4, error) {
return d.DefragIPv4WithTimestamp(in, time.Now())
}
// DefragIPv4WithTimestamp provides functionality of DefragIPv4 with
// an additional timestamp parameter which is used for discarding
// old fragments instead of time.Now()
//
// This is useful when operating on pcap files instead of live captured data
//
func (d *IPv4Defragmenter) DefragIPv4WithTimestamp(in *layers.IPv4, t time.Time) (*layers.IPv4, error) {
// check if we need to defrag
if st := d.dontDefrag(in); st == true {
debug.Printf("defrag: do nothing, do not need anything")
return in, nil
}
// perfom security checks
if err := d.securityChecks(in); err != nil {
debug.Printf("defrag: alert security check")
return nil, err
}
// ok, got a fragment
debug.Printf("defrag: got a new fragment in.Id=%d in.FragOffset=%d in.Flags=%d\n",
in.Id, in.FragOffset*8, in.Flags)
// have we already seen a flow between src/dst with that Id?
ipf := newIPv4(in)
var fl *fragmentList
var exist bool
d.Lock()
fl, exist = d.ipFlows[ipf]
if !exist {
debug.Printf("defrag: unknown flow, creating a new one\n")
fl = new(fragmentList)
d.ipFlows[ipf] = fl
}
d.Unlock()
// insert, and if final build it
out, err2 := fl.insert(in, t)
// at last, if we hit the maximum frag list len
// without any defrag success, we just drop everything and
// raise an error
if out == nil && fl.List.Len()+1 > IPv4MaximumFragmentListLen {
d.flush(ipf)
return nil, fmt.Errorf("defrag: Fragment List hits its maximum"+
"size(%d), without success. Flushing the list",
IPv4MaximumFragmentListLen)
}
// if we got a packet, it's a new one, and he is defragmented
if out != nil {
// when defrag is done for a flow between two ip
// clean the list
d.flush(ipf)
return out, nil
}
return nil, err2
}
// DiscardOlderThan forgets all packets without any activity since
// time t. It returns the number of FragmentList aka number of
// fragment packets it has discarded.
func (d *IPv4Defragmenter) DiscardOlderThan(t time.Time) int {
var nb int
d.Lock()
for k, v := range d.ipFlows {
if v.LastSeen.Before(t) {
nb = nb + 1
delete(d.ipFlows, k)
}
}
d.Unlock()
return nb
}
// flush the fragment list for a particular flow
func (d *IPv4Defragmenter) flush(ipf ipv4) {
d.Lock()
delete(d.ipFlows, ipf)
d.Unlock()
}
// dontDefrag returns true if the IPv4 packet do not need
// any defragmentation
func (d *IPv4Defragmenter) dontDefrag(ip *layers.IPv4) bool {
// don't defrag packet with DF flag
if ip.Flags&layers.IPv4DontFragment != 0 {
return true
}
// don't defrag not fragmented ones
if ip.Flags&layers.IPv4MoreFragments == 0 && ip.FragOffset == 0 {
return true
}
return false
}
// securityChecks performs the needed security checks
func (d *IPv4Defragmenter) securityChecks(ip *layers.IPv4) error {
fragSize := ip.Length - uint16(ip.IHL)*4
// don't allow small fragments outside of specification
if fragSize < IPv4MinimumFragmentSize {
return fmt.Errorf("defrag: fragment too small "+
"(handcrafted? %d < %d)", fragSize, IPv4MinimumFragmentSize)
}
// don't allow too big fragment offset
if ip.FragOffset > IPv4MaximumFragmentOffset {
return fmt.Errorf("defrag: fragment offset too big "+
"(handcrafted? %d > %d)", ip.FragOffset, IPv4MaximumFragmentOffset)
}
fragOffset := ip.FragOffset * 8
// don't allow fragment that would oversize an IP packet
if fragOffset+ip.Length > IPv4MaximumSize {
return fmt.Errorf("defrag: fragment will overrun "+
"(handcrafted? %d > %d)", fragOffset+ip.Length, IPv4MaximumSize)
}
return nil
}
// fragmentList holds a container/list used to contains IP
// packets/fragments. It stores internal counters to track the
// maximum total of byte, and the current length it has received.
// It also stores a flag to know if he has seen the last packet.
type fragmentList struct {
List list.List
Highest uint16
Current uint16
FinalReceived bool
LastSeen time.Time
}
// insert insert an IPv4 fragment/packet into the Fragment List
// It use the following strategy : we are inserting fragment based
// on their offset, latest first. This is sometimes called BSD-Right.
// See: http://www.sans.org/reading-room/whitepapers/detection/ip-fragment-reassembly-scapy-33969
func (f *fragmentList) insert(in *layers.IPv4, t time.Time) (*layers.IPv4, error) {
// TODO: should keep a copy of *in in the list
// or not (ie the packet source is reliable) ? -> depends on Lazy / last packet
fragOffset := in.FragOffset * 8
if fragOffset >= f.Highest {
f.List.PushBack(in)
} else {
for e := f.List.Front(); e != nil; e = e.Next() {
frag, _ := e.Value.(*layers.IPv4)
if in.FragOffset == frag.FragOffset {
// TODO: what if we receive a fragment
// that begins with duplicate data but
// *also* has new data? For example:
//
// AAAA
// BB
// BBCC
// DDDD
//
// In this situation we completely
// ignore CC and the complete packet can
// never be reassembled.
debug.Printf("defrag: ignoring frag %d as we already have it (duplicate?)\n",
fragOffset)
return nil, nil
}
if in.FragOffset < frag.FragOffset {
debug.Printf("defrag: inserting frag %d before existing frag %d\n",
fragOffset, frag.FragOffset*8)
f.List.InsertBefore(in, e)
break
}
}
}
f.LastSeen = t
fragLength := in.Length - 20
// After inserting the Fragment, we update the counters
if f.Highest < fragOffset+fragLength {
f.Highest = fragOffset + fragLength
}
f.Current = f.Current + fragLength
debug.Printf("defrag: insert ListLen: %d Highest:%d Current:%d\n",
f.List.Len(),
f.Highest, f.Current)
// Final Fragment ?
if in.Flags&layers.IPv4MoreFragments == 0 {
f.FinalReceived = true
}
// Ready to try defrag ?
if f.FinalReceived && f.Highest == f.Current {
return f.build(in)
}
return nil, nil
}
// Build builds the final datagram, modifying ip in place.
// It puts priority to packet in the early position of the list.
// See Insert for more details.
func (f *fragmentList) build(in *layers.IPv4) (*layers.IPv4, error) {
var final []byte
var currentOffset uint16
debug.Printf("defrag: building the datagram \n")
for e := f.List.Front(); e != nil; e = e.Next() {
frag, _ := e.Value.(*layers.IPv4)
if frag.FragOffset*8 == currentOffset {
debug.Printf("defrag: building - adding %d\n", frag.FragOffset*8)
final = append(final, frag.Payload...)
currentOffset = currentOffset + frag.Length - 20
} else if frag.FragOffset*8 < currentOffset {
// overlapping fragment - let's take only what we need
startAt := currentOffset - frag.FragOffset*8
debug.Printf("defrag: building - overlapping, starting at %d\n",
startAt)
if startAt > frag.Length-20 {
return nil, errors.New("defrag: building - invalid fragment")
}
final = append(final, frag.Payload[startAt:]...)
currentOffset = currentOffset + frag.FragOffset*8
} else {
// Houston - we have an hole !
debug.Printf("defrag: hole found while building, " +
"stopping the defrag process\n")
return nil, errors.New("defrag: building - hole found")
}
debug.Printf("defrag: building - next is %d\n", currentOffset)
}
// TODO recompute IP Checksum
out := &layers.IPv4{
Version: in.Version,
IHL: in.IHL,
TOS: in.TOS,
Length: f.Highest,
Id: in.Id,
Flags: 0,
FragOffset: 0,
TTL: in.TTL,
Protocol: in.Protocol,
Checksum: 0,
SrcIP: in.SrcIP,
DstIP: in.DstIP,
Options: in.Options,
Padding: in.Padding,
}
out.Payload = final
return out, nil
}
// ipv4 is a struct to be used as a key.
type ipv4 struct {
ip4 gopacket.Flow
id uint16
}
// newIPv4 returns a new initialized IPv4 Flow
func newIPv4(ip *layers.IPv4) ipv4 {
return ipv4{
ip4: ip.NetworkFlow(),
id: ip.Id,
}
}
// IPv4Defragmenter is a struct which embedded a map of
// all fragment/packet.
type IPv4Defragmenter struct {
sync.RWMutex
ipFlows map[ipv4]*fragmentList
}
// NewIPv4Defragmenter returns a new IPv4Defragmenter
// with an initialized map.
func NewIPv4Defragmenter() *IPv4Defragmenter {
return &IPv4Defragmenter{
ipFlows: make(map[ipv4]*fragmentList),
}
}