// AssembleWithTimestamp reassembles the given TCP packet into its appropriate // stream. // // The timestamp passed in must be the timestamp the packet was seen. // For packets read off the wire, time.Now() should be fine. For packets read // from PCAP files, CaptureInfo.Timestamp should be passed in. This timestamp // will affect which streams are flushed by a call to FlushOlderThan. // // Each Assemble call results in, in order: // // zero or one calls to StreamFactory.New, creating a stream // zero or one calls to Reassembled on a single stream // zero or one calls to ReassemblyComplete on the same stream func (a *Assembler) AssembleWithTimestamp(netFlow gopacket.Flow, t *layers.TCP, timestamp time.Time) { // Ignore empty TCP packets if !t.SYN && !t.FIN && !t.RST && len(t.LayerPayload()) == 0 { if *debugLog { log.Println("ignoring useless packet") } return } a.ret = a.ret[:0] key := key{netFlow, t.TransportFlow()} var conn *connection // This for loop handles a race condition where a connection will close, lock // the connection pool, and remove itself, but before it locked the connection // pool it's returned to another Assemble statement. This should loop 0-1 // times for the VAST majority of cases. for { conn = a.connPool.getConnection( key, !t.SYN && len(t.LayerPayload()) == 0, timestamp) if conn == nil { if *debugLog { log.Printf("%v got empty packet on otherwise empty connection", key) } return } conn.mu.Lock() if !conn.closed { break } conn.mu.Unlock() } if conn.lastSeen.Before(timestamp) { conn.lastSeen = timestamp } seq, bytes := Sequence(t.Seq), t.Payload if conn.nextSeq == invalidSequence { if t.SYN { if *debugLog { log.Printf("%v saw first SYN packet, returning immediately, seq=%v", key, seq) } a.ret = append(a.ret, Reassembly{ Bytes: bytes, Skip: 0, Start: true, Seen: timestamp, }) conn.nextSeq = seq.Add(len(bytes) + 1) } else { if *debugLog { log.Printf("%v waiting for start, storing into connection", key) } a.insertIntoConn(t, conn, timestamp) } } else if diff := conn.nextSeq.Difference(seq); diff > 0 { if *debugLog { log.Printf("%v gap in sequence numbers (%v, %v) diff %v, storing into connection", key, conn.nextSeq, seq, diff) } a.insertIntoConn(t, conn, timestamp) } else { bytes, conn.nextSeq = byteSpan(conn.nextSeq, seq, bytes) if *debugLog { log.Printf("%v found contiguous data (%v, %v), returning immediately", key, seq, conn.nextSeq) } a.ret = append(a.ret, Reassembly{ Bytes: bytes, Skip: 0, End: t.RST || t.FIN, Seen: timestamp, }) } if len(a.ret) > 0 { a.sendToConnection(conn) } conn.mu.Unlock() }
// AssembleWithTimestamp reassembles the given TCP packet into its appropriate // stream. // // The timestamp passed in must be the timestamp the packet was seen. For // packets read off the wire, time.Now() should be fine. For packets read from // PCAP files, CaptureInfo.Timestamp should be passed in. This timestamp will // affect which streams are flushed by a call to FlushOlderThan. // // Each Assemble call results in, in order: // // zero or one calls to StreamFactory.New, creating a stream // zero or one calls to Reassembled on a single stream // zero or one calls to ReassemblyComplete on the same stream func (a *Assembler) AssembleWithTimestamp(netFlow gopacket.Flow, t *layers.TCP, timestamp time.Time) { // Ignore empty TCP packets if !t.SYN && !t.FIN && !t.RST && len(t.LayerPayload()) == 0 { return } a.ret = a.ret[:0] key := key{netFlow, t.TransportFlow()} var conn *connection // This for loop handles a race condition where a connection will close, // lock the connection pool, and remove itself, but before it locked the // connection pool it's returned to another Assemble statement. This should // loop 0-1 times for the VAST majority of cases. for { conn = a.connPool.getConnection( key, !t.SYN && len(t.LayerPayload()) == 0, timestamp) if conn == nil { if *debugLog { log.Printf("%v got empty packet on otherwise empty connection", key) } return } conn.mu.Lock() if !conn.closed { break } conn.mu.Unlock() } if conn.lastSeen.Before(timestamp) { conn.lastSeen = timestamp } seq, bytes := Sequence(t.Seq), t.Payload if conn.nextSeq == invalidSequence { // Handling the first packet we've seen on the stream. skip := 0 if !t.SYN { // don't add 1 since we're just going to assume the sequence number // without the SYN packet. // stream was picked up somewhere in the middle, so indicate that we // don't know how many packets came before it. conn.nextSeq = seq.Add(len(bytes)) skip = -1 } else { // for SYN packets, also increment the sequence number by 1 conn.nextSeq = seq.Add(len(bytes) + 1) } a.ret = append(a.ret, tcpassembly.Reassembly{ Bytes: bytes, Skip: skip, Start: t.SYN, Seen: timestamp, }) a.insertIntoConn(t, conn, timestamp) } else if diff := conn.nextSeq.Difference(seq); diff > 0 { a.insertIntoConn(t, conn, timestamp) } else { bytes, conn.nextSeq = byteSpan(conn.nextSeq, seq, bytes) a.ret = append(a.ret, tcpassembly.Reassembly{ Bytes: bytes, Skip: 0, End: t.RST || t.FIN, Seen: timestamp, }) } if len(a.ret) > 0 { a.sendToConnection(conn) } conn.mu.Unlock() }