added support for string type
[govpp.git] / vendor / github.com / google / gopacket / reassembly / tcpcheck.go
1 // Copyright 2012 Google, Inc. All rights reserved.
2 //
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
5 // tree.
6
7 package reassembly
8
9 import (
10         "encoding/binary"
11         "fmt"
12
13         "github.com/google/gopacket"
14         "github.com/google/gopacket/layers"
15 )
16
17 /*
18  * Check TCP packet against options (window, MSS)
19  */
20
21 type tcpStreamOptions struct {
22         mss           int
23         scale         int
24         receiveWindow uint
25 }
26
27 // TCPOptionCheck contains options for the two directions
28 type TCPOptionCheck struct {
29         options [2]tcpStreamOptions
30 }
31
32 func (t *TCPOptionCheck) getOptions(dir TCPFlowDirection) *tcpStreamOptions {
33         if dir == TCPDirClientToServer {
34                 return &t.options[0]
35         }
36         return &t.options[1]
37 }
38
39 // NewTCPOptionCheck creates default options
40 func NewTCPOptionCheck() TCPOptionCheck {
41         return TCPOptionCheck{
42                 options: [2]tcpStreamOptions{
43                         tcpStreamOptions{
44                                 mss:           0,
45                                 scale:         -1,
46                                 receiveWindow: 0,
47                         }, tcpStreamOptions{
48                                 mss:           0,
49                                 scale:         -1,
50                                 receiveWindow: 0,
51                         },
52                 },
53         }
54 }
55
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)
59         if tcp.SYN {
60                 mss := -1
61                 scale := -1
62                 for _, o := range tcp.Options {
63                         // MSS
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))
67                                 }
68                                 mss = int(binary.BigEndian.Uint16(o.OptionData[:2]))
69                         }
70                         // Window scaling
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))
74                                 }
75                                 scale = int(o.OptionData[0])
76                         }
77                 }
78                 options.mss = mss
79                 options.scale = scale
80         } else {
81                 if acked != invalidSequence {
82                         revOptions := t.getOptions(dir.Reverse())
83                         length := len(tcp.Payload)
84
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
90                         } else if diff < 0 {
91                                 return fmt.Errorf("Re-emitted packet (diff:%d,seq:%d,rev-ack:%d)", diff,
92                                         tcp.Seq, acked)
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)
97                         }
98                 }
99         }
100         // Compute receiveWindow
101         options.receiveWindow = uint(tcp.Window)
102         if options.scale > 0 {
103                 options.receiveWindow = options.receiveWindow << (uint(options.scale))
104         }
105         return nil
106 }
107
108 // TCPSimpleFSM implements a very simple TCP state machine
109 //
110 // Usage:
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().
114 //
115 // Limitations:
116 // - packet should be received in-order.
117 // - no check on sequence number is performed
118 // - no RST
119 type TCPSimpleFSM struct {
120         dir     TCPFlowDirection
121         state   int
122         options TCPSimpleFSMOptions
123 }
124
125 // TCPSimpleFSMOptions holds options for TCPSimpleFSM
126 type TCPSimpleFSMOptions struct {
127         SupportMissingEstablishment bool // Allow missing SYN, SYN+ACK, ACK
128 }
129
130 // Internal values of state machine
131 const (
132         TCPStateClosed      = 0
133         TCPStateSynSent     = 1
134         TCPStateEstablished = 2
135         TCPStateCloseWait   = 3
136         TCPStateLastAck     = 4
137         TCPStateReset       = 5
138 )
139
140 // NewTCPSimpleFSM creates a new TCPSimpleFSM
141 func NewTCPSimpleFSM(options TCPSimpleFSMOptions) *TCPSimpleFSM {
142         return &TCPSimpleFSM{
143                 state:   TCPStateClosed,
144                 options: options,
145         }
146 }
147
148 func (t *TCPSimpleFSM) String() string {
149         switch t.state {
150         case TCPStateClosed:
151                 return "Closed"
152         case TCPStateSynSent:
153                 return "SynSent"
154         case TCPStateEstablished:
155                 return "Established"
156         case TCPStateCloseWait:
157                 return "CloseWait"
158         case TCPStateLastAck:
159                 return "LastAck"
160         case TCPStateReset:
161                 return "Reset"
162         }
163         return "?"
164 }
165
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 */
170                 switch true {
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()
179                 default:
180                         t.state = TCPStateEstablished
181                 }
182         }
183
184         switch t.state {
185         /* openning connection */
186         case TCPStateClosed:
187                 if tcp.SYN && !tcp.ACK {
188                         t.dir = dir
189                         t.state = TCPStateSynSent
190                         return true
191                 }
192         case TCPStateSynSent:
193                 if tcp.RST {
194                         t.state = TCPStateReset
195                         return true
196                 }
197
198                 if tcp.SYN && tcp.ACK && dir == t.dir.Reverse() {
199                         t.state = TCPStateEstablished
200                         return true
201                 }
202                 if tcp.SYN && !tcp.ACK && dir == t.dir {
203                         // re-transmission
204                         return true
205                 }
206         /* established */
207         case TCPStateEstablished:
208                 if tcp.RST {
209                         t.state = TCPStateReset
210                         return true
211                 }
212
213                 if tcp.FIN {
214                         t.state = TCPStateCloseWait
215                         t.dir = dir
216                         return true
217                 }
218                 // accept any packet
219                 return true
220         /* closing connection */
221         case TCPStateCloseWait:
222                 if tcp.RST {
223                         t.state = TCPStateReset
224                         return true
225                 }
226
227                 if tcp.FIN && tcp.ACK && dir == t.dir.Reverse() {
228                         t.state = TCPStateLastAck
229                         return true
230                 }
231                 if tcp.ACK {
232                         return true
233                 }
234         case TCPStateLastAck:
235                 if tcp.RST {
236                         t.state = TCPStateReset
237                         return true
238                 }
239
240                 if tcp.ACK && t.dir == dir {
241                         t.state = TCPStateClosed
242                         return true
243                 }
244         }
245         return false
246 }