func (p *Pcap) sniff(readHandle *pcap.Handle, consumer BridgeConsumer) { dec := NewEthernetDecoder() for { pkt, _, err := readHandle.ZeroCopyReadPacketData() if err == pcap.NextErrorTimeoutExpired { continue } checkFatal(err) dec.DecodeLayers(pkt) if len(dec.decoded) == 0 { continue } if fop := consumer(dec.PacketKey()); !fop.Discards() { // We are handing over the frame to // forwarders, so we need to make a copy of it // in order to prevent the next capture from // overwriting the data pktLen := len(pkt) pktCopy := make([]byte, pktLen, pktLen) copy(pktCopy, pkt) fop.Process(pktCopy, dec, false) } } }
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, ð, &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)) }