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