func (i *Sniffer) decodePackets() { var eth layers.Ethernet var ip layers.IPv4 var tcp layers.TCP var payload gopacket.Payload parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, ð, &ip, &tcp, &payload) decoded := make([]gopacket.LayerType, 0, 4) for { select { case <-i.stopDecodeChan: return case timedRawPacket := <-i.decodePacketChan: newPayload := new(gopacket.Payload) payload = *newPayload err := parser.DecodeLayers(timedRawPacket.RawPacket, &decoded) if err != nil { continue } flow := types.NewTcpIpFlowFromFlows(ip.NetworkFlow(), tcp.TransportFlow()) packetManifest := types.PacketManifest{ Timestamp: timedRawPacket.Timestamp, Flow: flow, RawPacket: timedRawPacket.RawPacket, IP: ip, TCP: tcp, Payload: payload, } i.dispatcher.ReceivePacket(&packetManifest) } } }
// getPacketFlow returns a TcpIpFlow struct given a byte array packet func NewTcpIpFlowFromPacket(packet []byte) (*TcpIpFlow, error) { var ip layers.IPv4 var tcp layers.TCP decoded := []gopacket.LayerType{} parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &ip, &tcp) err := parser.DecodeLayers(packet, &decoded) if err != nil { return &TcpIpFlow{}, err } return &TcpIpFlow{ ipFlow: ip.NetworkFlow(), tcpFlow: tcp.TransportFlow(), }, 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)) }
//kick off packet procesing threads and start the packet capture loop func doCapture(handle *pcap.Handle, logChan chan dnsLogEntry, config *pdnsConfig, reChan chan tcpDataStruct, stats *statsd.StatsdBuffer) { gcAgeDur, err := time.ParseDuration(config.gcAge) if err != nil { log.Fatal("Your gc_age parameter was not parseable. Use a string like '-1m'") } gcIntervalDur, err := time.ParseDuration(config.gcInterval) if err != nil { log.Fatal("Your gc_age parameter was not parseable. Use a string like '3m'") } //setup the global channel for reassembled TCP streams reassembleChan = reChan /* init channels for the packet handlers and kick off handler threads */ var channels []chan *packetData for i := 0; i < config.numprocs; i++ { channels = append(channels, make(chan *packetData, 100)) } for i := 0; i < config.numprocs; i++ { go handlePacket(channels[i], logChan, gcIntervalDur, gcAgeDur, i, stats) } // Use the handle as a packet source to process all packets packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) //only decode packet in response to function calls, this moves the //packet processing to the processing threads packetSource.DecodeOptions.Lazy = true //We don't mutate bytes of the packets, so no need to make a copy //this does mean we need to pass the packet via the channel, not a pointer to the packet //as the underlying buffer will get re-allocated packetSource.DecodeOptions.NoCopy = true /* parse up to the IP layer so we can consistently balance the packets across our processing threads TODO: in the future maybe pass this on the channel to so we don't reparse but the profiling I've done doesn't point to this as a problem */ var ethLayer layers.Ethernet var ipLayer layers.IPv4 parser := gopacket.NewDecodingLayerParser( layers.LayerTypeEthernet, ðLayer, &ipLayer, ) foundLayerTypes := []gopacket.LayerType{} CAPTURE: for { select { case reassembledTcp := <-reChan: pd := NewTcpData(reassembledTcp) channels[int(reassembledTcp.IpLayer.FastHash())&(config.numprocs-1)] <- pd if stats != nil { stats.Incr("reassembed_tcp", 1) } case packet := <-packetSource.Packets(): if packet != nil { parser.DecodeLayers(packet.Data(), &foundLayerTypes) if foundLayerType(layers.LayerTypeIPv4, foundLayerTypes) { pd := NewPacketData(packet) channels[int(ipLayer.NetworkFlow().FastHash())&(config.numprocs-1)] <- pd if stats != nil { stats.Incr("packets", 1) } } } else { //if we get here, we're likely reading a pcap and we've finished //or, potentially, the physical device we've been reading from has been //downed. Or something else crazy has gone wrong...so we break //out of the capture loop entirely. log.Debug("packetSource returned nil.") break CAPTURE } } } gracefulShutdown(channels, reChan, logChan) }
// newIPv4 returns a new initialized IPv4 Flow func newIPv4(ip *layers.IPv4) ipv4 { return ipv4{ ip4: ip.NetworkFlow(), id: ip.Id, } }
// 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, ð, &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) }
// NewTcpIpFlowFromLayers given IPv4 and TCP layers it returns a TcpIpFlow func NewTcpIpFlowFromLayers(ipLayer layers.IPv4, tcpLayer layers.TCP) *TcpIpFlow { return &TcpIpFlow{ ipFlow: ipLayer.NetworkFlow(), tcpFlow: tcpLayer.TransportFlow(), } }
func (i *Filter) decodePackets() { var eth layers.Ethernet var ip layers.IPv4 var ipv6 layers.IPv6 var tcp layers.TCP var udp layers.UDP var payload gopacket.Payload anomalyTest := make(chan *Pan) alertChan := make(chan *AlertMessage) panClose := make(chan *PanCtl) //_, IPNet, err := net.ParseCIDR("10.240.0.0/16") _, IPNet, err := net.ParseCIDR(i.options.FilterIpCIDR) if err != nil { log.Errorf("Error parsing CIDR: %#v", err) i.Stop() } decodedLen := 6 parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, ð, &ip, &ipv6, &tcp, &udp, &payload) decoded := make([]gopacket.LayerType, 0, decodedLen) // Initialize wherefore goroutines piChan := PanopticonInfo() /* for at := 0; at < 10; at++ { } */ go i.AnomalyTester(anomalyTest, piChan, alertChan) go i.AlertSlack(alertChan) go i.PanRemover(panClose) for { select { case <-i.stopDecodeChan: return case timedRawPacket := <-i.decodePacketChan: newPayload := new(gopacket.Payload) payload = *newPayload err := parser.DecodeLayers(timedRawPacket.RawPacket, &decoded) if err != nil { continue } flow := types.NewTcpIpFlowFromFlows(ip.NetworkFlow(), tcp.TransportFlow()) dcopy := make([]gopacket.LayerType, decodedLen, decodedLen) if dc := copy(dcopy, decoded); dc <= 0 { log.Errorf("Copy of decoded layers failed: %d", dc) continue } packetManifest := types.PacketManifest{ Timestamp: timedRawPacket.Timestamp, Flow: flow, RawPacket: timedRawPacket.RawPacket, DecodedLayers: dcopy, Eth: eth, IP: ip, IPv4: ip, IPv6: ipv6, TCP: tcp, UDP: udp, Payload: payload, } //Short circut to only watch traffic heading in one direction //if FilterExternal(&packetManifest) == nil { if i.options.FilterSrc { if i.options.FilterBool && IPNet.Contains(packetManifest.IP.SrcIP) { continue } } if i.options.FilterDst { if i.options.FilterBool && IPNet.Contains(packetManifest.IP.DstIP) { continue } } //Pass packet manifest to the PM-Monitor function //TODO: Improve the flow around packet processing from the sniffer/splitter i.PMMonitor(&packetManifest, anomalyTest, panClose) } } }
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)) }