func BenchmarkDecodingLayerParserHandlePanic(b *testing.B) { decoded := make([]gopacket.LayerType, 0, 20) dlp := gopacket.NewDecodingLayerParser(LayerTypeEthernet, &Ethernet{}, &IPv4{}, &TCP{}, &gopacket.Payload{}) dlp.IgnorePanic = false for i := 0; i < b.N; i++ { dlp.DecodeLayers(testSimpleTCPPacket, &decoded) } }
func TestDecodingLayerParserFullTCPPacket(t *testing.T) { dlp := gopacket.NewDecodingLayerParser(LayerTypeEthernet, &Ethernet{}, &IPv4{}, &TCP{}, &gopacket.Payload{}) decoded := make([]gopacket.LayerType, 1) err := dlp.DecodeLayers(testSimpleTCPPacket, &decoded) if err != nil { t.Error("Error from dlp parser: ", err) } if len(decoded) != 4 { t.Error("Expected 4 layers parsed, instead got ", len(decoded)) } }
// startParsingReplies starts a goroutine which participates // in the pcap sniffing pipeline by reading packets from // it's channel and writing them to an output channel; // either the TCP or ICMP output channels. func (o *NFQueueTraceObserver) startParsingReplies() { var eth layers.Ethernet var ip layers.IPv4 var icmp layers.ICMPv4 var eth2 layers.Ethernet var ip2 layers.IPv4 var tcp layers.TCP var payload gopacket.Payload icmpParser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, ð, &ip, &icmp, &payload) tcpParser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, ð2, &ip2, &tcp) decoded := make([]gopacket.LayerType, 0, 4) o.startReceivingIcmp() o.startWatchingForTcpClose() go func() { defer close(o.receiveIcmpChan) defer close(o.receiveTcpChan) for packet := range o.receiveParseChan { err := tcpParser.DecodeLayers(packet, &decoded) if err == nil { o.receiveTcpChan <- TcpIpLayer{ ip: ip2, tcp: tcp, } continue } err = icmpParser.DecodeLayers(packet, &decoded) if err == nil { o.receiveIcmpChan <- PayloadIcmpIpLayer{ ip: ip, icmp: icmp, payload: payload, } } } }() }
// Listen in an infinite loop for new packets func Listen(config *Config) error { // Array to store which layers were decoded decoded := []gopacket.LayerType{} // Faster, predefined layer parser that doesn't make copies of the layer slices parser := gopacket.NewDecodingLayerParser( layers.LayerTypeEthernet, ð, &ip, &tcp, &udp, &icmp, &dns, &payload) // Infinite loop that reads incoming packets for config.isRunning { data, ci, err := config.sniffer.ReadPacket() if err != nil { log.Printf("Error getting packet: %v %s", err, ci) continue } err = parser.DecodeLayers(data, &decoded) if err != nil { log.Printf("Error decoding packet: %v", err) continue } if len(decoded) == 0 { log.Print("Packet contained no valid layers") continue } // Example of how to get data out of specific layers // for _, layerType := range decoded { // switch layerType { // case layers.LayerTypeIPv4: // log.Printf("src: %v, dst: %v, proto: %v", ip.SrcIP, ip.DstIP, ip.Protocol) // } // } if config.pcapWriter != nil { config.pcapWriter.WritePacket(ci, data) } } return nil }
func benchmarkLayerDecode(source *BufferPacketSource, assemble bool) { var tcp layers.TCP var ip layers.IPv4 var eth layers.Ethernet var udp layers.UDP var icmp layers.ICMPv4 var payload gopacket.Payload parser := gopacket.NewDecodingLayerParser( layers.LayerTypeEthernet, ð, &ip, &icmp, &tcp, &udp, &payload) pool := tcpassembly.NewStreamPool(&streamFactory{}) assembler := tcpassembly.NewAssembler(pool) var decoded []gopacket.LayerType start := time.Now() packets, decodedlayers, assembled := 0, 0, 0 for { packets++ data, ci, err := source.ReadPacketData() if err == io.EOF { break } else if err != nil { fmt.Println("Error reading packet: ", err) continue } err = parser.DecodeLayers(data, &decoded) for _, typ := range decoded { decodedlayers++ if typ == layers.LayerTypeTCP && assemble { assembled++ assembler.AssembleWithTimestamp(ip.NetworkFlow(), &tcp, ci.Timestamp) } } } if assemble { assembler.FlushAll() } duration := time.Since(start) fmt.Printf("\tRead in %d packets in %v, decoded %v layers, assembled %v packets: %v per packet\n", packets, duration, decodedlayers, assembled, duration/time.Duration(packets)) }
func NewEthernetDecoder() *EthernetDecoder { dec := &EthernetDecoder{} dec.parser = gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &dec.eth, &dec.ip) return dec }
func main() { flag.Parse() log.Printf("starting capture on interface %q", *iface) // Set up pcap packet capture handle, err := pcap.OpenLive(*iface, int32(*snaplen), true, time.Minute) if err != nil { log.Fatal("error opening pcap handle: ", 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) 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(time.Minute) loop: for { // 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(-time.Minute * 2)) nextFlush = time.Now().Add(time.Minute) } // 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, _, err := handle.ZeroCopyReadPacketData() if err != nil { log.Printf("error getting packet: %v", err) 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) } // 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.Assemble(netFlow, &tcp) } else { log.Println("could not find IPv4 or IPv6 layer, inoring") } continue loop } } log.Println("could not find TCP layer") } }