blob: 3c416360906990619658931df4658ab58b274c16 [file] [log] [blame]
// Copyright 2016 The Netstack 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 fragmentation contains the implementation of IP fragmentation.
// It is based on RFC 791 and RFC 815.
package fragmentation
import (
"log"
"sync"
"time"
"github.com/google/netstack/tcpip/buffer"
)
// DefaultReassembleTimeout is based on the reassembling timeout suggest in RFC 791 (4.25 minutes).
const DefaultReassembleTimeout = 5 * time.Minute
// MemoryLimit is a suggested value for the limit on the memory used to reassemble packets.
const MemoryLimit = 8 * 1024 * 1024 // 8MB
// Fragmentation is the main structure that other modules
// of the stack should use to implement IP Fragmentation.
type Fragmentation struct {
mu sync.Mutex
limit int
reassemblers map[uint32]*reassembler
rList reassemblerList
size int
timeout time.Duration
}
// NewFragmentation creates a new Fragmentation.
//
// memoryLimit specifies the limit on the memory consumed
// by the fragments stored by Fragmentation (overhead of internal data-structures
// is not accounted). Fragments are dropped when the limit is reached.
//
// reassemblingTimeout specifes the maximum time allowed to reassemble a packet.
// Fragments are lazily evicted only when a new a packet with an
// already existing fragmentation-id arrives after the timeout.
func NewFragmentation(memoryLimit int, reassemblingTimeout time.Duration) Fragmentation {
return Fragmentation{
reassemblers: make(map[uint32]*reassembler),
limit: memoryLimit,
timeout: reassemblingTimeout,
}
}
// Process processes an incoming fragment beloning to an ID
// and returns a complete packet when all the packets belonging to that ID have been received.
func (f *Fragmentation) Process(id uint32, first, last uint16, more bool, vv *buffer.VectorisedView) (buffer.VectorisedView, bool) {
f.mu.Lock()
r, ok := f.reassemblers[id]
if ok && r.tooOld(f.timeout) {
// This is very likely to be an id-collision or someone performing a slow-rate attack.
f.release(r)
ok = false
}
if !ok {
r = newReassembler(id)
f.reassemblers[id] = r
f.rList.PushFront(r)
}
f.mu.Unlock()
res, done, consumed := r.process(first, last, more, vv)
f.mu.Lock()
f.size += consumed
if done {
f.release(r)
}
// Evict reassemblers if we are consuming more memory than the limit.
for f.size > f.limit {
f.release(f.rList.Back())
}
f.mu.Unlock()
return res, done
}
func (f *Fragmentation) release(r *reassembler) {
// Before releasing a fragment we need to check if r is already marked as done.
// Otherwise, we would delete it twice.
if r.checkDoneOrMark() {
return
}
delete(f.reassemblers, r.id)
f.rList.Remove(r)
f.size -= r.size
if f.size < 0 {
log.Printf("memory counter < 0 (%d), this is an accounting bug that requires investigation", f.size)
f.size = 0
}
}