gopacket Documentation

repository·master·Indexed 27 days ago

https://github.com/google/gopacket

A high-performance Go library for packet decoding and analysis. It provides tools for capturing network traffic, decoding protocol layers, TCP stream reassembly, and IPv4 defragmentation. Key features include support for lazy and no-copy decoding, a DecodingLayerParser for high-performance preallocated decoding, and interfaces for Link, Network, Transport, and Application layers.

Tokens
10.4K
Snippets
27
Records
56
Agent score
91%

What's inside gopacket

  1. Check Go version requirements for GoPacket

    master

    The minimum Go version required to use GoPacket is 1.5.

    However, if you intend to use the following specific packages, you must use at least Go 1.9 due to x/sys/unix dependencies:

    • pcapgo/EthernetHandle
    • afpacket
    • bsdbpf
  2. Read packets from a source using PacketSource

    master

    To read packets from an interface or file, construct a PacketSource. This typically involves using a PacketDataSource implementation (available in gopacket/pcap or gopacket/pfring). The easiest way to consume packets is via the Packets() method, which returns a channel that asynchronously writes new packets and closes when the source reaches EOF.

    packetSource := ...  // construct using pcap or pfring
      for packet := range packetSource.Packets() {
        handlePacket(packet)  // do something with each packet
      }
  3. Use DecodingLayerParser for high-performance decoding

    master

    The DecodingLayerParser provides a fast way to decode packet layers by reusing existing layer instances instead of allocating new ones for every packet. This is significantly faster than standard packet decoding.

    To use it:

    1. Initialize the parser with NewDecodingLayerParser, providing the starting LayerType and a list of DecodingLayer instances (e.g., pointers to your layer structs).
    2. Maintain a slice of LayerType to store the results of each successful decode.
    3. Call DecodeLayers(data, &decodedLayers) in your processing loop.
    4. Iterate through the decodedLayers slice and use a type switch to access the data in your layer instances.

    If the parser encounters a layer it doesn't recognize, it returns an UnsupportedLayerType error unless IgnoreUnsupported is set to true in DecodingLayerParserOptions.

    var eth layers.Ethernet
    var ip4 layers.IPv4
    var ip6 layers.IPv6
    var tcp layers.TCP
    var udp layers.UDP
    var payload gopacket.Payload
    
    // Initialize parser with the first layer type and the layer instances
    parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth, &ip4, &ip6, &tcp, &udp, &payload)
    
    // Slice to hold the sequence of decoded layer types
    decodedLayers := make([]gopacket.LayerType, 0, 10)
    
    // Decode the data
    err := parser.DecodeLayers(data, &decodedLayers)
    
    for _, typ := range decodedLayers {
    	switch typ {
    	case layers.LayerTypeEthernet:
    		fmt.Println("Eth ", eth.SrcMAC, eth.DstMAC)
    	case layers.LayerTypeIPv4:
    		fmt.Println("IP4 ", ip4.SrcIP, ip4.DstIP)
    	// ... handle other types
    	}
    }
  4. Use Lazy decoding for performance

    master

    By using gopacket.Lazy as the decoding option in NewPacket, gopacket only decodes a layer when it is explicitly requested via a function call. This can save significant processing time if you only need to inspect specific parts of a packet.

    Warning: Lazily-decoded packets are not concurrency-safe because calls to Layer() or Layers() may mutate the packet object to perform the decoding.

    // Create a packet, but don't actually decode anything yet
     packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy)
     // Now, decode the packet up to the first IPv4 layer found but no further.
     ip4 := packet.Layer(layers.LayerTypeIPv4)
     // Decode all layers and return them.
     layers := packet.Layers()
  5. Use NoCopy decoding to avoid memory allocation

    master

    If you can guarantee that the underlying byte slice provided to NewPacket will not be modified, use gopacket.NoCopy. This tells gopacket to use the passed-in slice directly instead of creating a copy, reducing memory overhead.

    // This channel returns new byte slices, each of which points to a new
     // memory location that's guaranteed immutable for the duration of the
     // packet.
     for data := range myByteSliceChannel {
       p := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy)
       doSomethingWithPacket(p)
     }
  6. Implement a custom Decoder

    master

    To handle custom encapsulation, you can implement a custom decoder by:

    1. Registering a new layer type using gopacket.RegisterLayerType.
    2. Defining a struct that implements the Layer interface (LayerType(), LayerContents(), and LayerPayload()).
    3. Writing a DecodeFunc that uses PacketBuilder.AddLayer() to add your layer and PacketBuilder.NextDecoder() to continue the chain.
    // Create a layer type
     var MyLayerType = gopacket.RegisterLayerType(12345, gopacket.LayerTypeMetadata{Name: "MyLayerType", Decoder: gopacket.DecodeFunc(decodeMyLayer)})
    
    // Implement my layer
     type MyLayer struct {
       StrangeHeader []byte
       payload []byte
     }
     func (m MyLayer) LayerType() gopacket.LayerType { return MyLayerType }
     func (m MyLayer) LayerContents() []byte { return m.StrangeHeader }
     func (m MyLayer) LayerPayload() []byte { return m.payload }
    
    // Implement a decoder
     func decodeMyLayer(data []byte, p gopacket.PacketBuilder) error {
       p.AddLayer(&MyLayer{data[:4], data[4:]})
       return p.NextDecoder(layers.LayerTypeEthernet)
     }
    
    // Decode
     p := gopacket.NewPacket(data, MyLayerType, gopacket.Lazy)
  7. Read packets using PacketSource

    master

    A PacketSource is used to read and decode packets from a PacketDataSource. There are two primary ways to consume packets:

    1. Using the Packets() channel (Convenient)

    Returns a channel of Packet objects. This is asynchronous and easy to iterate over. The channel closes when the source reaches io.EOF.

    for packet := range packetSource.Packets() {
        // handle packet
    }

    2. Using the NextPacket() method (Flexible/Fast)

    Returns the next decoded packet and an error. This is faster in tight loops and allows you to handle specific errors (like io.EOF or network errors) manually.

    for {
        packet, err := packetSource.NextPacket()
        if err == io.EOF {
            break
        }
        if err != nil {
            // handle error
            continue
        }
        // handle packet
    }
    // Using the channel approach
    for packet := range packetSource.Packets() {
        handlePacket(packet)
    }
    
    // Using the NextPacket approach
    for {
        packet, err := packetSource.NextPacket()
        if err == io.EOF {
            break
        } else if err != nil {
            log.Println("Error:", err)
            continue
        }
        handlePacket(packet)
    }
  8. Configure decoding with DecodeOptions

    master

    The DecodeOptions struct allows you to tune the performance and behavior of the packet decoder. You can use the provided package variables for common configurations:

    OptionDescription
    LazyDecodes minimum layers required per call. Faster, but not concurrency-safe.
    NoCopyDoes not copy the input buffer. Faster, but requires the underlying slice to remain immutable.
    SkipDecodeRecoveryIf true, panics during decoding are allowed to propagate instead of being caught and turned into an ErrorLayer.
    DecodeStreamsAsDatagramsEnables routing of application-level layers in the TCP decoder.

    Predefined Options:

    • gopacket.Default: Safest/slowest (Eager + Copy).
    • gopacket.Lazy: Optimized for single-threaded use.
    • gopacket.NoCopy: Optimized for immutable buffers.
  9. Configure serialization with SerializeOptions

    master

    Use SerializeOptions to control how layers are encoded during serialization:

    • FixLengths: If true, layers should fix the values for any length fields that depend on the payload.
    • ComputeChecksums: If true, layers should recompute checksums based on their payloads.
  10. Configure DecodingLayerParserOptions

    master

    You can customize the behavior of a DecodingLayerParser using the DecodingLayerParserOptions struct:

    • IgnorePanic (bool): If true, panics occurring during decoding are caught and returned as errors from DecodeLayers. If false (default), panics will propagate up the stack. Enabling this adds slight latency but increases safety.
    • IgnoreUnsupported (bool): If true, the parser stops decoding when it hits an unknown layer and returns nil instead of an UnsupportedLayerType error. If true, you must verify the contents of the decoded slice to ensure you got the layers you expected.
  11. Serialize layers to wire format using SerializeLayers

    master

    To write multiple packet layers into a byte buffer, use SerializeLayers. This function clears the provided buffer and then serializes the layers in reverse order so that they correctly wrap each other (e.g., if you pass layers A, B, and C, the resulting buffer will contain the encoding for A(B(C))).

    Note: SerializeLayers calls w.Clear(), which invalidates any byte slices previously returned by w.Bytes() from that buffer.

    buf := gopacket.NewSerializeBuffer()
    opts := gopacket.SerializeOptions{}
    // layers a, b, c are SerializableLayer implementations
    gopacket.SerializeLayers(buf, opts, a, b, c)
    firstPayload := buf.Bytes() // contains byte representation of a(b(c))