1 // Copyright 2012 Google, Inc. All rights reserved.
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the LICENSE file in the root of the source
13 "github.com/google/gopacket"
14 "github.com/google/gopacket/layers"
18 * Check TCP packet against options (window, MSS)
21 type tcpStreamOptions struct {
27 // TCPOptionCheck contains options for the two directions
28 type TCPOptionCheck struct {
29 options [2]tcpStreamOptions
32 func (t *TCPOptionCheck) getOptions(dir TCPFlowDirection) *tcpStreamOptions {
33 if dir == TCPDirClientToServer {
39 // NewTCPOptionCheck creates default options
40 func NewTCPOptionCheck() TCPOptionCheck {
41 return TCPOptionCheck{
42 options: [2]tcpStreamOptions{
56 // Accept checks whether the packet should be accepted by checking TCP options
57 func (t *TCPOptionCheck) Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir TCPFlowDirection, acked Sequence, start *bool) error {
58 options := t.getOptions(dir)
62 for _, o := range tcp.Options {
64 if o.OptionType == 2 {
65 if len(o.OptionData) != 2 {
66 return fmt.Errorf("MSS option data length expected 2, got %d", len(o.OptionData))
68 mss = int(binary.BigEndian.Uint16(o.OptionData[:2]))
71 if o.OptionType == 3 {
72 if len(o.OptionData) != 1 {
73 return fmt.Errorf("Window scaling length expected: 1, got %d", len(o.OptionData))
75 scale = int(o.OptionData[0])
81 if acked != invalidSequence {
82 revOptions := t.getOptions(dir.Reverse())
83 length := len(tcp.Payload)
85 // Check packet is in the correct window
86 diff := acked.Difference(Sequence(tcp.Seq))
87 if diff == -1 && (length == 1 || length == 0) {
88 // This is probably a Keep-alive
89 // TODO: check byte is ok
91 return fmt.Errorf("Re-emitted packet (diff:%d,seq:%d,rev-ack:%d)", diff,
93 } else if revOptions.mss > 0 && length > revOptions.mss {
94 return fmt.Errorf("%d > mss (%d)", length, revOptions.mss)
95 } else if revOptions.receiveWindow != 0 && revOptions.scale < 0 && diff > int(revOptions.receiveWindow) {
96 return fmt.Errorf("%d > receiveWindow(%d)", diff, revOptions.receiveWindow)
100 // Compute receiveWindow
101 options.receiveWindow = uint(tcp.Window)
102 if options.scale > 0 {
103 options.receiveWindow = options.receiveWindow << (uint(options.scale))
108 // TCPSimpleFSM implements a very simple TCP state machine
111 // When implementing a Stream interface and to avoid to consider packets that
112 // would be rejected due to client/server's TCP stack, the Accept() can call
113 // TCPSimpleFSM.CheckState().
116 // - packet should be received in-order.
117 // - no check on sequence number is performed
119 type TCPSimpleFSM struct {
122 options TCPSimpleFSMOptions
125 // TCPSimpleFSMOptions holds options for TCPSimpleFSM
126 type TCPSimpleFSMOptions struct {
127 SupportMissingEstablishment bool // Allow missing SYN, SYN+ACK, ACK
130 // Internal values of state machine
134 TCPStateEstablished = 2
135 TCPStateCloseWait = 3
140 // NewTCPSimpleFSM creates a new TCPSimpleFSM
141 func NewTCPSimpleFSM(options TCPSimpleFSMOptions) *TCPSimpleFSM {
142 return &TCPSimpleFSM{
143 state: TCPStateClosed,
148 func (t *TCPSimpleFSM) String() string {
152 case TCPStateSynSent:
154 case TCPStateEstablished:
156 case TCPStateCloseWait:
158 case TCPStateLastAck:
166 // CheckState returns false if tcp is invalid wrt current state or update the state machine's state
167 func (t *TCPSimpleFSM) CheckState(tcp *layers.TCP, dir TCPFlowDirection) bool {
168 if t.state == TCPStateClosed && t.options.SupportMissingEstablishment && !(tcp.SYN && !tcp.ACK) {
169 /* try to figure out state */
171 case tcp.SYN && tcp.ACK:
172 t.state = TCPStateSynSent
173 t.dir = dir.Reverse()
174 case tcp.FIN && !tcp.ACK:
175 t.state = TCPStateEstablished
176 case tcp.FIN && tcp.ACK:
177 t.state = TCPStateCloseWait
178 t.dir = dir.Reverse()
180 t.state = TCPStateEstablished
185 /* openning connection */
187 if tcp.SYN && !tcp.ACK {
189 t.state = TCPStateSynSent
192 case TCPStateSynSent:
194 t.state = TCPStateReset
198 if tcp.SYN && tcp.ACK && dir == t.dir.Reverse() {
199 t.state = TCPStateEstablished
202 if tcp.SYN && !tcp.ACK && dir == t.dir {
207 case TCPStateEstablished:
209 t.state = TCPStateReset
214 t.state = TCPStateCloseWait
220 /* closing connection */
221 case TCPStateCloseWait:
223 t.state = TCPStateReset
227 if tcp.FIN && tcp.ACK && dir == t.dir.Reverse() {
228 t.state = TCPStateLastAck
234 case TCPStateLastAck:
236 t.state = TCPStateReset
240 if tcp.ACK && t.dir == dir {
241 t.state = TCPStateClosed