github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/packetimpact/tests/ipv4_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 ipv4_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/header"
    25  	"github.com/SagerNet/gvisor/test/packetimpact/testbench"
    26  )
    27  
    28  func init() {
    29  	testbench.Initialize(flag.CommandLine)
    30  }
    31  
    32  type fragmentInfo struct {
    33  	offset uint16
    34  	size   uint16
    35  	more   uint8
    36  	id     uint16
    37  }
    38  
    39  func TestIPv4FragmentReassembly(t *testing.T) {
    40  	icmpv4ProtoNum := uint8(header.ICMPv4ProtocolNumber)
    41  
    42  	tests := []struct {
    43  		description  string
    44  		ipPayloadLen int
    45  		fragments    []fragmentInfo
    46  		expectReply  bool
    47  	}{
    48  		{
    49  			description:  "basic reassembly",
    50  			ipPayloadLen: 3000,
    51  			fragments: []fragmentInfo{
    52  				{offset: 0, size: 1000, id: 5, more: header.IPv4FlagMoreFragments},
    53  				{offset: 1000, size: 1000, id: 5, more: header.IPv4FlagMoreFragments},
    54  				{offset: 2000, size: 1000, id: 5, more: 0},
    55  			},
    56  			expectReply: true,
    57  		},
    58  		{
    59  			description:  "out of order fragments",
    60  			ipPayloadLen: 3000,
    61  			fragments: []fragmentInfo{
    62  				{offset: 2000, size: 1000, id: 6, more: 0},
    63  				{offset: 0, size: 1000, id: 6, more: header.IPv4FlagMoreFragments},
    64  				{offset: 1000, size: 1000, id: 6, more: header.IPv4FlagMoreFragments},
    65  			},
    66  			expectReply: true,
    67  		},
    68  		{
    69  			description:  "duplicated fragments",
    70  			ipPayloadLen: 3000,
    71  			fragments: []fragmentInfo{
    72  				{offset: 0, size: 1000, id: 7, more: header.IPv4FlagMoreFragments},
    73  				{offset: 1000, size: 1000, id: 7, more: header.IPv4FlagMoreFragments},
    74  				{offset: 1000, size: 1000, id: 7, more: header.IPv4FlagMoreFragments},
    75  				{offset: 2000, size: 1000, id: 7, more: 0},
    76  			},
    77  			expectReply: true,
    78  		},
    79  		{
    80  			description:  "fragment subset",
    81  			ipPayloadLen: 3000,
    82  			fragments: []fragmentInfo{
    83  				{offset: 0, size: 1000, id: 8, more: header.IPv4FlagMoreFragments},
    84  				{offset: 1000, size: 1000, id: 8, more: header.IPv4FlagMoreFragments},
    85  				{offset: 512, size: 256, id: 8, more: header.IPv4FlagMoreFragments},
    86  				{offset: 2000, size: 1000, id: 8, more: 0},
    87  			},
    88  			expectReply: true,
    89  		},
    90  		{
    91  			description:  "fragment overlap",
    92  			ipPayloadLen: 3000,
    93  			fragments: []fragmentInfo{
    94  				{offset: 0, size: 1000, id: 9, more: header.IPv4FlagMoreFragments},
    95  				{offset: 1512, size: 1000, id: 9, more: header.IPv4FlagMoreFragments},
    96  				{offset: 1000, size: 1000, id: 9, more: header.IPv4FlagMoreFragments},
    97  				{offset: 2000, size: 1000, id: 9, more: 0},
    98  			},
    99  			expectReply: false,
   100  		},
   101  	}
   102  
   103  	for _, test := range tests {
   104  		t.Run(test.description, func(t *testing.T) {
   105  			dut := testbench.NewDUT(t)
   106  			conn := dut.Net.NewIPv4Conn(t, testbench.IPv4{}, testbench.IPv4{})
   107  			defer conn.Close(t)
   108  
   109  			data := make([]byte, test.ipPayloadLen)
   110  			icmp := header.ICMPv4(data[:header.ICMPv4MinimumSize])
   111  			icmp.SetType(header.ICMPv4Echo)
   112  			icmp.SetCode(header.ICMPv4UnusedCode)
   113  			icmp.SetChecksum(0)
   114  			icmp.SetSequence(0)
   115  			icmp.SetIdent(0)
   116  			originalPayload := data[header.ICMPv4MinimumSize:]
   117  			if _, err := rand.Read(originalPayload); err != nil {
   118  				t.Fatalf("rand.Read: %s", err)
   119  			}
   120  			cksum := header.ICMPv4Checksum(icmp, header.Checksum(originalPayload, 0 /* initial */))
   121  			icmp.SetChecksum(cksum)
   122  
   123  			for _, fragment := range test.fragments {
   124  				conn.Send(t,
   125  					testbench.IPv4{
   126  						Protocol:       &icmpv4ProtoNum,
   127  						FragmentOffset: testbench.Uint16(fragment.offset),
   128  						Flags:          testbench.Uint8(fragment.more),
   129  						ID:             testbench.Uint16(fragment.id),
   130  					},
   131  					&testbench.Payload{
   132  						Bytes: data[fragment.offset:][:fragment.size],
   133  					})
   134  			}
   135  
   136  			var bytesReceived int
   137  			reassembledPayload := make([]byte, test.ipPayloadLen)
   138  			// We are sending a packet fragmented into smaller parts but the
   139  			// response may also be large enough to require fragmentation.
   140  			// Therefore we only look for payload for an IPv4 packet not ICMP.
   141  			for {
   142  				incomingFrame, err := conn.ExpectFrame(t, testbench.Layers{
   143  					&testbench.Ether{},
   144  					&testbench.IPv4{},
   145  				}, time.Second)
   146  				if err != nil {
   147  					// Either an unexpected frame was received, or none at all.
   148  					if test.expectReply && bytesReceived < test.ipPayloadLen {
   149  						t.Fatalf("received %d bytes out of %d, then conn.ExpectFrame(_, _, time.Second) failed with %s", bytesReceived, test.ipPayloadLen, err)
   150  					}
   151  					break
   152  				}
   153  				if !test.expectReply {
   154  					t.Fatalf("unexpected reply received:\n%s", incomingFrame)
   155  				}
   156  				// We only asked for Ethernet and IPv4 so the rest should be payload.
   157  				ipPayload, err := incomingFrame[2 /* Payload */].ToBytes()
   158  				if err != nil {
   159  					t.Fatalf("failed to parse payload: incomingPacket[2].ToBytes() = (_, %s)", err)
   160  				}
   161  				offset := *incomingFrame[1 /* IPv4 */].(*testbench.IPv4).FragmentOffset
   162  				if copied := copy(reassembledPayload[offset:], ipPayload); copied != len(ipPayload) {
   163  					t.Fatalf("wrong number of bytes copied into reassembledPayload: got = %d, want = %d", copied, len(ipPayload))
   164  				}
   165  				bytesReceived += len(ipPayload)
   166  			}
   167  
   168  			if test.expectReply {
   169  				if diff := cmp.Diff(originalPayload, reassembledPayload[header.ICMPv4MinimumSize:]); diff != "" {
   170  					t.Fatalf("reassembledPayload mismatch (-want +got):\n%s", diff)
   171  				}
   172  			}
   173  		})
   174  	}
   175  }