github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/tcpip/transport/tcp/forwarder.go (about) 1 // Copyright 2018 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 16 17 import ( 18 "github.com/SagerNet/gvisor/pkg/sync" 19 "github.com/SagerNet/gvisor/pkg/tcpip" 20 "github.com/SagerNet/gvisor/pkg/tcpip/header" 21 "github.com/SagerNet/gvisor/pkg/tcpip/seqnum" 22 "github.com/SagerNet/gvisor/pkg/tcpip/stack" 23 "github.com/SagerNet/gvisor/pkg/waiter" 24 ) 25 26 // Forwarder is a connection request forwarder, which allows clients to decide 27 // what to do with a connection request, for example: ignore it, send a RST, or 28 // attempt to complete the 3-way handshake. 29 // 30 // The canonical way of using it is to pass the Forwarder.HandlePacket function 31 // to stack.SetTransportProtocolHandler. 32 type Forwarder struct { 33 stack *stack.Stack 34 35 maxInFlight int 36 handler func(*ForwarderRequest) 37 38 mu sync.Mutex 39 inFlight map[stack.TransportEndpointID]struct{} 40 listen *listenContext 41 } 42 43 // NewForwarder allocates and initializes a new forwarder with the given 44 // maximum number of in-flight connection attempts. Once the maximum is reached 45 // new incoming connection requests will be ignored. 46 // 47 // If rcvWnd is set to zero, the default buffer size is used instead. 48 func NewForwarder(s *stack.Stack, rcvWnd, maxInFlight int, handler func(*ForwarderRequest)) *Forwarder { 49 if rcvWnd == 0 { 50 rcvWnd = DefaultReceiveBufferSize 51 } 52 return &Forwarder{ 53 stack: s, 54 maxInFlight: maxInFlight, 55 handler: handler, 56 inFlight: make(map[stack.TransportEndpointID]struct{}), 57 listen: newListenContext(s, nil /* listenEP */, seqnum.Size(rcvWnd), true, 0), 58 } 59 } 60 61 // HandlePacket handles a packet if it is of interest to the forwarder (i.e., if 62 // it's a SYN packet), returning true if it's the case. Otherwise the packet 63 // is not handled and false is returned. 64 // 65 // This function is expected to be passed as an argument to the 66 // stack.SetTransportProtocolHandler function. 67 func (f *Forwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool { 68 s := newIncomingSegment(id, f.stack.Clock(), pkt) 69 defer s.decRef() 70 71 // We only care about well-formed SYN packets. 72 if !s.parse(pkt.RXTransportChecksumValidated) || !s.csumValid || s.flags != header.TCPFlagSyn { 73 return false 74 } 75 76 opts := parseSynSegmentOptions(s) 77 78 f.mu.Lock() 79 defer f.mu.Unlock() 80 81 // We have an inflight request for this id, ignore this one for now. 82 if _, ok := f.inFlight[id]; ok { 83 return true 84 } 85 86 // Ignore the segment if we're beyond the limit. 87 if len(f.inFlight) >= f.maxInFlight { 88 return true 89 } 90 91 // Launch a new goroutine to handle the request. 92 f.inFlight[id] = struct{}{} 93 s.incRef() 94 go f.handler(&ForwarderRequest{ // S/R-SAFE: not used by Sentry. 95 forwarder: f, 96 segment: s, 97 synOptions: opts, 98 }) 99 100 return true 101 } 102 103 // ForwarderRequest represents a connection request received by the forwarder 104 // and passed to the client. Clients must eventually call Complete() on it, and 105 // may optionally create an endpoint to represent it via CreateEndpoint. 106 type ForwarderRequest struct { 107 mu sync.Mutex 108 forwarder *Forwarder 109 segment *segment 110 synOptions header.TCPSynOptions 111 } 112 113 // ID returns the 4-tuple (src address, src port, dst address, dst port) that 114 // represents the connection request. 115 func (r *ForwarderRequest) ID() stack.TransportEndpointID { 116 return r.segment.id 117 } 118 119 // Complete completes the request, and optionally sends a RST segment back to the 120 // sender. 121 func (r *ForwarderRequest) Complete(sendReset bool) { 122 r.mu.Lock() 123 defer r.mu.Unlock() 124 125 if r.segment == nil { 126 panic("Completing already completed forwarder request") 127 } 128 129 // Remove request from the forwarder. 130 r.forwarder.mu.Lock() 131 delete(r.forwarder.inFlight, r.segment.id) 132 r.forwarder.mu.Unlock() 133 134 if sendReset { 135 replyWithReset(r.forwarder.stack, r.segment, stack.DefaultTOS, 0 /* ttl */) 136 } 137 138 // Release all resources. 139 r.segment.decRef() 140 r.segment = nil 141 r.forwarder = nil 142 } 143 144 // CreateEndpoint creates a TCP endpoint for the connection request, performing 145 // the 3-way handshake in the process. 146 func (r *ForwarderRequest) CreateEndpoint(queue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) { 147 r.mu.Lock() 148 defer r.mu.Unlock() 149 150 if r.segment == nil { 151 return nil, &tcpip.ErrInvalidEndpointState{} 152 } 153 154 f := r.forwarder 155 ep, err := f.listen.performHandshake(r.segment, &header.TCPSynOptions{ 156 MSS: r.synOptions.MSS, 157 WS: r.synOptions.WS, 158 TS: r.synOptions.TS, 159 TSVal: r.synOptions.TSVal, 160 TSEcr: r.synOptions.TSEcr, 161 SACKPermitted: r.synOptions.SACKPermitted, 162 }, queue, nil) 163 if err != nil { 164 return nil, err 165 } 166 167 // Start the protocol goroutine. Note that the endpoint is returned 168 // from performHandshake locked. 169 ep.startAcceptedLoop() // +checklocksforce 170 171 return ep, nil 172 }