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 (s *Server) ListenAndServeUDPv4() { ipAddr := &net.IPAddr{IP: net.IPv4zero} conn, err := net.ListenIP("ip4:udp", ipAddr) if err != nil { l.Info(err.Error()) return } defer conn.Close() if err = bindToDevice(conn, "tap"+s.name); err != nil { l.Info(err.Error()) return } s.Lock() s.ipv4conn, err = ipv4.NewRawConn(conn) s.Unlock() if err != nil { l.Info(err.Error()) return } if err = s.ipv4conn.SetControlMessage(ipv4.FlagDst, true); err != nil { l.Warning(err.Error()) return } buffer := make([]byte, 1500) var gw net.IP for _, addr := range s.metadata.Network.IP { if addr.Family == "ipv4" && addr.Host == "true" && addr.Gateway == "true" { gw = net.ParseIP(addr.Address) } } iface, err := net.InterfaceByName("tap" + s.name) if err != nil { l.Info(fmt.Sprintf("failed to get iface: %s", err.Error())) return } for { select { case <-s.done: return default: s.ipv4conn.SetReadDeadline(time.Now().Add(time.Second)) hdr, _, _, err := s.ipv4conn.ReadFrom(buffer) if err != nil { switch v := err.(type) { case *net.OpError: if v.Timeout() { continue } case *net.AddrError: if v.Timeout() { continue } case *net.UnknownNetworkError: if v.Timeout() { continue } default: l.Warning(err.Error()) return } } var ip4 layers.IPv4 var udp layers.UDP var dhcp4req layers.DHCPv4 parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &ip4, &udp, &dhcp4req) decoded := []gopacket.LayerType{} err = parser.DecodeLayers(buffer, &decoded) for _, layerType := range decoded { switch layerType { case layers.LayerTypeDHCPv4: if dhcp4req.Operation == layers.DHCP_MSG_REQ { dhcp4res, err := s.ServeUDPv4(&dhcp4req) if err != nil { l.Warning(err.Error()) continue } if dhcp4res == nil { // ignore empty dhcp packets continue } buf := gopacket.NewSerializeBuffer() opts := gopacket.SerializeOptions{true, true} gopacket.SerializeLayers(buf, opts, &layers.UDP{SrcPort: 67, DstPort: 68}, dhcp4res) wcm := ipv4.ControlMessage{TTL: 255} wcm.Dst = net.IPv4bcast.To4() wcm.Src = gw.To4() wcm.IfIndex = iface.Index err = s.ipv4conn.WriteTo(&ipv4.Header{Len: 20, TOS: hdr.TOS, TotalLen: 20 + int(len(buf.Bytes())), FragOff: 0, TTL: 255, Protocol: int(layers.IPProtocolUDP), Src: gw.To4(), Dst: net.IPv4bcast.To4()}, buf.Bytes(), &wcm) if err != nil { l.Warning(err.Error()) continue } } else { continue } } } } } }
func main() { defer util.Run()() 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) } 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) 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)) }