516 lines
15 KiB
Go
516 lines
15 KiB
Go
// Copyright 2020 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 header
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
|
)
|
|
|
|
// Equal returns true of a and b are equivalent.
|
|
//
|
|
// Note, Equal will return true if a and b hold the same Identifier value and
|
|
// contain the same bytes in Buf, even if the bytes are split across views
|
|
// differently.
|
|
//
|
|
// Needed to use cmp.Equal on IPv6RawPayloadHeader as it contains unexported
|
|
// fields.
|
|
func (a IPv6RawPayloadHeader) Equal(b IPv6RawPayloadHeader) bool {
|
|
return a.Identifier == b.Identifier && bytes.Equal(a.Buf.ToView(), b.Buf.ToView())
|
|
}
|
|
|
|
func TestIPv6RoutingExtHdr(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
bytes []byte
|
|
segmentsLeft uint8
|
|
}{
|
|
{
|
|
name: "Zeroes",
|
|
bytes: []byte{0, 0, 0, 0, 0, 0},
|
|
segmentsLeft: 0,
|
|
},
|
|
{
|
|
name: "Ones",
|
|
bytes: []byte{1, 1, 1, 1, 1, 1},
|
|
segmentsLeft: 1,
|
|
},
|
|
{
|
|
name: "Mixed",
|
|
bytes: []byte{1, 2, 3, 4, 5, 6},
|
|
segmentsLeft: 2,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
extHdr := IPv6RoutingExtHdr(test.bytes)
|
|
if got := extHdr.SegmentsLeft(); got != test.segmentsLeft {
|
|
t.Errorf("got SegmentsLeft() = %d, want = %d", got, test.segmentsLeft)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIPv6FragmentExtHdr(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
bytes [6]byte
|
|
fragmentOffset uint16
|
|
more bool
|
|
id uint32
|
|
}{
|
|
{
|
|
name: "Zeroes",
|
|
bytes: [6]byte{0, 0, 0, 0, 0, 0},
|
|
fragmentOffset: 0,
|
|
more: false,
|
|
id: 0,
|
|
},
|
|
{
|
|
name: "Ones",
|
|
bytes: [6]byte{0, 9, 0, 0, 0, 1},
|
|
fragmentOffset: 1,
|
|
more: true,
|
|
id: 1,
|
|
},
|
|
{
|
|
name: "Mixed",
|
|
bytes: [6]byte{68, 9, 128, 4, 2, 1},
|
|
fragmentOffset: 2177,
|
|
more: true,
|
|
id: 2147746305,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
extHdr := IPv6FragmentExtHdr(test.bytes)
|
|
if got := extHdr.FragmentOffset(); got != test.fragmentOffset {
|
|
t.Errorf("got FragmentOffset() = %d, want = %d", got, test.fragmentOffset)
|
|
}
|
|
if got := extHdr.More(); got != test.more {
|
|
t.Errorf("got More() = %t, want = %t", got, test.more)
|
|
}
|
|
if got := extHdr.ID(); got != test.id {
|
|
t.Errorf("got ID() = %d, want = %d", got, test.id)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func makeVectorisedViewFromByteBuffers(bs ...[]byte) buffer.VectorisedView {
|
|
size := 0
|
|
var vs []buffer.View
|
|
|
|
for _, b := range bs {
|
|
vs = append(vs, buffer.View(b))
|
|
size += len(b)
|
|
}
|
|
|
|
return buffer.NewVectorisedView(size, vs)
|
|
}
|
|
|
|
func TestIPv6ExtHdrIterErr(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
firstNextHdr IPv6ExtensionHeaderIdentifier
|
|
payload buffer.VectorisedView
|
|
err error
|
|
}{
|
|
{
|
|
name: "Upper layer only without data",
|
|
firstNextHdr: 255,
|
|
},
|
|
{
|
|
name: "Upper layer only with data",
|
|
firstNextHdr: 255,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{1, 2, 3, 4}),
|
|
},
|
|
|
|
{
|
|
name: "No next header",
|
|
firstNextHdr: IPv6NoNextHeaderIdentifier,
|
|
},
|
|
{
|
|
name: "No next header with data",
|
|
firstNextHdr: IPv6NoNextHeaderIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{1, 2, 3, 4}),
|
|
},
|
|
|
|
{
|
|
name: "Valid single fragment",
|
|
firstNextHdr: IPv6FragmentExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{255, 0, 68, 9, 128, 4, 2, 1}),
|
|
},
|
|
{
|
|
name: "Fragment too small",
|
|
firstNextHdr: IPv6FragmentExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{255, 0, 68, 9, 128, 4, 2}),
|
|
err: io.ErrUnexpectedEOF,
|
|
},
|
|
|
|
{
|
|
name: "Valid single routing",
|
|
firstNextHdr: IPv6RoutingExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 2, 3, 4, 5, 6}),
|
|
},
|
|
{
|
|
name: "Valid single routing across views",
|
|
firstNextHdr: IPv6RoutingExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 2}, []byte{3, 4, 5, 6}),
|
|
},
|
|
{
|
|
name: "Routing too small with zero length field",
|
|
firstNextHdr: IPv6RoutingExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 2, 3, 4, 5}),
|
|
err: io.ErrUnexpectedEOF,
|
|
},
|
|
{
|
|
name: "Valid routing with non-zero length field",
|
|
firstNextHdr: IPv6RoutingExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{255, 1, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8}),
|
|
},
|
|
{
|
|
name: "Valid routing with non-zero length field across views",
|
|
firstNextHdr: IPv6RoutingExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{255, 1, 1, 2, 3, 4, 5, 6}, []byte{1, 2, 3, 4, 5, 6, 7, 8}),
|
|
},
|
|
{
|
|
name: "Routing too small with non-zero length field",
|
|
firstNextHdr: IPv6RoutingExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{255, 1, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7}),
|
|
err: io.ErrUnexpectedEOF,
|
|
},
|
|
{
|
|
name: "Routing too small with non-zero length field across views",
|
|
firstNextHdr: IPv6RoutingExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{255, 1, 1, 2, 3, 4, 5, 6}, []byte{1, 2, 3, 4, 5, 6, 7}),
|
|
err: io.ErrUnexpectedEOF,
|
|
},
|
|
|
|
{
|
|
name: "Mixed",
|
|
firstNextHdr: IPv6FragmentExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{
|
|
// Fragment extension header.
|
|
uint8(IPv6RoutingExtHdrIdentifier), 0, 68, 9, 128, 4, 2, 1,
|
|
|
|
// Routing extension header.
|
|
255, 0, 1, 2, 3, 4, 5, 6,
|
|
|
|
// Upper layer data.
|
|
1, 2, 3, 4,
|
|
}),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if _, err := MakeIPv6PayloadIterator(test.firstNextHdr, test.payload, false); err != nil {
|
|
t.Errorf("got MakeIPv6PayloadIterator(%d, _, false) = %s, want = nil", test.firstNextHdr, err)
|
|
}
|
|
|
|
if _, err := MakeIPv6PayloadIterator(test.firstNextHdr, test.payload, true); !errors.Is(err, test.err) {
|
|
t.Errorf("got MakeIPv6PayloadIterator(%d, _, true) = %v, want = %v", test.firstNextHdr, err, test.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIPv6ExtHdrIter(t *testing.T) {
|
|
routingExtHdrWithUpperLayerData := buffer.View([]byte{255, 0, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4})
|
|
upperLayerData := buffer.View([]byte{1, 2, 3, 4})
|
|
tests := []struct {
|
|
name string
|
|
firstNextHdr IPv6ExtensionHeaderIdentifier
|
|
payload buffer.VectorisedView
|
|
expected []IPv6PayloadHeader
|
|
}{
|
|
// With a non-atomic fragment, the payload after the fragment will not be
|
|
// parsed because the payload may not be complete.
|
|
{
|
|
name: "fragment - routing - upper",
|
|
firstNextHdr: IPv6FragmentExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{
|
|
// Fragment extension header.
|
|
uint8(IPv6RoutingExtHdrIdentifier), 0, 68, 9, 128, 4, 2, 1,
|
|
|
|
// Routing extension header.
|
|
255, 0, 1, 2, 3, 4, 5, 6,
|
|
|
|
// Upper layer data.
|
|
1, 2, 3, 4,
|
|
}),
|
|
expected: []IPv6PayloadHeader{
|
|
IPv6FragmentExtHdr([6]byte{68, 9, 128, 4, 2, 1}),
|
|
IPv6RawPayloadHeader{
|
|
Identifier: IPv6RoutingExtHdrIdentifier,
|
|
Buf: routingExtHdrWithUpperLayerData.ToVectorisedView(),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "fragment - routing - upper (across views)",
|
|
firstNextHdr: IPv6FragmentExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{
|
|
// Fragment extension header.
|
|
uint8(IPv6RoutingExtHdrIdentifier), 0, 68, 9, 128, 4, 2, 1,
|
|
|
|
// Routing extension header.
|
|
255, 0, 1, 2}, []byte{3, 4, 5, 6,
|
|
|
|
// Upper layer data.
|
|
1, 2, 3, 4,
|
|
}),
|
|
expected: []IPv6PayloadHeader{
|
|
IPv6FragmentExtHdr([6]byte{68, 9, 128, 4, 2, 1}),
|
|
IPv6RawPayloadHeader{
|
|
Identifier: IPv6RoutingExtHdrIdentifier,
|
|
Buf: routingExtHdrWithUpperLayerData.ToVectorisedView(),
|
|
},
|
|
},
|
|
},
|
|
|
|
// If we have an atomic fragment, the payload following the fragment
|
|
// extension header should be parsed normally.
|
|
{
|
|
name: "atomic fragment - routing - upper",
|
|
firstNextHdr: IPv6FragmentExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{
|
|
// Fragment extension header.
|
|
//
|
|
// Reserved bits are 1 which should not affect anything.
|
|
uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6, 128, 4, 2, 1,
|
|
|
|
// Routing extension header.
|
|
255, 0, 1, 2, 3, 4, 5, 6,
|
|
|
|
// Upper layer data.
|
|
1, 2, 3, 4,
|
|
}),
|
|
expected: []IPv6PayloadHeader{
|
|
IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}),
|
|
IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}),
|
|
IPv6RawPayloadHeader{
|
|
Identifier: 255,
|
|
Buf: upperLayerData.ToVectorisedView(),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "atomic fragment - routing - upper (across views)",
|
|
firstNextHdr: IPv6FragmentExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{
|
|
// Fragment extension header.
|
|
//
|
|
// Reserved bits are 1 which should not affect anything.
|
|
uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6}, []byte{128, 4, 2, 1,
|
|
|
|
// Routing extension header.
|
|
255, 0, 1, 2}, []byte{3, 4, 5, 6,
|
|
|
|
// Upper layer data.
|
|
1, 2}, []byte{3, 4}),
|
|
expected: []IPv6PayloadHeader{
|
|
IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}),
|
|
IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}),
|
|
IPv6RawPayloadHeader{
|
|
Identifier: 255,
|
|
Buf: makeVectorisedViewFromByteBuffers(upperLayerData[:2], upperLayerData[2:]),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "atomic fragment - no next header",
|
|
firstNextHdr: IPv6FragmentExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{
|
|
// Fragment extension header.
|
|
//
|
|
// Res (Reserved) bits are 1 which should not affect anything.
|
|
uint8(IPv6NoNextHeaderIdentifier), 0, 0, 6, 128, 4, 2, 1,
|
|
|
|
// Random data.
|
|
1, 2, 3, 4,
|
|
}),
|
|
expected: []IPv6PayloadHeader{
|
|
IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}),
|
|
},
|
|
},
|
|
{
|
|
name: "routing - atomic fragment - no next header",
|
|
firstNextHdr: IPv6RoutingExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{
|
|
// Routing extension header.
|
|
uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6,
|
|
|
|
// Fragment extension header.
|
|
//
|
|
// Reserved bits are 1 which should not affect anything.
|
|
uint8(IPv6NoNextHeaderIdentifier), 0, 0, 6, 128, 4, 2, 1,
|
|
|
|
// Random data.
|
|
1, 2, 3, 4,
|
|
}),
|
|
expected: []IPv6PayloadHeader{
|
|
IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}),
|
|
IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}),
|
|
},
|
|
},
|
|
{
|
|
name: "routing - atomic fragment - no next header (across views)",
|
|
firstNextHdr: IPv6RoutingExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{
|
|
// Routing extension header.
|
|
uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6,
|
|
|
|
// Fragment extension header.
|
|
//
|
|
// Reserved bits are 1 which should not affect anything.
|
|
uint8(IPv6NoNextHeaderIdentifier), 255, 0, 6}, []byte{128, 4, 2, 1,
|
|
|
|
// Random data.
|
|
1, 2, 3, 4,
|
|
}),
|
|
expected: []IPv6PayloadHeader{
|
|
IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}),
|
|
IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}),
|
|
},
|
|
},
|
|
{
|
|
name: "routing - fragment - no next header",
|
|
firstNextHdr: IPv6RoutingExtHdrIdentifier,
|
|
payload: makeVectorisedViewFromByteBuffers([]byte{
|
|
// Routing extension header.
|
|
uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6,
|
|
|
|
// Fragment extension header.
|
|
//
|
|
// Fragment Offset = 32; Res = 6.
|
|
uint8(IPv6NoNextHeaderIdentifier), 0, 1, 6, 128, 4, 2, 1,
|
|
|
|
// Random data.
|
|
1, 2, 3, 4,
|
|
}),
|
|
expected: []IPv6PayloadHeader{
|
|
IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}),
|
|
IPv6FragmentExtHdr([6]byte{1, 6, 128, 4, 2, 1}),
|
|
IPv6RawPayloadHeader{
|
|
Identifier: IPv6NoNextHeaderIdentifier,
|
|
Buf: upperLayerData.ToVectorisedView(),
|
|
},
|
|
},
|
|
},
|
|
|
|
// Test the raw payload for common transport layer protocol numbers.
|
|
{
|
|
name: "TCP raw payload",
|
|
firstNextHdr: IPv6ExtensionHeaderIdentifier(TCPProtocolNumber),
|
|
payload: makeVectorisedViewFromByteBuffers(upperLayerData),
|
|
expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{
|
|
Identifier: IPv6ExtensionHeaderIdentifier(TCPProtocolNumber),
|
|
Buf: upperLayerData.ToVectorisedView(),
|
|
}},
|
|
},
|
|
{
|
|
name: "UDP raw payload",
|
|
firstNextHdr: IPv6ExtensionHeaderIdentifier(UDPProtocolNumber),
|
|
payload: makeVectorisedViewFromByteBuffers(upperLayerData),
|
|
expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{
|
|
Identifier: IPv6ExtensionHeaderIdentifier(UDPProtocolNumber),
|
|
Buf: upperLayerData.ToVectorisedView(),
|
|
}},
|
|
},
|
|
{
|
|
name: "ICMPv4 raw payload",
|
|
firstNextHdr: IPv6ExtensionHeaderIdentifier(ICMPv4ProtocolNumber),
|
|
payload: makeVectorisedViewFromByteBuffers(upperLayerData),
|
|
expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{
|
|
Identifier: IPv6ExtensionHeaderIdentifier(ICMPv4ProtocolNumber),
|
|
Buf: upperLayerData.ToVectorisedView(),
|
|
}},
|
|
},
|
|
{
|
|
name: "ICMPv6 raw payload",
|
|
firstNextHdr: IPv6ExtensionHeaderIdentifier(ICMPv6ProtocolNumber),
|
|
payload: makeVectorisedViewFromByteBuffers(upperLayerData),
|
|
expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{
|
|
Identifier: IPv6ExtensionHeaderIdentifier(ICMPv6ProtocolNumber),
|
|
Buf: upperLayerData.ToVectorisedView(),
|
|
}},
|
|
},
|
|
{
|
|
name: "Unknwon next header raw payload",
|
|
firstNextHdr: 255,
|
|
payload: makeVectorisedViewFromByteBuffers(upperLayerData),
|
|
expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{
|
|
Identifier: 255,
|
|
Buf: upperLayerData.ToVectorisedView(),
|
|
}},
|
|
},
|
|
{
|
|
name: "Unknwon next header raw payload (across views)",
|
|
firstNextHdr: 255,
|
|
payload: makeVectorisedViewFromByteBuffers(upperLayerData[:2], upperLayerData[2:]),
|
|
expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{
|
|
Identifier: 255,
|
|
Buf: makeVectorisedViewFromByteBuffers(upperLayerData[:2], upperLayerData[2:]),
|
|
}},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
it, err := MakeIPv6PayloadIterator(test.firstNextHdr, test.payload, true)
|
|
if err != nil {
|
|
t.Fatalf("MakeIPv6PayloadIterator(%d, _ true): %s", test.firstNextHdr, err)
|
|
}
|
|
|
|
for i, e := range test.expected {
|
|
extHdr, done, err := it.Next()
|
|
if err != nil {
|
|
t.Errorf("(i=%d) Next(): %s", i, err)
|
|
}
|
|
if done {
|
|
t.Errorf("(i=%d) unexpectedly done iterating", i)
|
|
}
|
|
if diff := cmp.Diff(e, extHdr); diff != "" {
|
|
t.Errorf("(i=%d) got ext hdr mismatch (-want +got):\n%s", i, diff)
|
|
}
|
|
|
|
if t.Failed() {
|
|
t.FailNow()
|
|
}
|
|
}
|
|
|
|
extHdr, done, err := it.Next()
|
|
if err != nil {
|
|
t.Errorf("(last) Next(): %s", err)
|
|
}
|
|
if !done {
|
|
t.Errorf("(last) iterator unexpectedly not done")
|
|
}
|
|
if extHdr != nil {
|
|
t.Errorf("(last) got Next() = %T, want = nil", extHdr)
|
|
}
|
|
})
|
|
}
|
|
}
|