github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/tcpip/transport/tcpconntrack/tcp_conntrack.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 tcpconntrack implements a TCP connection tracking object. It allows 16 // users with access to a segment stream to figure out when a connection is 17 // established, reset, and closed (and in the last case, who closed first). 18 package tcpconntrack 19 20 import ( 21 "github.com/SagerNet/gvisor/pkg/tcpip/header" 22 "github.com/SagerNet/gvisor/pkg/tcpip/seqnum" 23 ) 24 25 // Result is returned when the state of a TCB is updated in response to an 26 // inbound or outbound segment. 27 type Result int 28 29 const ( 30 // ResultDrop indicates that the segment should be dropped. 31 ResultDrop Result = iota 32 33 // ResultConnecting indicates that the connection remains in a 34 // connecting state. 35 ResultConnecting 36 37 // ResultAlive indicates that the connection remains alive (connected). 38 ResultAlive 39 40 // ResultReset indicates that the connection was reset. 41 ResultReset 42 43 // ResultClosedByPeer indicates that the connection was gracefully 44 // closed, and the inbound stream was closed first. 45 ResultClosedByPeer 46 47 // ResultClosedBySelf indicates that the connection was gracefully 48 // closed, and the outbound stream was closed first. 49 ResultClosedBySelf 50 ) 51 52 // TCB is a TCP Control Block. It holds state necessary to keep track of a TCP 53 // connection and inform the caller when the connection has been closed. 54 type TCB struct { 55 inbound stream 56 outbound stream 57 58 // State handlers. 59 handlerInbound func(*TCB, header.TCP) Result 60 handlerOutbound func(*TCB, header.TCP) Result 61 62 // firstFin holds a pointer to the first stream to send a FIN. 63 firstFin *stream 64 65 // state is the current state of the stream. 66 state Result 67 } 68 69 // Init initializes the state of the TCB according to the initial SYN. 70 func (t *TCB) Init(initialSyn header.TCP) Result { 71 t.handlerInbound = synSentStateInbound 72 t.handlerOutbound = synSentStateOutbound 73 74 iss := seqnum.Value(initialSyn.SequenceNumber()) 75 t.outbound.una = iss 76 t.outbound.nxt = iss.Add(logicalLen(initialSyn)) 77 t.outbound.end = t.outbound.nxt 78 79 // Even though "end" is a sequence number, we don't know the initial 80 // receive sequence number yet, so we store the window size until we get 81 // a SYN from the peer. 82 t.inbound.una = 0 83 t.inbound.nxt = 0 84 t.inbound.end = seqnum.Value(initialSyn.WindowSize()) 85 t.state = ResultConnecting 86 return t.state 87 } 88 89 // UpdateStateInbound updates the state of the TCB based on the supplied inbound 90 // segment. 91 func (t *TCB) UpdateStateInbound(tcp header.TCP) Result { 92 st := t.handlerInbound(t, tcp) 93 if st != ResultDrop { 94 t.state = st 95 } 96 return st 97 } 98 99 // UpdateStateOutbound updates the state of the TCB based on the supplied 100 // outbound segment. 101 func (t *TCB) UpdateStateOutbound(tcp header.TCP) Result { 102 st := t.handlerOutbound(t, tcp) 103 if st != ResultDrop { 104 t.state = st 105 } 106 return st 107 } 108 109 // State returns the current state of the TCB. 110 func (t *TCB) State() Result { 111 return t.state 112 } 113 114 // IsAlive returns true as long as the connection is established(Alive) 115 // or connecting state. 116 func (t *TCB) IsAlive() bool { 117 return !t.inbound.rstSeen && !t.outbound.rstSeen && (!t.inbound.closed() || !t.outbound.closed()) 118 } 119 120 // OutboundSendSequenceNumber returns the snd.NXT for the outbound stream. 121 func (t *TCB) OutboundSendSequenceNumber() seqnum.Value { 122 return t.outbound.nxt 123 } 124 125 // InboundSendSequenceNumber returns the snd.NXT for the inbound stream. 126 func (t *TCB) InboundSendSequenceNumber() seqnum.Value { 127 return t.inbound.nxt 128 } 129 130 // adapResult modifies the supplied "Result" according to the state of the TCB; 131 // if r is anything other than "Alive", or if one of the streams isn't closed 132 // yet, it is returned unmodified. Otherwise it's converted to either 133 // ClosedBySelf or ClosedByPeer depending on which stream was closed first. 134 func (t *TCB) adaptResult(r Result) Result { 135 // Check the unmodified case. 136 if r != ResultAlive || !t.inbound.closed() || !t.outbound.closed() { 137 return r 138 } 139 140 // Find out which was closed first. 141 if t.firstFin == &t.outbound { 142 return ResultClosedBySelf 143 } 144 145 return ResultClosedByPeer 146 } 147 148 // synSentStateInbound is the state handler for inbound segments when the 149 // connection is in SYN-SENT state. 150 func synSentStateInbound(t *TCB, tcp header.TCP) Result { 151 flags := tcp.Flags() 152 ackPresent := flags&header.TCPFlagAck != 0 153 ack := seqnum.Value(tcp.AckNumber()) 154 155 // Ignore segment if ack is present but not acceptable. 156 if ackPresent && !(ack-1).InRange(t.outbound.una, t.outbound.nxt) { 157 return ResultConnecting 158 } 159 160 // If reset is specified, we will let the packet through no matter what 161 // but we will also destroy the connection if the ACK is present (and 162 // implicitly acceptable). 163 if flags&header.TCPFlagRst != 0 { 164 if ackPresent { 165 t.inbound.rstSeen = true 166 return ResultReset 167 } 168 return ResultConnecting 169 } 170 171 // Ignore segment if SYN is not set. 172 if flags&header.TCPFlagSyn == 0 { 173 return ResultConnecting 174 } 175 176 // Update state informed by this SYN. 177 irs := seqnum.Value(tcp.SequenceNumber()) 178 t.inbound.una = irs 179 t.inbound.nxt = irs.Add(logicalLen(tcp)) 180 t.inbound.end += irs 181 182 t.outbound.end = t.outbound.una.Add(seqnum.Size(tcp.WindowSize())) 183 184 // If the ACK was set (it is acceptable), update our unacknowledgement 185 // tracking. 186 if ackPresent { 187 // Advance the "una" and "end" indices of the outbound stream. 188 if t.outbound.una.LessThan(ack) { 189 t.outbound.una = ack 190 } 191 192 if end := ack.Add(seqnum.Size(tcp.WindowSize())); t.outbound.end.LessThan(end) { 193 t.outbound.end = end 194 } 195 } 196 197 // Update handlers so that new calls will be handled by new state. 198 t.handlerInbound = allOtherInbound 199 t.handlerOutbound = allOtherOutbound 200 201 return ResultAlive 202 } 203 204 // synSentStateOutbound is the state handler for outbound segments when the 205 // connection is in SYN-SENT state. 206 func synSentStateOutbound(t *TCB, tcp header.TCP) Result { 207 // Drop outbound segments that aren't retransmits of the original one. 208 if tcp.Flags() != header.TCPFlagSyn || 209 tcp.SequenceNumber() != uint32(t.outbound.una) { 210 return ResultDrop 211 } 212 213 // Update the receive window. We only remember the largest value seen. 214 if wnd := seqnum.Value(tcp.WindowSize()); wnd > t.inbound.end { 215 t.inbound.end = wnd 216 } 217 218 return ResultConnecting 219 } 220 221 // update updates the state of inbound and outbound streams, given the supplied 222 // inbound segment. For outbound segments, this same function can be called with 223 // swapped inbound/outbound streams. 224 func update(tcp header.TCP, inbound, outbound *stream, firstFin **stream) Result { 225 // Ignore segments out of the window. 226 s := seqnum.Value(tcp.SequenceNumber()) 227 if !inbound.acceptable(s, dataLen(tcp)) { 228 return ResultAlive 229 } 230 231 flags := tcp.Flags() 232 if flags&header.TCPFlagRst != 0 { 233 inbound.rstSeen = true 234 return ResultReset 235 } 236 237 // Ignore segments that don't have the ACK flag, and those with the SYN 238 // flag. 239 if flags&header.TCPFlagAck == 0 || flags&header.TCPFlagSyn != 0 { 240 return ResultAlive 241 } 242 243 // Ignore segments that acknowledge not yet sent data. 244 ack := seqnum.Value(tcp.AckNumber()) 245 if outbound.nxt.LessThan(ack) { 246 return ResultAlive 247 } 248 249 // Advance the "una" and "end" indices of the outbound stream. 250 if outbound.una.LessThan(ack) { 251 outbound.una = ack 252 } 253 254 if end := ack.Add(seqnum.Size(tcp.WindowSize())); outbound.end.LessThan(end) { 255 outbound.end = end 256 } 257 258 // Advance the "nxt" index of the inbound stream. 259 end := s.Add(logicalLen(tcp)) 260 if inbound.nxt.LessThan(end) { 261 inbound.nxt = end 262 } 263 264 // Note the index of the FIN segment. And stash away a pointer to the 265 // first stream to see a FIN. 266 if flags&header.TCPFlagFin != 0 && !inbound.finSeen { 267 inbound.finSeen = true 268 inbound.fin = end - 1 269 270 if *firstFin == nil { 271 *firstFin = inbound 272 } 273 } 274 275 return ResultAlive 276 } 277 278 // allOtherInbound is the state handler for inbound segments in all states 279 // except SYN-SENT. 280 func allOtherInbound(t *TCB, tcp header.TCP) Result { 281 return t.adaptResult(update(tcp, &t.inbound, &t.outbound, &t.firstFin)) 282 } 283 284 // allOtherOutbound is the state handler for outbound segments in all states 285 // except SYN-SENT. 286 func allOtherOutbound(t *TCB, tcp header.TCP) Result { 287 return t.adaptResult(update(tcp, &t.outbound, &t.inbound, &t.firstFin)) 288 } 289 290 // streams holds the state of a TCP unidirectional stream. 291 type stream struct { 292 // The interval [una, end) is the allowed interval as defined by the 293 // receiver, i.e., anything less than una has already been acknowledged 294 // and anything greater than or equal to end is beyond the receiver 295 // window. The interval [una, nxt) is the acknowledgable range, whose 296 // right edge indicates the sequence number of the next byte to be sent 297 // by the sender, i.e., anything greater than or equal to nxt hasn't 298 // been sent yet. 299 una seqnum.Value 300 nxt seqnum.Value 301 end seqnum.Value 302 303 // finSeen indicates if a FIN has already been sent on this stream. 304 finSeen bool 305 306 // fin is the sequence number of the FIN. It is only valid after finSeen 307 // is set to true. 308 fin seqnum.Value 309 310 // rstSeen indicates if a RST has already been sent on this stream. 311 rstSeen bool 312 } 313 314 // acceptable determines if the segment with the given sequence number and data 315 // length is acceptable, i.e., if it's within the [una, end) window or, in case 316 // the window is zero, if it's a packet with no payload and sequence number 317 // equal to una. 318 func (s *stream) acceptable(segSeq seqnum.Value, segLen seqnum.Size) bool { 319 return header.Acceptable(segSeq, segLen, s.una, s.end) 320 } 321 322 // closed determines if the stream has already been closed. This happens when 323 // a FIN has been set by the sender and acknowledged by the receiver. 324 func (s *stream) closed() bool { 325 return s.finSeen && s.fin.LessThan(s.una) 326 } 327 328 // dataLen returns the length of the TCP segment payload. 329 func dataLen(tcp header.TCP) seqnum.Size { 330 return seqnum.Size(len(tcp) - int(tcp.DataOffset())) 331 } 332 333 // logicalLen calculates the logical length of the TCP segment. 334 func logicalLen(tcp header.TCP) seqnum.Size { 335 l := dataLen(tcp) 336 flags := tcp.Flags() 337 if flags&header.TCPFlagSyn != 0 { 338 l++ 339 } 340 if flags&header.TCPFlagFin != 0 { 341 l++ 342 } 343 return l 344 } 345 346 // IsEmpty returns true if tcb is not initialized. 347 func (t *TCB) IsEmpty() bool { 348 if t.inbound != (stream{}) || t.outbound != (stream{}) { 349 return false 350 } 351 352 if t.firstFin != nil || t.state != ResultDrop { 353 return false 354 } 355 356 return true 357 }