mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Test IPv6 fragment reassembly
A packetimpact test for: "A node must be able to accept a fragmented packet that, after reassembly, is as large as 1500 octets." PiperOrigin-RevId: 321210729
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
package testbench
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"reflect"
|
||||
@@ -470,21 +471,11 @@ func (l *IPv6) ToBytes() ([]byte, error) {
|
||||
if l.NextHeader != nil {
|
||||
fields.NextHeader = *l.NextHeader
|
||||
} else {
|
||||
switch n := l.next().(type) {
|
||||
case *TCP:
|
||||
fields.NextHeader = uint8(header.TCPProtocolNumber)
|
||||
case *UDP:
|
||||
fields.NextHeader = uint8(header.UDPProtocolNumber)
|
||||
case *ICMPv6:
|
||||
fields.NextHeader = uint8(header.ICMPv6ProtocolNumber)
|
||||
case *IPv6HopByHopOptionsExtHdr:
|
||||
fields.NextHeader = uint8(header.IPv6HopByHopOptionsExtHdrIdentifier)
|
||||
case *IPv6DestinationOptionsExtHdr:
|
||||
fields.NextHeader = uint8(header.IPv6DestinationOptionsExtHdrIdentifier)
|
||||
default:
|
||||
// TODO(b/150301488): Support more protocols as needed.
|
||||
return nil, fmt.Errorf("ToBytes can't deduce the IPv6 header's next protocol: %#v", n)
|
||||
nh, err := nextHeaderByLayer(l.next())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields.NextHeader = nh
|
||||
}
|
||||
if l.HopLimit != nil {
|
||||
fields.HopLimit = *l.HopLimit
|
||||
@@ -514,6 +505,8 @@ func nextIPv6PayloadParser(nextHeader uint8) layerParser {
|
||||
return parseIPv6HopByHopOptionsExtHdr
|
||||
case header.IPv6DestinationOptionsExtHdrIdentifier:
|
||||
return parseIPv6DestinationOptionsExtHdr
|
||||
case header.IPv6FragmentExtHdrIdentifier:
|
||||
return parseIPv6FragmentExtHdr
|
||||
}
|
||||
return parsePayload
|
||||
}
|
||||
@@ -566,14 +559,56 @@ type IPv6DestinationOptionsExtHdr struct {
|
||||
Options []byte
|
||||
}
|
||||
|
||||
// IPv6FragmentExtHdr can construct and match an IPv6 Fragment Extension Header.
|
||||
type IPv6FragmentExtHdr struct {
|
||||
LayerBase
|
||||
NextHeader *header.IPv6ExtensionHeaderIdentifier
|
||||
FragmentOffset *uint16
|
||||
MoreFragments *bool
|
||||
Identification *uint32
|
||||
}
|
||||
|
||||
// nextHeaderByLayer finds the correct next header protocol value for layer l.
|
||||
func nextHeaderByLayer(l Layer) (uint8, error) {
|
||||
if l == nil {
|
||||
return uint8(header.IPv6NoNextHeaderIdentifier), nil
|
||||
}
|
||||
switch l.(type) {
|
||||
case *TCP:
|
||||
return uint8(header.TCPProtocolNumber), nil
|
||||
case *UDP:
|
||||
return uint8(header.UDPProtocolNumber), nil
|
||||
case *ICMPv6:
|
||||
return uint8(header.ICMPv6ProtocolNumber), nil
|
||||
case *Payload:
|
||||
return uint8(header.IPv6NoNextHeaderIdentifier), nil
|
||||
case *IPv6HopByHopOptionsExtHdr:
|
||||
return uint8(header.IPv6HopByHopOptionsExtHdrIdentifier), nil
|
||||
case *IPv6DestinationOptionsExtHdr:
|
||||
return uint8(header.IPv6DestinationOptionsExtHdrIdentifier), nil
|
||||
case *IPv6FragmentExtHdr:
|
||||
return uint8(header.IPv6FragmentExtHdrIdentifier), nil
|
||||
default:
|
||||
// TODO(b/161005083): Support more protocols as needed.
|
||||
return 0, fmt.Errorf("failed to deduce the IPv6 header's next protocol: %T", l)
|
||||
}
|
||||
}
|
||||
|
||||
// ipv6OptionsExtHdrToBytes serializes an options extension header into bytes.
|
||||
func ipv6OptionsExtHdrToBytes(nextHeader *header.IPv6ExtensionHeaderIdentifier, options []byte) []byte {
|
||||
func ipv6OptionsExtHdrToBytes(nextHeader *header.IPv6ExtensionHeaderIdentifier, nextLayer Layer, options []byte) ([]byte, error) {
|
||||
length := len(options) + 2
|
||||
if length%8 != 0 {
|
||||
return nil, fmt.Errorf("IPv6 extension headers must be a multiple of 8 octets long, but the length given: %d, options: %s", length, hex.Dump(options))
|
||||
}
|
||||
bytes := make([]byte, length)
|
||||
if nextHeader == nil {
|
||||
bytes[0] = byte(header.IPv6NoNextHeaderIdentifier)
|
||||
} else {
|
||||
if nextHeader != nil {
|
||||
bytes[0] = byte(*nextHeader)
|
||||
} else {
|
||||
nh, err := nextHeaderByLayer(nextLayer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bytes[0] = nh
|
||||
}
|
||||
// ExtHdrLen field is the length of the extension header
|
||||
// in 8-octet unit, ignoring the first 8 octets.
|
||||
@@ -581,7 +616,7 @@ func ipv6OptionsExtHdrToBytes(nextHeader *header.IPv6ExtensionHeaderIdentifier,
|
||||
// https://tools.ietf.org/html/rfc2460#section-4.6
|
||||
bytes[1] = uint8((length - 8) / 8)
|
||||
copy(bytes[2:], options)
|
||||
return bytes
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// IPv6ExtHdrIdent is a helper routine that allocates a new
|
||||
@@ -591,14 +626,45 @@ func IPv6ExtHdrIdent(id header.IPv6ExtensionHeaderIdentifier) *header.IPv6Extens
|
||||
return &id
|
||||
}
|
||||
|
||||
// ToBytes implements Layer.ToBytes
|
||||
// ToBytes implements Layer.ToBytes.
|
||||
func (l *IPv6HopByHopOptionsExtHdr) ToBytes() ([]byte, error) {
|
||||
return ipv6OptionsExtHdrToBytes(l.NextHeader, l.Options), nil
|
||||
return ipv6OptionsExtHdrToBytes(l.NextHeader, l.next(), l.Options)
|
||||
}
|
||||
|
||||
// ToBytes implements Layer.ToBytes
|
||||
// ToBytes implements Layer.ToBytes.
|
||||
func (l *IPv6DestinationOptionsExtHdr) ToBytes() ([]byte, error) {
|
||||
return ipv6OptionsExtHdrToBytes(l.NextHeader, l.Options), nil
|
||||
return ipv6OptionsExtHdrToBytes(l.NextHeader, l.next(), l.Options)
|
||||
}
|
||||
|
||||
// ToBytes implements Layer.ToBytes.
|
||||
func (l *IPv6FragmentExtHdr) ToBytes() ([]byte, error) {
|
||||
var offset, mflag uint16
|
||||
var ident uint32
|
||||
bytes := make([]byte, header.IPv6FragmentExtHdrLength)
|
||||
if l.NextHeader != nil {
|
||||
bytes[0] = byte(*l.NextHeader)
|
||||
} else {
|
||||
nh, err := nextHeaderByLayer(l.next())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bytes[0] = nh
|
||||
}
|
||||
bytes[1] = 0 // reserved
|
||||
if l.MoreFragments != nil && *l.MoreFragments {
|
||||
mflag = 1
|
||||
}
|
||||
if l.FragmentOffset != nil {
|
||||
offset = *l.FragmentOffset
|
||||
}
|
||||
if l.Identification != nil {
|
||||
ident = *l.Identification
|
||||
}
|
||||
offsetAndMflag := offset<<3 | mflag
|
||||
binary.BigEndian.PutUint16(bytes[2:], offsetAndMflag)
|
||||
binary.BigEndian.PutUint32(bytes[4:], ident)
|
||||
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// parseIPv6ExtHdr parses an IPv6 extension header and returns the NextHeader
|
||||
@@ -631,6 +697,26 @@ func parseIPv6DestinationOptionsExtHdr(b []byte) (Layer, layerParser) {
|
||||
return &IPv6DestinationOptionsExtHdr{NextHeader: &nextHeader, Options: options}, nextParser
|
||||
}
|
||||
|
||||
// Bool is a helper routine that allocates a new
|
||||
// bool value to store v and returns a pointer to it.
|
||||
func Bool(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
// parseIPv6FragmentExtHdr parses the bytes assuming that they start
|
||||
// with an IPv6 Fragment Extension Header.
|
||||
func parseIPv6FragmentExtHdr(b []byte) (Layer, layerParser) {
|
||||
nextHeader := b[0]
|
||||
var extHdr header.IPv6FragmentExtHdr
|
||||
copy(extHdr[:], b[2:])
|
||||
return &IPv6FragmentExtHdr{
|
||||
NextHeader: IPv6ExtHdrIdent(header.IPv6ExtensionHeaderIdentifier(nextHeader)),
|
||||
FragmentOffset: Uint16(extHdr.FragmentOffset()),
|
||||
MoreFragments: Bool(extHdr.More()),
|
||||
Identification: Uint32(extHdr.ID()),
|
||||
}, nextIPv6PayloadParser(nextHeader)
|
||||
}
|
||||
|
||||
func (l *IPv6HopByHopOptionsExtHdr) length() int {
|
||||
return len(l.Options) + 2
|
||||
}
|
||||
@@ -667,13 +753,31 @@ func (l *IPv6DestinationOptionsExtHdr) String() string {
|
||||
return stringLayer(l)
|
||||
}
|
||||
|
||||
func (*IPv6FragmentExtHdr) length() int {
|
||||
return header.IPv6FragmentExtHdrLength
|
||||
}
|
||||
|
||||
func (l *IPv6FragmentExtHdr) match(other Layer) bool {
|
||||
return equalLayer(l, other)
|
||||
}
|
||||
|
||||
// merge overrides the values in l with the values from other but only in fields
|
||||
// where the value is not nil.
|
||||
func (l *IPv6FragmentExtHdr) merge(other Layer) error {
|
||||
return mergeLayer(l, other)
|
||||
}
|
||||
|
||||
func (l *IPv6FragmentExtHdr) String() string {
|
||||
return stringLayer(l)
|
||||
}
|
||||
|
||||
// ICMPv6 can construct and match an ICMPv6 encapsulation.
|
||||
type ICMPv6 struct {
|
||||
LayerBase
|
||||
Type *header.ICMPv6Type
|
||||
Code *byte
|
||||
Checksum *uint16
|
||||
NDPPayload []byte
|
||||
Type *header.ICMPv6Type
|
||||
Code *byte
|
||||
Checksum *uint16
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
func (l *ICMPv6) String() string {
|
||||
@@ -684,7 +788,7 @@ func (l *ICMPv6) String() string {
|
||||
|
||||
// ToBytes implements Layer.ToBytes.
|
||||
func (l *ICMPv6) ToBytes() ([]byte, error) {
|
||||
b := make([]byte, header.ICMPv6HeaderSize+len(l.NDPPayload))
|
||||
b := make([]byte, header.ICMPv6HeaderSize+len(l.Payload))
|
||||
h := header.ICMPv6(b)
|
||||
if l.Type != nil {
|
||||
h.SetType(*l.Type)
|
||||
@@ -692,7 +796,7 @@ func (l *ICMPv6) ToBytes() ([]byte, error) {
|
||||
if l.Code != nil {
|
||||
h.SetCode(*l.Code)
|
||||
}
|
||||
copy(h.NDPPayload(), l.NDPPayload)
|
||||
copy(h.NDPPayload(), l.Payload)
|
||||
if l.Checksum != nil {
|
||||
h.SetChecksum(*l.Checksum)
|
||||
} else {
|
||||
@@ -725,10 +829,10 @@ func Byte(v byte) *byte {
|
||||
func parseICMPv6(b []byte) (Layer, layerParser) {
|
||||
h := header.ICMPv6(b)
|
||||
icmpv6 := ICMPv6{
|
||||
Type: ICMPv6Type(h.Type()),
|
||||
Code: Byte(h.Code()),
|
||||
Checksum: Uint16(h.Checksum()),
|
||||
NDPPayload: h.NDPPayload(),
|
||||
Type: ICMPv6Type(h.Type()),
|
||||
Code: Byte(h.Code()),
|
||||
Checksum: Uint16(h.Checksum()),
|
||||
Payload: h.NDPPayload(),
|
||||
}
|
||||
return &icmpv6, nil
|
||||
}
|
||||
@@ -738,7 +842,7 @@ func (l *ICMPv6) match(other Layer) bool {
|
||||
}
|
||||
|
||||
func (l *ICMPv6) length() int {
|
||||
return header.ICMPv6HeaderSize + len(l.NDPPayload)
|
||||
return header.ICMPv6HeaderSize + len(l.Payload)
|
||||
}
|
||||
|
||||
// merge overrides the values in l with the values from other but only in fields
|
||||
|
||||
@@ -593,10 +593,107 @@ func TestIPv6ExtHdrOptions(t *testing.T) {
|
||||
Options: []byte{0x05, 0x02, 0x00, 0x00, 0x01, 0x00},
|
||||
},
|
||||
&ICMPv6{
|
||||
Type: ICMPv6Type(header.ICMPv6ParamProblem),
|
||||
Code: Byte(0),
|
||||
Checksum: Uint16(0x5f98),
|
||||
NDPPayload: []byte{0x00, 0x00, 0x00, 0x06},
|
||||
Type: ICMPv6Type(header.ICMPv6ParamProblem),
|
||||
Code: Byte(0),
|
||||
Checksum: Uint16(0x5f98),
|
||||
Payload: []byte{0x00, 0x00, 0x00, 0x06},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "IPv6/HopByHop/Fragment",
|
||||
wantBytes: []byte{
|
||||
// IPv6 Header
|
||||
0x60, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x40, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
|
||||
// HopByHop Options
|
||||
0x2c, 0x00, 0x05, 0x02, 0x00, 0x00, 0x01, 0x00,
|
||||
// Fragment ExtHdr
|
||||
0x3b, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x2a,
|
||||
},
|
||||
wantLayers: []Layer{
|
||||
&IPv6{
|
||||
SrcAddr: Address(tcpip.Address(net.ParseIP("::1"))),
|
||||
DstAddr: Address(tcpip.Address(net.ParseIP("fe80::dead:beef"))),
|
||||
},
|
||||
&IPv6HopByHopOptionsExtHdr{
|
||||
NextHeader: IPv6ExtHdrIdent(header.IPv6FragmentExtHdrIdentifier),
|
||||
Options: []byte{0x05, 0x02, 0x00, 0x00, 0x01, 0x00},
|
||||
},
|
||||
&IPv6FragmentExtHdr{
|
||||
NextHeader: IPv6ExtHdrIdent(header.IPv6NoNextHeaderIdentifier),
|
||||
FragmentOffset: Uint16(100),
|
||||
MoreFragments: Bool(false),
|
||||
Identification: Uint32(42),
|
||||
},
|
||||
&Payload{
|
||||
Bytes: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "IPv6/DestOpt/Fragment/Payload",
|
||||
wantBytes: []byte{
|
||||
// IPv6 Header
|
||||
0x60, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x3c, 0x40, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
|
||||
// Destination Options
|
||||
0x2c, 0x00, 0x05, 0x02, 0x00, 0x00, 0x01, 0x00,
|
||||
// Fragment ExtHdr
|
||||
0x3b, 0x00, 0x03, 0x21, 0x00, 0x00, 0x00, 0x2a,
|
||||
// Sample Data
|
||||
0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61,
|
||||
},
|
||||
wantLayers: []Layer{
|
||||
&IPv6{
|
||||
SrcAddr: Address(tcpip.Address(net.ParseIP("::1"))),
|
||||
DstAddr: Address(tcpip.Address(net.ParseIP("fe80::dead:beef"))),
|
||||
},
|
||||
&IPv6DestinationOptionsExtHdr{
|
||||
NextHeader: IPv6ExtHdrIdent(header.IPv6FragmentExtHdrIdentifier),
|
||||
Options: []byte{0x05, 0x02, 0x00, 0x00, 0x01, 0x00},
|
||||
},
|
||||
&IPv6FragmentExtHdr{
|
||||
NextHeader: IPv6ExtHdrIdent(header.IPv6NoNextHeaderIdentifier),
|
||||
FragmentOffset: Uint16(100),
|
||||
MoreFragments: Bool(true),
|
||||
Identification: Uint32(42),
|
||||
},
|
||||
&Payload{
|
||||
Bytes: []byte("Sample Data"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "IPv6/Fragment/Payload",
|
||||
wantBytes: []byte{
|
||||
// IPv6 Header
|
||||
0x60, 0x00, 0x00, 0x00, 0x00, 0x13, 0x2c, 0x40, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
|
||||
// Fragment ExtHdr
|
||||
0x3b, 0x00, 0x03, 0x21, 0x00, 0x00, 0x00, 0x2a,
|
||||
// Sample Data
|
||||
0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61,
|
||||
},
|
||||
wantLayers: []Layer{
|
||||
&IPv6{
|
||||
SrcAddr: Address(tcpip.Address(net.ParseIP("::1"))),
|
||||
DstAddr: Address(tcpip.Address(net.ParseIP("fe80::dead:beef"))),
|
||||
},
|
||||
&IPv6FragmentExtHdr{
|
||||
NextHeader: IPv6ExtHdrIdent(header.IPv6NoNextHeaderIdentifier),
|
||||
FragmentOffset: Uint16(100),
|
||||
MoreFragments: Bool(true),
|
||||
Identification: Uint32(42),
|
||||
},
|
||||
&Payload{
|
||||
Bytes: []byte("Sample Data"),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -606,6 +703,19 @@ func TestIPv6ExtHdrOptions(t *testing.T) {
|
||||
if !layers.match(tt.wantLayers) {
|
||||
t.Fatalf("match failed with diff: %s", layers.diff(tt.wantLayers))
|
||||
}
|
||||
// Make sure we can generate correct next header values and checksums
|
||||
for _, layer := range layers {
|
||||
switch layer := layer.(type) {
|
||||
case *IPv6HopByHopOptionsExtHdr:
|
||||
layer.NextHeader = nil
|
||||
case *IPv6DestinationOptionsExtHdr:
|
||||
layer.NextHeader = nil
|
||||
case *IPv6FragmentExtHdr:
|
||||
layer.NextHeader = nil
|
||||
case *ICMPv6:
|
||||
layer.Checksum = nil
|
||||
}
|
||||
}
|
||||
gotBytes, err := layers.ToBytes()
|
||||
if err != nil {
|
||||
t.Fatalf("ToBytes() failed on %s: %s", &layers, err)
|
||||
|
||||
@@ -254,6 +254,20 @@ packetimpact_go_test(
|
||||
],
|
||||
)
|
||||
|
||||
packetimpact_go_test(
|
||||
name = "ipv6_fragment_reassembly",
|
||||
srcs = ["ipv6_fragment_reassembly_test.go"],
|
||||
# TODO(b/160919104): Fix netstack then remove the line below.
|
||||
expect_netstack_failure = True,
|
||||
deps = [
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/buffer",
|
||||
"//pkg/tcpip/header",
|
||||
"//test/packetimpact/testbench",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
packetimpact_go_test(
|
||||
name = "udp_send_recv_dgram",
|
||||
srcs = ["udp_send_recv_dgram_test.go"],
|
||||
|
||||
@@ -41,8 +41,8 @@ func TestICMPv6ParamProblemTest(t *testing.T) {
|
||||
NextHeader: testbench.Uint8(254),
|
||||
}
|
||||
icmpv6 := testbench.ICMPv6{
|
||||
Type: testbench.ICMPv6Type(header.ICMPv6EchoRequest),
|
||||
NDPPayload: []byte("hello world"),
|
||||
Type: testbench.ICMPv6Type(header.ICMPv6EchoRequest),
|
||||
Payload: []byte("hello world"),
|
||||
}
|
||||
|
||||
toSend := (*testbench.Connection)(&conn).CreateFrame(testbench.Layers{&ipv6}, &icmpv6)
|
||||
@@ -62,8 +62,8 @@ func TestICMPv6ParamProblemTest(t *testing.T) {
|
||||
binary.BigEndian.PutUint32(b, header.IPv6NextHeaderOffset)
|
||||
expectedPayload = append(b, expectedPayload...)
|
||||
expectedICMPv6 := testbench.ICMPv6{
|
||||
Type: testbench.ICMPv6Type(header.ICMPv6ParamProblem),
|
||||
NDPPayload: expectedPayload,
|
||||
Type: testbench.ICMPv6Type(header.ICMPv6ParamProblem),
|
||||
Payload: expectedPayload,
|
||||
}
|
||||
|
||||
paramProblem := testbench.Layers{
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
// 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 ipv6_fragment_reassembly_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
const (
|
||||
// The payload length for the first fragment we send. This number
|
||||
// is a multiple of 8 near 750 (half of 1500).
|
||||
firstPayloadLength = 752
|
||||
// The ID field for our outgoing fragments.
|
||||
fragmentID = 1
|
||||
// A node must be able to accept a fragmented packet that,
|
||||
// after reassembly, is as large as 1500 octets.
|
||||
reassemblyCap = 1500
|
||||
)
|
||||
|
||||
func init() {
|
||||
testbench.RegisterFlags(flag.CommandLine)
|
||||
}
|
||||
|
||||
func TestIPv6FragmentReassembly(t *testing.T) {
|
||||
dut := testbench.NewDUT(t)
|
||||
defer dut.TearDown()
|
||||
conn := testbench.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{})
|
||||
defer conn.Close()
|
||||
|
||||
firstPayloadToSend := make([]byte, firstPayloadLength)
|
||||
for i := range firstPayloadToSend {
|
||||
firstPayloadToSend[i] = 'A'
|
||||
}
|
||||
|
||||
secondPayloadLength := reassemblyCap - firstPayloadLength - header.ICMPv6EchoMinimumSize
|
||||
secondPayloadToSend := firstPayloadToSend[:secondPayloadLength]
|
||||
|
||||
icmpv6EchoPayload := make([]byte, 4)
|
||||
binary.BigEndian.PutUint16(icmpv6EchoPayload[0:], 0)
|
||||
binary.BigEndian.PutUint16(icmpv6EchoPayload[2:], 0)
|
||||
icmpv6EchoPayload = append(icmpv6EchoPayload, firstPayloadToSend...)
|
||||
|
||||
lIP := tcpip.Address(net.ParseIP(testbench.LocalIPv6).To16())
|
||||
rIP := tcpip.Address(net.ParseIP(testbench.RemoteIPv6).To16())
|
||||
icmpv6 := testbench.ICMPv6{
|
||||
Type: testbench.ICMPv6Type(header.ICMPv6EchoRequest),
|
||||
Code: testbench.Byte(0),
|
||||
Payload: icmpv6EchoPayload,
|
||||
}
|
||||
icmpv6Bytes, err := icmpv6.ToBytes()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to serialize ICMPv6: %s", err)
|
||||
}
|
||||
cksum := header.ICMPv6Checksum(
|
||||
header.ICMPv6(icmpv6Bytes),
|
||||
lIP,
|
||||
rIP,
|
||||
buffer.NewVectorisedView(len(secondPayloadToSend), []buffer.View{secondPayloadToSend}),
|
||||
)
|
||||
|
||||
conn.Send(testbench.IPv6{},
|
||||
&testbench.IPv6FragmentExtHdr{
|
||||
FragmentOffset: testbench.Uint16(0),
|
||||
MoreFragments: testbench.Bool(true),
|
||||
Identification: testbench.Uint32(fragmentID),
|
||||
},
|
||||
&testbench.ICMPv6{
|
||||
Type: testbench.ICMPv6Type(header.ICMPv6EchoRequest),
|
||||
Code: testbench.Byte(0),
|
||||
Payload: icmpv6EchoPayload,
|
||||
Checksum: &cksum,
|
||||
})
|
||||
|
||||
icmpv6ProtoNum := header.IPv6ExtensionHeaderIdentifier(header.ICMPv6ProtocolNumber)
|
||||
|
||||
conn.Send(testbench.IPv6{},
|
||||
&testbench.IPv6FragmentExtHdr{
|
||||
NextHeader: &icmpv6ProtoNum,
|
||||
FragmentOffset: testbench.Uint16((firstPayloadLength + header.ICMPv6EchoMinimumSize) / 8),
|
||||
MoreFragments: testbench.Bool(false),
|
||||
Identification: testbench.Uint32(fragmentID),
|
||||
},
|
||||
&testbench.Payload{
|
||||
Bytes: secondPayloadToSend,
|
||||
})
|
||||
|
||||
gotEchoReplyFirstPart, err := conn.ExpectFrame(testbench.Layers{
|
||||
&testbench.Ether{},
|
||||
&testbench.IPv6{},
|
||||
&testbench.IPv6FragmentExtHdr{
|
||||
FragmentOffset: testbench.Uint16(0),
|
||||
MoreFragments: testbench.Bool(true),
|
||||
},
|
||||
&testbench.ICMPv6{
|
||||
Type: testbench.ICMPv6Type(header.ICMPv6EchoReply),
|
||||
Code: testbench.Byte(0),
|
||||
},
|
||||
}, time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("expected a fragmented ICMPv6 Echo Reply, but got none: %s", err)
|
||||
}
|
||||
|
||||
id := *gotEchoReplyFirstPart[2].(*testbench.IPv6FragmentExtHdr).Identification
|
||||
gotFirstPayload, err := gotEchoReplyFirstPart[len(gotEchoReplyFirstPart)-1].ToBytes()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to serialize ICMPv6: %s", err)
|
||||
}
|
||||
icmpPayload := gotFirstPayload[header.ICMPv6EchoMinimumSize:]
|
||||
receivedLen := len(icmpPayload)
|
||||
wantSecondPayloadLen := reassemblyCap - header.ICMPv6EchoMinimumSize - receivedLen
|
||||
wantFirstPayload := make([]byte, receivedLen)
|
||||
for i := range wantFirstPayload {
|
||||
wantFirstPayload[i] = 'A'
|
||||
}
|
||||
wantSecondPayload := wantFirstPayload[:wantSecondPayloadLen]
|
||||
if !bytes.Equal(icmpPayload, wantFirstPayload) {
|
||||
t.Fatalf("received unexpected payload, got: %s, want: %s",
|
||||
hex.Dump(icmpPayload),
|
||||
hex.Dump(wantFirstPayload))
|
||||
}
|
||||
|
||||
gotEchoReplySecondPart, err := conn.ExpectFrame(testbench.Layers{
|
||||
&testbench.Ether{},
|
||||
&testbench.IPv6{},
|
||||
&testbench.IPv6FragmentExtHdr{
|
||||
NextHeader: &icmpv6ProtoNum,
|
||||
FragmentOffset: testbench.Uint16(uint16((receivedLen + header.ICMPv6EchoMinimumSize) / 8)),
|
||||
MoreFragments: testbench.Bool(false),
|
||||
Identification: &id,
|
||||
},
|
||||
&testbench.ICMPv6{},
|
||||
}, time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("expected the rest of ICMPv6 Echo Reply, but got none: %s", err)
|
||||
}
|
||||
secondPayload, err := gotEchoReplySecondPart[len(gotEchoReplySecondPart)-1].ToBytes()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to serialize ICMPv6 Echo Reply: %s", err)
|
||||
}
|
||||
if !bytes.Equal(secondPayload, wantSecondPayload) {
|
||||
t.Fatalf("received unexpected payload, got: %s, want: %s",
|
||||
hex.Dump(secondPayload),
|
||||
hex.Dump(wantSecondPayload))
|
||||
}
|
||||
}
|
||||
@@ -171,9 +171,9 @@ func TestIPv6UnknownOptionAction(t *testing.T) {
|
||||
&tb.Ether{},
|
||||
&tb.IPv6{},
|
||||
&tb.ICMPv6{
|
||||
Type: tb.ICMPv6Type(header.ICMPv6ParamProblem),
|
||||
Code: tb.Byte(2),
|
||||
NDPPayload: icmpv6Payload,
|
||||
Type: tb.ICMPv6Type(header.ICMPv6ParamProblem),
|
||||
Code: tb.Byte(2),
|
||||
Payload: icmpv6Payload,
|
||||
},
|
||||
}, time.Second)
|
||||
if tt.wantICMPv6 && err != nil {
|
||||
|
||||
Reference in New Issue
Block a user