166 lines
4.8 KiB
Go
166 lines
4.8 KiB
Go
// Copyright 2018 The gVisor Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package fragmentation
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
|
)
|
|
|
|
// vv is a helper to build VectorisedView from different strings.
|
|
func vv(size int, pieces ...string) buffer.VectorisedView {
|
|
views := make([]buffer.View, len(pieces))
|
|
for i, p := range pieces {
|
|
views[i] = []byte(p)
|
|
}
|
|
|
|
return buffer.NewVectorisedView(size, views)
|
|
}
|
|
|
|
type processInput struct {
|
|
id uint32
|
|
first uint16
|
|
last uint16
|
|
more bool
|
|
vv buffer.VectorisedView
|
|
}
|
|
|
|
type processOutput struct {
|
|
vv buffer.VectorisedView
|
|
done bool
|
|
}
|
|
|
|
var processTestCases = []struct {
|
|
comment string
|
|
in []processInput
|
|
out []processOutput
|
|
}{
|
|
{
|
|
comment: "One ID",
|
|
in: []processInput{
|
|
{id: 0, first: 0, last: 1, more: true, vv: vv(2, "01")},
|
|
{id: 0, first: 2, last: 3, more: false, vv: vv(2, "23")},
|
|
},
|
|
out: []processOutput{
|
|
{vv: buffer.VectorisedView{}, done: false},
|
|
{vv: vv(4, "01", "23"), done: true},
|
|
},
|
|
},
|
|
{
|
|
comment: "Two IDs",
|
|
in: []processInput{
|
|
{id: 0, first: 0, last: 1, more: true, vv: vv(2, "01")},
|
|
{id: 1, first: 0, last: 1, more: true, vv: vv(2, "ab")},
|
|
{id: 1, first: 2, last: 3, more: false, vv: vv(2, "cd")},
|
|
{id: 0, first: 2, last: 3, more: false, vv: vv(2, "23")},
|
|
},
|
|
out: []processOutput{
|
|
{vv: buffer.VectorisedView{}, done: false},
|
|
{vv: buffer.VectorisedView{}, done: false},
|
|
{vv: vv(4, "ab", "cd"), done: true},
|
|
{vv: vv(4, "01", "23"), done: true},
|
|
},
|
|
},
|
|
}
|
|
|
|
func TestFragmentationProcess(t *testing.T) {
|
|
for _, c := range processTestCases {
|
|
t.Run(c.comment, func(t *testing.T) {
|
|
f := NewFragmentation(1024, 512, DefaultReassembleTimeout)
|
|
for i, in := range c.in {
|
|
vv, done, err := f.Process(in.id, in.first, in.last, in.more, in.vv)
|
|
if err != nil {
|
|
t.Fatalf("f.Process(%+v, %+d, %+d, %t, %+v) failed: %v", in.id, in.first, in.last, in.more, in.vv, err)
|
|
}
|
|
if !reflect.DeepEqual(vv, c.out[i].vv) {
|
|
t.Errorf("got Process(%d) = %+v, want = %+v", i, vv, c.out[i].vv)
|
|
}
|
|
if done != c.out[i].done {
|
|
t.Errorf("got Process(%d) = %+v, want = %+v", i, done, c.out[i].done)
|
|
}
|
|
if c.out[i].done {
|
|
if _, ok := f.reassemblers[in.id]; ok {
|
|
t.Errorf("Process(%d) did not remove buffer from reassemblers", i)
|
|
}
|
|
for n := f.rList.Front(); n != nil; n = n.Next() {
|
|
if n.id == in.id {
|
|
t.Errorf("Process(%d) did not remove buffer from rList", i)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReassemblingTimeout(t *testing.T) {
|
|
timeout := time.Millisecond
|
|
f := NewFragmentation(1024, 512, timeout)
|
|
// Send first fragment with id = 0, first = 0, last = 0, and more = true.
|
|
f.Process(0, 0, 0, true, vv(1, "0"))
|
|
// Sleep more than the timeout.
|
|
time.Sleep(2 * timeout)
|
|
// Send another fragment that completes a packet.
|
|
// However, no packet should be reassembled because the fragment arrived after the timeout.
|
|
_, done, err := f.Process(0, 1, 1, false, vv(1, "1"))
|
|
if err != nil {
|
|
t.Fatalf("f.Process(0, 1, 1, false, vv(1, \"1\")) failed: %v", err)
|
|
}
|
|
if done {
|
|
t.Errorf("Fragmentation does not respect the reassembling timeout.")
|
|
}
|
|
}
|
|
|
|
func TestMemoryLimits(t *testing.T) {
|
|
f := NewFragmentation(3, 1, DefaultReassembleTimeout)
|
|
// Send first fragment with id = 0.
|
|
f.Process(0, 0, 0, true, vv(1, "0"))
|
|
// Send first fragment with id = 1.
|
|
f.Process(1, 0, 0, true, vv(1, "1"))
|
|
// Send first fragment with id = 2.
|
|
f.Process(2, 0, 0, true, vv(1, "2"))
|
|
|
|
// Send first fragment with id = 3. This should caused id = 0 and id = 1 to be
|
|
// evicted.
|
|
f.Process(3, 0, 0, true, vv(1, "3"))
|
|
|
|
if _, ok := f.reassemblers[0]; ok {
|
|
t.Errorf("Memory limits are not respected: id=0 has not been evicted.")
|
|
}
|
|
if _, ok := f.reassemblers[1]; ok {
|
|
t.Errorf("Memory limits are not respected: id=1 has not been evicted.")
|
|
}
|
|
if _, ok := f.reassemblers[3]; !ok {
|
|
t.Errorf("Implementation of memory limits is wrong: id=3 is not present.")
|
|
}
|
|
}
|
|
|
|
func TestMemoryLimitsIgnoresDuplicates(t *testing.T) {
|
|
f := NewFragmentation(1, 0, DefaultReassembleTimeout)
|
|
// Send first fragment with id = 0.
|
|
f.Process(0, 0, 0, true, vv(1, "0"))
|
|
// Send the same packet again.
|
|
f.Process(0, 0, 0, true, vv(1, "0"))
|
|
|
|
got := f.size
|
|
want := 1
|
|
if got != want {
|
|
t.Errorf("Wrong size, duplicates are not handled correctly: got=%d, want=%d.", got, want)
|
|
}
|
|
}
|