github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/packetimpact/tests/ipv6_fragment_reassembly_test.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package ipv6_fragment_reassembly_test
    16  
    17  import (
    18  	"flag"
    19  	"math/rand"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	"github.com/SagerNet/gvisor/pkg/tcpip"
    25  	"github.com/SagerNet/gvisor/pkg/tcpip/header"
    26  	"github.com/SagerNet/gvisor/test/packetimpact/testbench"
    27  )
    28  
    29  func init() {
    30  	testbench.Initialize(flag.CommandLine)
    31  }
    32  
    33  type fragmentInfo struct {
    34  	offset uint16
    35  	size   uint16
    36  	more   bool
    37  	id     uint32
    38  }
    39  
    40  func TestIPv6FragmentReassembly(t *testing.T) {
    41  	icmpv6ProtoNum := header.IPv6ExtensionHeaderIdentifier(header.ICMPv6ProtocolNumber)
    42  
    43  	tests := []struct {
    44  		description  string
    45  		ipPayloadLen int
    46  		fragments    []fragmentInfo
    47  		expectReply  bool
    48  	}{
    49  		{
    50  			description:  "basic reassembly",
    51  			ipPayloadLen: 3000,
    52  			fragments: []fragmentInfo{
    53  				{offset: 0, size: 1000, id: 100, more: true},
    54  				{offset: 1000, size: 1000, id: 100, more: true},
    55  				{offset: 2000, size: 1000, id: 100, more: false},
    56  			},
    57  			expectReply: true,
    58  		},
    59  		{
    60  			description:  "out of order fragments",
    61  			ipPayloadLen: 3000,
    62  			fragments: []fragmentInfo{
    63  				{offset: 0, size: 1000, id: 101, more: true},
    64  				{offset: 2000, size: 1000, id: 101, more: false},
    65  				{offset: 1000, size: 1000, id: 101, more: true},
    66  			},
    67  			expectReply: true,
    68  		},
    69  		{
    70  			description:  "duplicated fragments",
    71  			ipPayloadLen: 3000,
    72  			fragments: []fragmentInfo{
    73  				{offset: 0, size: 1000, id: 102, more: true},
    74  				{offset: 1000, size: 1000, id: 102, more: true},
    75  				{offset: 1000, size: 1000, id: 102, more: true},
    76  				{offset: 2000, size: 1000, id: 102, more: false},
    77  			},
    78  			expectReply: true,
    79  		},
    80  		{
    81  			description:  "fragment subset",
    82  			ipPayloadLen: 3000,
    83  			fragments: []fragmentInfo{
    84  				{offset: 0, size: 1000, id: 103, more: true},
    85  				{offset: 1000, size: 1000, id: 103, more: true},
    86  				{offset: 512, size: 256, id: 103, more: true},
    87  				{offset: 2000, size: 1000, id: 103, more: false},
    88  			},
    89  			expectReply: true,
    90  		},
    91  		{
    92  			description:  "fragment overlap",
    93  			ipPayloadLen: 3000,
    94  			fragments: []fragmentInfo{
    95  				{offset: 0, size: 1000, id: 104, more: true},
    96  				{offset: 1512, size: 1000, id: 104, more: true},
    97  				{offset: 1000, size: 1000, id: 104, more: true},
    98  				{offset: 2000, size: 1000, id: 104, more: false},
    99  			},
   100  			expectReply: false,
   101  		},
   102  	}
   103  
   104  	for _, test := range tests {
   105  		t.Run(test.description, func(t *testing.T) {
   106  			dut := testbench.NewDUT(t)
   107  			conn := dut.Net.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{})
   108  			defer conn.Close(t)
   109  
   110  			lIP := tcpip.Address(dut.Net.LocalIPv6)
   111  			rIP := tcpip.Address(dut.Net.RemoteIPv6)
   112  
   113  			data := make([]byte, test.ipPayloadLen)
   114  			icmp := header.ICMPv6(data[:header.ICMPv6HeaderSize])
   115  			icmp.SetType(header.ICMPv6EchoRequest)
   116  			icmp.SetCode(header.ICMPv6UnusedCode)
   117  			icmp.SetChecksum(0)
   118  			originalPayload := data[header.ICMPv6HeaderSize:]
   119  			if _, err := rand.Read(originalPayload); err != nil {
   120  				t.Fatalf("rand.Read: %s", err)
   121  			}
   122  
   123  			cksum := header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
   124  				Header:      icmp,
   125  				Src:         lIP,
   126  				Dst:         rIP,
   127  				PayloadCsum: header.Checksum(originalPayload, 0 /* initial */),
   128  				PayloadLen:  len(originalPayload),
   129  			})
   130  			icmp.SetChecksum(cksum)
   131  
   132  			for _, fragment := range test.fragments {
   133  				conn.Send(t, testbench.IPv6{},
   134  					&testbench.IPv6FragmentExtHdr{
   135  						NextHeader:     &icmpv6ProtoNum,
   136  						FragmentOffset: testbench.Uint16(fragment.offset / header.IPv6FragmentExtHdrFragmentOffsetBytesPerUnit),
   137  						MoreFragments:  testbench.Bool(fragment.more),
   138  						Identification: testbench.Uint32(fragment.id),
   139  					},
   140  					&testbench.Payload{
   141  						Bytes: data[fragment.offset:][:fragment.size],
   142  					})
   143  			}
   144  
   145  			var bytesReceived int
   146  			reassembledPayload := make([]byte, test.ipPayloadLen)
   147  			for {
   148  				incomingFrame, err := conn.ExpectFrame(t, testbench.Layers{
   149  					&testbench.Ether{},
   150  					&testbench.IPv6{},
   151  					&testbench.IPv6FragmentExtHdr{},
   152  				}, time.Second)
   153  				if err != nil {
   154  					// Either an unexpected frame was received, or none at all.
   155  					if test.expectReply && bytesReceived < test.ipPayloadLen {
   156  						t.Fatalf("received %d bytes out of %d, then conn.ExpectFrame(_, _, time.Second) failed with %s", bytesReceived, test.ipPayloadLen, err)
   157  					}
   158  					break
   159  				}
   160  				if !test.expectReply {
   161  					t.Fatalf("unexpected reply received:\n%s", incomingFrame)
   162  				}
   163  				ipPayload, err := incomingFrame[3 /* Payload */].ToBytes()
   164  				if err != nil {
   165  					t.Fatalf("failed to parse ICMPv6 header: incomingPacket[3].ToBytes() = (_, %s)", err)
   166  				}
   167  				offset := *incomingFrame[2 /* IPv6FragmentExtHdr */].(*testbench.IPv6FragmentExtHdr).FragmentOffset
   168  				offset *= header.IPv6FragmentExtHdrFragmentOffsetBytesPerUnit
   169  				if copied := copy(reassembledPayload[offset:], ipPayload); copied != len(ipPayload) {
   170  					t.Fatalf("wrong number of bytes copied into reassembledPayload: got = %d, want = %d", copied, len(ipPayload))
   171  				}
   172  				bytesReceived += len(ipPayload)
   173  			}
   174  
   175  			if test.expectReply {
   176  				if diff := cmp.Diff(originalPayload, reassembledPayload[header.ICMPv6HeaderSize:]); diff != "" {
   177  					t.Fatalf("reassembledPayload mismatch (-want +got):\n%s", diff)
   178  				}
   179  			}
   180  		})
   181  	}
   182  }