func BenchmarkMultiStreamGrow(b *testing.B) { t := layers.TCP{ SrcPort: 1, DstPort: 2, Seq: 0, BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, } a := NewAssembler(NewStreamPool(&testFactory{})) for i := 0; i < b.N; i++ { t.SrcPort = layers.TCPPort(i) a.Assemble(netFlow, &t) t.Seq += 10 } }
func BenchmarkSingleStreamLoss(b *testing.B) { t := layers.TCP{ SrcPort: 1, DstPort: 2, SYN: true, Seq: 1000, BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, } a := NewAssembler(NewStreamPool(&testFactory{})) for i := 0; i < b.N; i++ { a.Assemble(netFlow, &t) t.SYN = false t.Seq += 11 } }
func BenchmarkMultiStreamConn(b *testing.B) { t := layers.TCP{ SrcPort: 1, DstPort: 2, Seq: 0, SYN: true, BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, } a := NewAssembler(NewStreamPool(&testFactory{})) for i := 0; i < b.N; i++ { t.SrcPort = layers.TCPPort(i) a.Assemble(netFlow, &t) if i%65536 == 65535 { if t.SYN { t.SYN = false t.Seq += 1 } t.Seq += 10 } } }
func BenchmarkSingleStreamSkips(b *testing.B) { t := layers.TCP{ SrcPort: 1, DstPort: 2, SYN: true, Seq: 1000, BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, } a := NewAssembler(NewStreamPool(&testFactory{})) skipped := false for i := 0; i < b.N; i++ { if i%10 == 9 { t.Seq += 10 skipped = true } else if skipped { t.Seq -= 20 } a.Assemble(netFlow, &t) if t.SYN { t.SYN = false t.Seq++ } t.Seq += 10 if skipped { t.Seq += 10 skipped = false } } }
func (tcp *Tcp) Process(id *flows.FlowID, tcphdr *layers.TCP, pkt *protos.Packet) { // This Recover should catch all exceptions in // protocol modules. defer logp.Recover("Process tcp exception") stream, created := tcp.getStream(pkt) if stream.conn == nil { return } conn := stream.conn if id != nil { id.AddConnectionID(uint64(conn.id)) } if isDebug { debugf("tcp flow id: %p", id) } if len(pkt.Payload) == 0 && !tcphdr.FIN { // return early if packet is not interesting. Still need to find/create // stream first in order to update the TCP stream timer return } tcpStartSeq := tcphdr.Seq tcpSeq := tcpStartSeq + uint32(len(pkt.Payload)) lastSeq := conn.lastSeq[stream.dir] if isDebug { debugf("pkt.start_seq=%v pkt.last_seq=%v stream.last_seq=%v (len=%d)", tcpStartSeq, tcpSeq, lastSeq, len(pkt.Payload)) } if len(pkt.Payload) > 0 && lastSeq != 0 { if tcpSeqBeforeEq(tcpSeq, lastSeq) { if isDebug { debugf("Ignoring retransmitted segment. pkt.seq=%v len=%v stream.seq=%v", tcphdr.Seq, len(pkt.Payload), lastSeq) } return } switch tcpSeqCompare(lastSeq, tcpStartSeq) { case seqLT: // lastSeq < tcpStartSeq => Gap in tcp stream detected if created { break } gap := int(tcpStartSeq - lastSeq) debugf("Gap in tcp stream. last_seq: %d, seq: %d, gap: %d", lastSeq, tcpStartSeq, gap) drop := stream.gapInStream(gap) if drop { if isDebug { debugf("Dropping connection state because of gap") } droppedBecauseOfGaps.Add(1) // drop application layer connection state and // update stream_id for app layer analysers using stream_id for lookups conn.id = tcp.getId() conn.data = nil } case seqGT: // lastSeq > tcpStartSeq => overlapping TCP segment detected. shrink packet delta := lastSeq - tcpStartSeq if isDebug { debugf("Overlapping tcp segment. last_seq %d, seq: %d, delta: %d", lastSeq, tcpStartSeq, delta) } pkt.Payload = pkt.Payload[delta:] tcphdr.Seq += delta } } conn.lastSeq[stream.dir] = tcpSeq stream.addPacket(pkt, tcphdr) }
// scan scans the dst IP address of this scanner. func (s *scanner) scan() error { // First off, get the MAC address we should be sending packets to. hwaddr, err := s.getHwAddr() if err != nil { return err } // Construct all the network layers we need. eth := layers.Ethernet{ SrcMAC: s.iface.HardwareAddr, DstMAC: hwaddr, EthernetType: layers.EthernetTypeIPv4, } ip4 := layers.IPv4{ SrcIP: s.src, DstIP: s.dst, Version: 4, TTL: 64, Protocol: layers.IPProtocolTCP, } tcp := layers.TCP{ SrcPort: 54321, DstPort: 0, // will be incremented during the scan SYN: true, } tcp.SetNetworkLayerForChecksum(&ip4) // Create the flow we expect returning packets to have, so we can check // against it and discard useless packets. ipFlow := gopacket.NewFlow(layers.EndpointIPv4, s.dst, s.src) start := time.Now() for { // Send one packet per loop iteration until we've sent packets // to all of ports [1, 65535]. if tcp.DstPort < 65535 { start = time.Now() tcp.DstPort++ if err := s.send(ð, &ip4, &tcp); err != nil { log.Printf("error sending to port %v: %v", tcp.DstPort, err) } } // Time out 5 seconds after the last packet we sent. if time.Since(start) > time.Second*5 { log.Printf("timed out for %v, assuming we've seen all we can", s.dst) return nil } // Read in the next packet. data, _, err := s.handle.ReadPacketData() if err == pcap.NextErrorTimeoutExpired { continue } else if err != nil { log.Printf("error reading packet: %v", err) continue } // Parse the packet. We'd use DecodingLayerParser here if we // wanted to be really fast. packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy) // Find the packets we care about, and print out logging // information about them. All others are ignored. if net := packet.NetworkLayer(); net == nil { // log.Printf("packet has no network layer") } else if net.NetworkFlow() != ipFlow { // log.Printf("packet does not match our ip src/dst") } else if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer == nil { // log.Printf("packet has not tcp layer") } else if tcp, ok := tcpLayer.(*layers.TCP); !ok { // We panic here because this is guaranteed to never // happen. panic("tcp layer is not tcp layer :-/") } else if tcp.DstPort != 54321 { // log.Printf("dst port %v does not match", tcp.DstPort) } else if tcp.RST { log.Printf(" port %v closed", tcp.SrcPort) } else if tcp.SYN && tcp.ACK { log.Printf(" port %v open", tcp.SrcPort) } else { // log.Printf("ignoring useless packet") } } }
// 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() }