gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/packetimpact/tests/tcp_connect_icmp_error_test.go (about) 1 // Copyright 2021 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 tcp_connect_icmp_error_test 16 17 import ( 18 "flag" 19 "testing" 20 "time" 21 22 "golang.org/x/sys/unix" 23 "gvisor.dev/gvisor/pkg/tcpip/header" 24 "gvisor.dev/gvisor/test/packetimpact/testbench" 25 ) 26 27 func init() { 28 testbench.Initialize(flag.CommandLine) 29 } 30 31 func sendICMPError(t *testing.T, conn *testbench.TCPIPv4, tcp *testbench.TCP) { 32 t.Helper() 33 34 icmpPayload := testbench.Layers{tcp.Prev(), tcp} 35 bytes, err := icmpPayload.ToBytes() 36 if err != nil { 37 t.Fatalf("got icmpPayload.ToBytes() = (_, %s), want = (_, nil)", err) 38 } 39 40 layers := conn.CreateFrame(t, nil) 41 layers[len(layers)-1] = &testbench.ICMPv4{ 42 Type: testbench.ICMPv4Type(header.ICMPv4DstUnreachable), 43 Code: testbench.ICMPv4Code(header.ICMPv4HostUnreachable), 44 Payload: bytes, 45 } 46 conn.SendFrameStateless(t, layers) 47 } 48 49 // TestTCPConnectICMPError tests for the handshake to fail and the socket state 50 // cleaned up on receiving an ICMP error. 51 func TestTCPConnectICMPError(t *testing.T) { 52 dut := testbench.NewDUT(t) 53 54 clientFD, clientPort := dut.CreateBoundSocket(t, unix.SOCK_STREAM|unix.SOCK_NONBLOCK, unix.IPPROTO_TCP, dut.Net.RemoteIPv4) 55 port := uint16(9001) 56 conn := dut.Net.NewTCPIPv4(t, testbench.TCP{SrcPort: &port, DstPort: &clientPort}, testbench.TCP{SrcPort: &clientPort, DstPort: &port}) 57 defer conn.Close(t) 58 sa := unix.SockaddrInet4{Port: int(port)} 59 copy(sa.Addr[:], dut.Net.LocalIPv4) 60 // Bring the dut to SYN-SENT state with a non-blocking connect. 61 dut.Connect(t, clientFD, &sa) 62 tcp, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, time.Second) 63 if err != nil { 64 t.Fatalf("expected SYN, %s", err) 65 } 66 67 // Continuously try to read the ICMP error in an attempt to trigger a race 68 // condition. 69 start := make(chan struct{}) 70 done := make(chan struct{}) 71 go func() { 72 defer close(done) 73 74 close(start) 75 for { 76 select { 77 case <-done: 78 return 79 default: 80 } 81 const want = unix.EHOSTUNREACH 82 switch got := unix.Errno(dut.GetSockOptInt(t, clientFD, unix.SOL_SOCKET, unix.SO_ERROR)); got { 83 case unix.Errno(0): 84 continue 85 case want: 86 return 87 default: 88 t.Fatalf("got SO_ERROR = %s, want %s", got, want) 89 } 90 91 } 92 }() 93 94 <-start 95 sendICMPError(t, &conn, tcp) 96 97 dut.PollOne(t, clientFD, unix.POLLHUP, time.Second) 98 <-done 99 100 conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}) 101 // The DUT should reply with RST to our ACK as the state should have 102 // transitioned to CLOSED because of handshake error. 103 if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, time.Second); err != nil { 104 t.Fatalf("expected RST, %s", err) 105 } 106 }