// stateFinWait2 handles packets for the FIN-WAIT-2 state func (c *Connection) stateFinWait2(p *types.PacketManifest, flow *types.TcpIpFlow, nextSeqPtr *types.Sequence, nextAckPtr *types.Sequence, statePtr *uint8) { c.detectCensorInjection(p) diff := nextSeqPtr.Difference(types.Sequence(p.TCP.Seq)) if diff < 0 { // overlap if len(p.Payload) > 0 { c.detectInjection(p, p.Flow) } } else if diff > 0 { // future out of order log.Print("FIN-WAIT-2: out of order packet received.\n") log.Printf("got TCP.Seq %d expected %d\n", p.TCP.Seq, *nextSeqPtr) c.Close() } else if diff == 0 { // contiguous if p.TCP.ACK && p.TCP.FIN { if types.Sequence(p.TCP.Ack).Difference(*nextAckPtr) != 0 { log.Print("FIN-WAIT-2: out of order ACK packet received.\n") c.Close() return } *nextSeqPtr += 1 *statePtr = TCP_TIME_WAIT } else { if len(p.Payload) > 0 { c.detectInjection(p, p.Flow) } else { log.Print("FIN-WAIT-2: wtf non-FIN/ACK with payload len 0") } } } }
// addContiguous adds contiguous byte-sets to a connection. // returns the next Sequence number and a bool value set to // true if the end of connection was detected. func (o *OrderedCoalesce) addContiguous(nextSeq types.Sequence) (types.Sequence, bool) { var isEnd bool for o.first != nil && nextSeq.Difference(o.first.Seq) <= 0 { nextSeq, isEnd = o.addNext(nextSeq) if isEnd { return nextSeq, true } } return nextSeq, false }
// addNext pops the first page off our doubly-linked-list and // appends it to the reassembly-ring. // Here we also handle the case where the connection should be closed // by returning the bool value set to true. func (o *OrderedCoalesce) addNext(nextSeq types.Sequence) (types.Sequence, bool) { if o.first == nil { panic("o.first is nil") } diff := nextSeq.Difference(o.first.Seq) if nextSeq == types.InvalidSequence { o.first.Skip = -1 } else if diff > 0 { o.first.Skip = int(diff) } if o.first.End { o.freeNext() return -1, true // after closing the connection our Sequence return value doesn't matter } if len(o.first.Bytes) == 0 { o.freeNext() return nextSeq, false } // ensure we do not add segments that end before nextSeq diff = o.first.Seq.Add(len(o.first.Bytes)).Difference(nextSeq) if diff > 0 { o.freeNext() return nextSeq, false } if o.DetectCoalesceInjection && len(o.first.Bytes) > 0 { // XXX stream segment overlap condition if diff < 0 { p := types.PacketManifest{ Timestamp: o.first.Seen, Payload: o.first.Bytes, TCP: layers.TCP{ Seq: uint32(o.first.Seq), }, } event := injectionInStreamRing(&p, o.Flow, o.StreamRing, "coalesce injection", 0) if event != nil { o.log.Log(event) } else { log.Print("not an attack attempt; a normal TCP unordered stream segment coalesce\n") } } } bytes, seq := byteSpan(nextSeq, o.first.Seq, o.first.Bytes) // XXX injection happens here if bytes != nil { o.first.Bytes = bytes nextSeq = seq // append reassembly to the reassembly ring buffer if len(o.first.Bytes) > 0 { o.StreamRing.Reassembly = &o.first.Reassembly o.StreamRing = o.StreamRing.Next() } } o.freeNext() return nextSeq, false }
func byteSpan(expected, received types.Sequence, bytes []byte) (toSend []byte, next types.Sequence) { if expected == types.InvalidSequence { return bytes, received.Add(len(bytes)) } span := int(received.Difference(expected)) if span <= 0 { return bytes, received.Add(len(bytes)) } else if len(bytes) < span { return nil, expected } return bytes[span:], expected.Add(len(bytes) - span) }
// getHeadFromRing returns a pointer to the oldest ring element that // contains the beginning of our sequence range (start - end) AND // whose Reassembly.Skip value is 0 (zero). func getHeadFromRing(ringPtr *types.Ring, start, end types.Sequence) *types.Ring { var head *types.Ring current := ringPtr.Prev() if current.Reassembly == nil { return nil } if start.Difference(current.Reassembly.Seq.Add(len(current.Reassembly.Bytes)-1)) < 0 { log.Printf("lastestSeq %d < newStartSeq %d\n", current.Reassembly.Seq.Add(len(current.Reassembly.Bytes)-1), start) return nil } head = nil var candidate *types.Ring = nil for current := ringPtr.Prev(); current != ringPtr && current.Reassembly != nil; current = current.Prev() { if len(current.Reassembly.Bytes) == 0 { continue } startDiff := current.Reassembly.Seq.Difference(start) if startDiff == 0 { return current } if startDiff < 0 { finishEndDiff := current.Reassembly.Seq.Difference(end) if finishEndDiff >= 0 { candidate = current } continue } else { endDiff := start.Difference(current.Reassembly.Seq.Add(len(current.Reassembly.Bytes) - 1)) if endDiff >= 0 { head = current break } } } if head == nil && candidate != nil { head = candidate } return head }
func TestGetStartSequence(t *testing.T) { var start types.Sequence = 4 var head *types.Ring = types.NewRing(10) head.Reassembly = &types.Reassembly{ Seq: 3, Bytes: []byte{1, 2, 3, 4, 5, 6, 7}, } startSeq := getStartSequence(head, start) if startSeq.Difference(start) != 0 { t.Errorf("startSeq %d != start %d\n", startSeq, start) t.Fail() } start = 2 startSeq = getStartSequence(head, start) if startSeq != start.Add(1) { t.Errorf("startSeq %d != start %d\n", startSeq, start.Add(1)) t.Fail() } }
// stateFinWait1 handles packets for the FIN-WAIT-1 state //func (c *Connection) stateFinWait1(p *types.PacketManifest) { func (c *Connection) stateFinWait1(p *types.PacketManifest, flow *types.TcpIpFlow, nextSeqPtr *types.Sequence, nextAckPtr *types.Sequence, statePtr, otherStatePtr *uint8) { c.detectCensorInjection(p) diff := nextSeqPtr.Difference(types.Sequence(p.TCP.Seq)) if diff < 0 { // overlap if len(p.Payload) > 0 { c.detectInjection(p, p.Flow) } return } else if diff > 0 { // future out of order log.Print("FIN-WAIT-1: ignoring out of order packet") return } else if p.TCP.ACK { *nextAckPtr += 1 if p.TCP.FIN { *statePtr = TCP_CLOSING *otherStatePtr = TCP_LAST_ACK *nextSeqPtr = types.Sequence(p.TCP.Seq).Add(len(p.Payload) + 1) if types.Sequence(p.TCP.Ack).Difference(*nextAckPtr) != 0 { log.Printf("FIN-WAIT-1: unexpected ACK: got %d expected %d TCP.Seq %d\n", p.TCP.Ack, *nextAckPtr, p.TCP.Seq) c.closingFlow = p.Flow c.closingSeq = types.Sequence(p.TCP.Seq) return } } else { *statePtr = TCP_FIN_WAIT2 *nextSeqPtr = types.Sequence(p.TCP.Seq).Add(len(p.Payload)) } } else { log.Print("FIN-WAIT-1: non-ACK packet received.\n") c.closingFlow = p.Flow c.closingSeq = types.Sequence(p.TCP.Seq) return } }
// getOverlapBytes takes several arguments: // head and tail - ring pointers used to indentify a list of ring elements. // start and end - sequence numbers representing locations in head and tail respectively. // NOTE: here we assume that the head and tail were calculated properly such that: // 1. start must be located within the head segment's sequence boundaries or BEFORE. // 2. end must be located within the tail segment's sequence boundaries or AFTER. // normally head and tail values would be procured with a call to getOverlapRings like this: // head, tail := getOverlapRings(p, flow, ringPtr) // Given these arguments, getOverlapBytes returns the overlap byte array; // that is the contiguous data stored in our ring buffer // that overlaps with the stream segment specified by the start and end Sequence boundaries. // The other return values are the slice offsets of the original packet payload that can be used to derive // the section of the packet that has overlapped with our Reassembly ring buffer. func getOverlapBytes(head, tail *types.Ring, start, end types.Sequence) ([]byte, int, int) { var overlapStartSlice, overlapEndSlice int var overlapBytes []byte var diff int if head == nil || tail == nil { panic("wtf; head or tail is nil\n") } if len(head.Reassembly.Bytes) == 0 { panic("length of head ring element is zero") } if len(tail.Reassembly.Bytes) == 0 { panic("length of tail ring element is zero") } packetLength := start.Difference(end) + 1 if packetLength <= 0 { panic("wtf") } var headOffset int tailLastSeq := tail.Reassembly.Seq.Add(len(tail.Reassembly.Bytes) - 1) startDiff := head.Reassembly.Seq.Difference(start) if startDiff < 0 { headOffset = 0 overlapStartSlice = -1 * startDiff if overlapStartSlice > packetLength { // XXX print a error message here or panic? log.Print("getOverlapbytes: incorrect start/end head/tail parameters.") return nil, 0, 0 } } else if startDiff == 0 { headOffset = 0 overlapStartSlice = 0 } else { headOffset = startDiff overlapStartSlice = 0 } if head.Reassembly.Seq == tail.Reassembly.Seq { log.Print("head == tail\n") var endOffset int diff = tailLastSeq.Difference(end) if diff <= 0 { overlapEndSlice = packetLength tailDiff := end.Difference(tailLastSeq) endOffset = len(head.Reassembly.Bytes) - tailDiff } else { overlapEndSlice = packetLength - diff endOffset = len(head.Reassembly.Bytes) } overlapBytes = head.Reassembly.Bytes[headOffset:endOffset] } else { log.Print("head != tail\n") diff = tailLastSeq.Difference(end) var tailSlice int // if end is equal or less than tailLastSeq if diff <= 0 { overlapEndSlice = packetLength tailSlice = len(tail.Reassembly.Bytes) - (diff * -1) if tailSlice < 0 { panic("regression in getTailFromRing") } } else { if diff > packetLength { // XXX should we opt out instead of making the comparison? overlapEndSlice = packetLength } else { overlapEndSlice = packetLength - diff if overlapEndSlice < overlapStartSlice { // XXX wtf return nil, 0, 0 } } tailSlice = len(tail.Reassembly.Bytes) } overlapBytes = getRingSlice(head, tail, headOffset, tailSlice) if overlapBytes == nil { return nil, 0, 0 } } return overlapBytes, overlapStartSlice, overlapEndSlice }