Example #1
0
// processor is a worker that decodes packets and passes on to Account and Log.
func (c *Capture) processor(num int, packetsCh <-chan gopacket.Packet) {
	log.Printf("processor %d: starting", num)

	buffer := c.nextBuffer()
	defer func() {
		// TODO: Save a checkpoint.
		if c.Log != nil {
			c.Log(buffer)
		}
	}()

	var (
		eth     layers.Ethernet
		ip4     layers.IPv4
		ip6     layers.IPv6
		tcp     layers.TCP
		udp     layers.UDP
		dns     layers.DNS
		payload gopacket.Payload
	)
	parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth, &ip4, &ip6, &tcp, &udp, &dns, &payload)
	for packet := range packetsCh {
		var decoded []gopacket.LayerType
		if err := parser.DecodeLayers(packet.Data(), &decoded); err != nil {
			log.Printf("processor %d: %v", num, err)
		}
		m := packet.Metadata()
		b := Metadata{
			Timestamp: m.Timestamp,
			Size:      uint64(m.Length),
		}
		for _, layerType := range decoded {
			switch layerType {
			case layers.LayerTypeIPv6:
				b.SrcIP, b.DstIP = ip6.SrcIP, ip6.DstIP
				b.SrcName, b.DstName = c.revDNS.names(local(b.SrcIP, b.DstIP), ip6.NetworkFlow())
				b.V6 = true
			case layers.LayerTypeIPv4:
				b.SrcIP, b.DstIP = ip4.SrcIP, ip4.DstIP
				b.SrcName, b.DstName = c.revDNS.names(local(b.SrcIP, b.DstIP), ip4.NetworkFlow())
			case layers.LayerTypeTCP:
				b.SrcPort, b.DstPort = uint16(tcp.SrcPort), uint16(tcp.DstPort)
			case layers.LayerTypeUDP:
				b.SrcPort, b.DstPort = uint16(udp.SrcPort), uint16(udp.DstPort)
			case layers.LayerTypeDNS:
				// Add DNS answers to reverse DNS map.
				// The "src" is the host who did the query, but answers are replies, so "src" = dst.
				// Should be here only after b.DstIP is set.
				c.revDNS.add(b.DstIP, &dns)
			}
		}

		c.Account(&b)

		if c.Log != nil {
			buffer = append(buffer, b)
			if len(buffer) >= c.BufferSize {
				go c.logBuffer(buffer)
				buffer = c.nextBuffer()
			}
		}
	}
	log.Printf("processor %d: stopping", num)
}
func main() {
	defer util.Run()()
	var handle *pcap.Handle
	var err error

	flushDuration, err := time.ParseDuration(*flushAfter)
	if err != nil {
		log.Fatal("invalid flush duration: ", *flushAfter)
	}

	// log.Printf("starting capture on interface %q", *iface)
	// // Set up pcap packet capture
	// handle, err := pcap.OpenLive(*iface, int32(*snaplen), true, flushDuration/2)
	// if err != nil {
	// 	log.Fatal("error opening pcap handle: ", err)
	// }
	// Set up pcap packet capture
	if *fname != "" {
		log.Printf("Reading from pcap dump %q", *fname)
		handle, err = pcap.OpenOffline(*fname)
	} else {
		log.Fatalln("Error: pcap file name is required!")
		// log.Printf("Starting capture on interface %q", *iface)
		// handle, err = pcap.OpenLive(*iface, int32(*snaplen), true, pcap.BlockForever)
	}
	if err != nil {
		log.Fatal(err)
	}

	if err := handle.SetBPFFilter(*filter); err != nil {
		log.Fatal("error setting BPF filter: ", err)
	}

	// Set up assembly
	streamFactory := &statsStreamFactory{}
	streamPool := tcpassembly.NewStreamPool(streamFactory)
	assembler := tcpassembly.NewAssembler(streamPool)
	assembler.MaxBufferedPagesPerConnection = *bufferedPerConnection
	assembler.MaxBufferedPagesTotal = *bufferedTotal

	log.Println("reading in packets")

	// We use a DecodingLayerParser here instead of a simpler PacketSource.
	// This approach should be measurably faster, but is also more rigid.
	// PacketSource will handle any known type of packet safely and easily,
	// but DecodingLayerParser will only handle those packet types we
	// specifically pass in.  This trade-off can be quite useful, though, in
	// high-throughput situations.
	var eth layers.Ethernet
	var dot1q layers.Dot1Q
	var ip4 layers.IPv4
	var ip6 layers.IPv6
	var ip6extensions layers.IPv6ExtensionSkipper
	var tcp layers.TCP
	var payload gopacket.Payload
	parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet,
		&eth, &dot1q, &ip4, &ip6, &ip6extensions, &tcp, &payload)
	decoded := make([]gopacket.LayerType, 0, 4)

	nextFlush := time.Now().Add(flushDuration / 2)

	var byteCount int64
	start := time.Now()

loop:
	for ; *packetCount != 0; *packetCount-- {
		// Check to see if we should flush the streams we have
		// that haven't seen any new data in a while.  Note we set a
		// timeout on our PCAP handle, so this should happen even if we
		// never see packet data.
		if time.Now().After(nextFlush) {
			stats, _ := handle.Stats()
			log.Printf("flushing all streams that haven't seen packets in the last 2 minutes, pcap stats: %+v", stats)
			assembler.FlushOlderThan(time.Now().Add(flushDuration))
			nextFlush = time.Now().Add(flushDuration / 2)
		}

		// To speed things up, we're also using the ZeroCopy method for
		// reading packet data.  This method is faster than the normal
		// ReadPacketData, but the returned bytes in 'data' are
		// invalidated by any subsequent ZeroCopyReadPacketData call.
		// Note that tcpassembly is entirely compatible with this packet
		// reading method.  This is another trade-off which might be
		// appropriate for high-throughput sniffing:  it avoids a packet
		// copy, but its cost is much more careful handling of the
		// resulting byte slice.
		data, ci, err := handle.ZeroCopyReadPacketData()

		if err != nil {
			log.Printf("error getting packet: %v", err)
			break loop // continue
		}
		err = parser.DecodeLayers(data, &decoded)
		if err != nil {
			log.Printf("error decoding packet: %v", err)
			continue
		}
		if *logAllPackets {
			log.Printf("decoded the following layers: %v", decoded)
		}
		byteCount += int64(len(data))
		// Find either the IPv4 or IPv6 address to use as our network
		// layer.
		foundNetLayer := false
		var netFlow gopacket.Flow
		for _, typ := range decoded {
			switch typ {
			case layers.LayerTypeIPv4:
				netFlow = ip4.NetworkFlow()
				foundNetLayer = true
			case layers.LayerTypeIPv6:
				netFlow = ip6.NetworkFlow()
				foundNetLayer = true
			case layers.LayerTypeTCP:
				if foundNetLayer {
					assembler.AssembleWithTimestamp(netFlow, &tcp, ci.Timestamp)
				} else {
					log.Println("could not find IPv4 or IPv6 layer, inoring")
				}
				continue loop
			}
		}
		log.Println("could not find TCP layer")
	}
	assembler.FlushAll()
	log.Printf("processed %d bytes in %v", byteCount, time.Since(start))
}