aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/google/gopacket/reassembly/tcpcheck.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/google/gopacket/reassembly/tcpcheck.go')
-rw-r--r--vendor/github.com/google/gopacket/reassembly/tcpcheck.go246
1 files changed, 246 insertions, 0 deletions
diff --git a/vendor/github.com/google/gopacket/reassembly/tcpcheck.go b/vendor/github.com/google/gopacket/reassembly/tcpcheck.go
new file mode 100644
index 0000000..4b52aba
--- /dev/null
+++ b/vendor/github.com/google/gopacket/reassembly/tcpcheck.go
@@ -0,0 +1,246 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package reassembly
+
+import (
+ "encoding/binary"
+ "fmt"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+/*
+ * Check TCP packet against options (window, MSS)
+ */
+
+type tcpStreamOptions struct {
+ mss int
+ scale int
+ receiveWindow uint
+}
+
+// TCPOptionCheck contains options for the two directions
+type TCPOptionCheck struct {
+ options [2]tcpStreamOptions
+}
+
+func (t *TCPOptionCheck) getOptions(dir TCPFlowDirection) *tcpStreamOptions {
+ if dir == TCPDirClientToServer {
+ return &t.options[0]
+ }
+ return &t.options[1]
+}
+
+// NewTCPOptionCheck creates default options
+func NewTCPOptionCheck() TCPOptionCheck {
+ return TCPOptionCheck{
+ options: [2]tcpStreamOptions{
+ tcpStreamOptions{
+ mss: 0,
+ scale: -1,
+ receiveWindow: 0,
+ }, tcpStreamOptions{
+ mss: 0,
+ scale: -1,
+ receiveWindow: 0,
+ },
+ },
+ }
+}
+
+// Accept checks whether the packet should be accepted by checking TCP options
+func (t *TCPOptionCheck) Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir TCPFlowDirection, acked Sequence, start *bool) error {
+ options := t.getOptions(dir)
+ if tcp.SYN {
+ mss := -1
+ scale := -1
+ for _, o := range tcp.Options {
+ // MSS
+ if o.OptionType == 2 {
+ if len(o.OptionData) != 2 {
+ return fmt.Errorf("MSS option data length expected 2, got %d", len(o.OptionData))
+ }
+ mss = int(binary.BigEndian.Uint16(o.OptionData[:2]))
+ }
+ // Window scaling
+ if o.OptionType == 3 {
+ if len(o.OptionData) != 1 {
+ return fmt.Errorf("Window scaling length expected: 1, got %d", len(o.OptionData))
+ }
+ scale = int(o.OptionData[0])
+ }
+ }
+ options.mss = mss
+ options.scale = scale
+ } else {
+ if acked != invalidSequence {
+ revOptions := t.getOptions(dir.Reverse())
+ length := len(tcp.Payload)
+
+ // Check packet is in the correct window
+ diff := acked.Difference(Sequence(tcp.Seq))
+ if diff == -1 && (length == 1 || length == 0) {
+ // This is probably a Keep-alive
+ // TODO: check byte is ok
+ } else if diff < 0 {
+ return fmt.Errorf("Re-emitted packet (diff:%d,seq:%d,rev-ack:%d)", diff,
+ tcp.Seq, acked)
+ } else if revOptions.mss > 0 && length > revOptions.mss {
+ return fmt.Errorf("%d > mss (%d)", length, revOptions.mss)
+ } else if revOptions.receiveWindow != 0 && revOptions.scale < 0 && diff > int(revOptions.receiveWindow) {
+ return fmt.Errorf("%d > receiveWindow(%d)", diff, revOptions.receiveWindow)
+ }
+ }
+ }
+ // Compute receiveWindow
+ options.receiveWindow = uint(tcp.Window)
+ if options.scale > 0 {
+ options.receiveWindow = options.receiveWindow << (uint(options.scale))
+ }
+ return nil
+}
+
+// TCPSimpleFSM implements a very simple TCP state machine
+//
+// Usage:
+// When implementing a Stream interface and to avoid to consider packets that
+// would be rejected due to client/server's TCP stack, the Accept() can call
+// TCPSimpleFSM.CheckState().
+//
+// Limitations:
+// - packet should be received in-order.
+// - no check on sequence number is performed
+// - no RST
+type TCPSimpleFSM struct {
+ dir TCPFlowDirection
+ state int
+ options TCPSimpleFSMOptions
+}
+
+// TCPSimpleFSMOptions holds options for TCPSimpleFSM
+type TCPSimpleFSMOptions struct {
+ SupportMissingEstablishment bool // Allow missing SYN, SYN+ACK, ACK
+}
+
+// Internal values of state machine
+const (
+ TCPStateClosed = 0
+ TCPStateSynSent = 1
+ TCPStateEstablished = 2
+ TCPStateCloseWait = 3
+ TCPStateLastAck = 4
+ TCPStateReset = 5
+)
+
+// NewTCPSimpleFSM creates a new TCPSimpleFSM
+func NewTCPSimpleFSM(options TCPSimpleFSMOptions) *TCPSimpleFSM {
+ return &TCPSimpleFSM{
+ state: TCPStateClosed,
+ options: options,
+ }
+}
+
+func (t *TCPSimpleFSM) String() string {
+ switch t.state {
+ case TCPStateClosed:
+ return "Closed"
+ case TCPStateSynSent:
+ return "SynSent"
+ case TCPStateEstablished:
+ return "Established"
+ case TCPStateCloseWait:
+ return "CloseWait"
+ case TCPStateLastAck:
+ return "LastAck"
+ case TCPStateReset:
+ return "Reset"
+ }
+ return "?"
+}
+
+// CheckState returns false if tcp is invalid wrt current state or update the state machine's state
+func (t *TCPSimpleFSM) CheckState(tcp *layers.TCP, dir TCPFlowDirection) bool {
+ if t.state == TCPStateClosed && t.options.SupportMissingEstablishment && !(tcp.SYN && !tcp.ACK) {
+ /* try to figure out state */
+ switch true {
+ case tcp.SYN && tcp.ACK:
+ t.state = TCPStateSynSent
+ t.dir = dir.Reverse()
+ case tcp.FIN && !tcp.ACK:
+ t.state = TCPStateEstablished
+ case tcp.FIN && tcp.ACK:
+ t.state = TCPStateCloseWait
+ t.dir = dir.Reverse()
+ default:
+ t.state = TCPStateEstablished
+ }
+ }
+
+ switch t.state {
+ /* openning connection */
+ case TCPStateClosed:
+ if tcp.SYN && !tcp.ACK {
+ t.dir = dir
+ t.state = TCPStateSynSent
+ return true
+ }
+ case TCPStateSynSent:
+ if tcp.RST {
+ t.state = TCPStateReset
+ return true
+ }
+
+ if tcp.SYN && tcp.ACK && dir == t.dir.Reverse() {
+ t.state = TCPStateEstablished
+ return true
+ }
+ if tcp.SYN && !tcp.ACK && dir == t.dir {
+ // re-transmission
+ return true
+ }
+ /* established */
+ case TCPStateEstablished:
+ if tcp.RST {
+ t.state = TCPStateReset
+ return true
+ }
+
+ if tcp.FIN {
+ t.state = TCPStateCloseWait
+ t.dir = dir
+ return true
+ }
+ // accept any packet
+ return true
+ /* closing connection */
+ case TCPStateCloseWait:
+ if tcp.RST {
+ t.state = TCPStateReset
+ return true
+ }
+
+ if tcp.FIN && tcp.ACK && dir == t.dir.Reverse() {
+ t.state = TCPStateLastAck
+ return true
+ }
+ if tcp.ACK {
+ return true
+ }
+ case TCPStateLastAck:
+ if tcp.RST {
+ t.state = TCPStateReset
+ return true
+ }
+
+ if tcp.ACK && t.dir == dir {
+ t.state = TCPStateClosed
+ return true
+ }
+ }
+ return false
+}