| // Copyright 2012 Google, Inc. All rights reserved. |
| // |
| // Use of this source code is governed by a BSD-style license |
| // that can be found in the LICENSE file in the root of the source |
| // tree. |
| |
| /* |
| Package gopacket provides packet decoding for the Go language. |
| |
| gopacket contains many sub-packages with additional functionality you may find |
| useful, including: |
| |
| * layers: You'll probably use this every time. This contains of the logic |
| built into gopacket for decoding packet protocols. Note that all example |
| code below assumes that you have imported both gopacket and |
| gopacket/layers. |
| * pcap: C bindings to use libpcap to read packets off the wire. |
| * pfring: C bindings to use PF_RING to read packets off the wire. |
| * afpacket: C bindings for Linux's AF_PACKET to read packets off the wire. |
| * tcpassembly: TCP stream reassembly |
| |
| Also, if you're looking to dive right into code, see the examples subdirectory |
| for numerous simple binaries built using gopacket libraries. |
| |
| Minimum go version required is 1.5 except for pcapgo/EthernetHandle, afpacket, |
| and bsdbpf which need at least 1.7 due to x/sys/unix dependencies. |
| |
| Basic Usage |
| |
| gopacket takes in packet data as a []byte and decodes it into a packet with |
| a non-zero number of "layers". Each layer corresponds to a protocol |
| within the bytes. Once a packet has been decoded, the layers of the packet |
| can be requested from the packet. |
| |
| // Decode a packet |
| packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Default) |
| // Get the TCP layer from this packet |
| if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil { |
| fmt.Println("This is a TCP packet!") |
| // Get actual TCP data from this layer |
| tcp, _ := tcpLayer.(*layers.TCP) |
| fmt.Printf("From src port %d to dst port %d\n", tcp.SrcPort, tcp.DstPort) |
| } |
| // Iterate over all layers, printing out each layer type |
| for _, layer := range packet.Layers() { |
| fmt.Println("PACKET LAYER:", layer.LayerType()) |
| } |
| |
| Packets can be decoded from a number of starting points. Many of our base |
| types implement Decoder, which allow us to decode packets for which |
| we don't have full data. |
| |
| // Decode an ethernet packet |
| ethP := gopacket.NewPacket(p1, layers.LayerTypeEthernet, gopacket.Default) |
| // Decode an IPv6 header and everything it contains |
| ipP := gopacket.NewPacket(p2, layers.LayerTypeIPv6, gopacket.Default) |
| // Decode a TCP header and its payload |
| tcpP := gopacket.NewPacket(p3, layers.LayerTypeTCP, gopacket.Default) |
| |
| |
| Reading Packets From A Source |
| |
| Most of the time, you won't just have a []byte of packet data lying around. |
| Instead, you'll want to read packets in from somewhere (file, interface, etc) |
| and process them. To do that, you'll want to build a PacketSource. |
| |
| First, you'll need to construct an object that implements the PacketDataSource |
| interface. There are implementations of this interface bundled with gopacket |
| in the gopacket/pcap and gopacket/pfring subpackages... see their documentation |
| for more information on their usage. Once you have a PacketDataSource, you can |
| pass it into NewPacketSource, along with a Decoder of your choice, to create |
| a PacketSource. |
| |
| Once you have a PacketSource, you can read packets from it in multiple ways. |
| See the docs for PacketSource for more details. The easiest method is the |
| Packets function, which returns a channel, then asynchronously writes new |
| packets into that channel, closing the channel if the packetSource hits an |
| end-of-file. |
| |
| packetSource := ... // construct using pcap or pfring |
| for packet := range packetSource.Packets() { |
| handlePacket(packet) // do something with each packet |
| } |
| |
| You can change the decoding options of the packetSource by setting fields in |
| packetSource.DecodeOptions... see the following sections for more details. |
| |
| |
| Lazy Decoding |
| |
| gopacket optionally decodes packet data lazily, meaning it |
| only decodes a packet layer when it needs to handle a function call. |
| |
| // 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. |
| // If no IPv4 layer was found, the whole packet will be decoded looking for |
| // it. |
| ip4 := packet.Layer(layers.LayerTypeIPv4) |
| // Decode all layers and return them. The layers up to the first IPv4 layer |
| // are already decoded, and will not require decoding a second time. |
| layers := packet.Layers() |
| |
| Lazily-decoded packets are not concurrency-safe. Since layers have not all been |
| decoded, each call to Layer() or Layers() has the potential to mutate the packet |
| in order to decode the next layer. If a packet is used |
| in multiple goroutines concurrently, don't use gopacket.Lazy. Then gopacket |
| will decode the packet fully, and all future function calls won't mutate the |
| object. |
| |
| |
| NoCopy Decoding |
| |
| By default, gopacket will copy the slice passed to NewPacket and store the |
| copy within the packet, so future mutations to the bytes underlying the slice |
| don't affect the packet and its layers. If you can guarantee that the |
| underlying slice bytes won't be changed, you can use NoCopy to tell |
| gopacket.NewPacket, and it'll use the passed-in slice itself. |
| |
| // 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) |
| } |
| |
| The fastest method of decoding is to use both Lazy and NoCopy, but note from |
| the many caveats above that for some implementations either or both may be |
| dangerous. |
| |
| |
| Pointers To Known Layers |
| |
| During decoding, certain layers are stored in the packet as well-known |
| layer types. For example, IPv4 and IPv6 are both considered NetworkLayer |
| layers, while TCP and UDP are both TransportLayer layers. We support 4 |
| layers, corresponding to the 4 layers of the TCP/IP layering scheme (roughly |
| anagalous to layers 2, 3, 4, and 7 of the OSI model). To access these, |
| you can use the packet.LinkLayer, packet.NetworkLayer, |
| packet.TransportLayer, and packet.ApplicationLayer functions. Each of |
| these functions returns a corresponding interface |
| (gopacket.{Link,Network,Transport,Application}Layer). The first three |
| provide methods for getting src/dst addresses for that particular layer, |
| while the final layer provides a Payload function to get payload data. |
| This is helpful, for example, to get payloads for all packets regardless |
| of their underlying data type: |
| |
| // Get packets from some source |
| for packet := range someSource { |
| if app := packet.ApplicationLayer(); app != nil { |
| if strings.Contains(string(app.Payload()), "magic string") { |
| fmt.Println("Found magic string in a packet!") |
| } |
| } |
| } |
| |
| A particularly useful layer is ErrorLayer, which is set whenever there's |
| an error parsing part of the packet. |
| |
| packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Default) |
| if err := packet.ErrorLayer(); err != nil { |
| fmt.Println("Error decoding some part of the packet:", err) |
| } |
| |
| Note that we don't return an error from NewPacket because we may have decoded |
| a number of layers successfully before running into our erroneous layer. You |
| may still be able to get your Ethernet and IPv4 layers correctly, even if |
| your TCP layer is malformed. |
| |
| |
| Flow And Endpoint |
| |
| gopacket has two useful objects, Flow and Endpoint, for communicating in a protocol |
| independent manner the fact that a packet is coming from A and going to B. |
| The general layer types LinkLayer, NetworkLayer, and TransportLayer all provide |
| methods for extracting their flow information, without worrying about the type |
| of the underlying Layer. |
| |
| A Flow is a simple object made up of a set of two Endpoints, one source and one |
| destination. It details the sender and receiver of the Layer of the Packet. |
| |
| An Endpoint is a hashable representation of a source or destination. For |
| example, for LayerTypeIPv4, an Endpoint contains the IP address bytes for a v4 |
| IP packet. A Flow can be broken into Endpoints, and Endpoints can be combined |
| into Flows: |
| |
| packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy) |
| netFlow := packet.NetworkLayer().NetworkFlow() |
| src, dst := netFlow.Endpoints() |
| reverseFlow := gopacket.NewFlow(dst, src) |
| |
| Both Endpoint and Flow objects can be used as map keys, and the equality |
| operator can compare them, so you can easily group together all packets |
| based on endpoint criteria: |
| |
| flows := map[gopacket.Endpoint]chan gopacket.Packet |
| packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy) |
| // Send all TCP packets to channels based on their destination port. |
| if tcp := packet.Layer(layers.LayerTypeTCP); tcp != nil { |
| flows[tcp.TransportFlow().Dst()] <- packet |
| } |
| // Look for all packets with the same source and destination network address |
| if net := packet.NetworkLayer(); net != nil { |
| src, dst := net.NetworkFlow().Endpoints() |
| if src == dst { |
| fmt.Println("Fishy packet has same network source and dst: %s", src) |
| } |
| } |
| // Find all packets coming from UDP port 1000 to UDP port 500 |
| interestingFlow := gopacket.NewFlow(layers.NewUDPPortEndpoint(1000), layers.NewUDPPortEndpoint(500)) |
| if t := packet.NetworkLayer(); t != nil && t.TransportFlow() == interestingFlow { |
| fmt.Println("Found that UDP flow I was looking for!") |
| } |
| |
| For load-balancing purposes, both Flow and Endpoint have FastHash() functions, |
| which provide quick, non-cryptographic hashes of their contents. Of particular |
| importance is the fact that Flow FastHash() is symmetric: A->B will have the same |
| hash as B->A. An example usage could be: |
| |
| channels := [8]chan gopacket.Packet |
| for i := 0; i < 8; i++ { |
| channels[i] = make(chan gopacket.Packet) |
| go packetHandler(channels[i]) |
| } |
| for packet := range getPackets() { |
| if net := packet.NetworkLayer(); net != nil { |
| channels[int(net.NetworkFlow().FastHash()) & 0x7] <- packet |
| } |
| } |
| |
| This allows us to split up a packet stream while still making sure that each |
| stream sees all packets for a flow (and its bidirectional opposite). |
| |
| |
| Implementing Your Own Decoder |
| |
| If your network has some strange encapsulation, you can implement your own |
| decoder. In this example, we handle Ethernet packets which are encapsulated |
| in a 4-byte header. |
| |
| // Create a layer type, should be unique and high, so it doesn't conflict, |
| // giving it a name and a decoder to use. |
| 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 } |
| |
| // Now implement a decoder... this one strips off the first 4 bytes of the |
| // packet. |
| func decodeMyLayer(data []byte, p gopacket.PacketBuilder) error { |
| // Create my layer |
| p.AddLayer(&MyLayer{data[:4], data[4:]}) |
| // Determine how to handle the rest of the packet |
| return p.NextDecoder(layers.LayerTypeEthernet) |
| } |
| |
| // Finally, decode your packets: |
| p := gopacket.NewPacket(data, MyLayerType, gopacket.Lazy) |
| |
| See the docs for Decoder and PacketBuilder for more details on how coding |
| decoders works, or look at RegisterLayerType and RegisterEndpointType to see how |
| to add layer/endpoint types to gopacket. |
| |
| |
| Fast Decoding With DecodingLayerParser |
| |
| TLDR: DecodingLayerParser takes about 10% of the time as NewPacket to decode |
| packet data, but only for known packet stacks. |
| |
| Basic decoding using gopacket.NewPacket or PacketSource.Packets is somewhat slow |
| due to its need to allocate a new packet and every respective layer. It's very |
| versatile and can handle all known layer types, but sometimes you really only |
| care about a specific set of layers regardless, so that versatility is wasted. |
| |
| DecodingLayerParser avoids memory allocation altogether by decoding packet |
| layers directly into preallocated objects, which you can then reference to get |
| the packet's information. A quick example: |
| |
| func main() { |
| var eth layers.Ethernet |
| var ip4 layers.IPv4 |
| var ip6 layers.IPv6 |
| var tcp layers.TCP |
| parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, ð, &ip4, &ip6, &tcp) |
| decoded := []gopacket.LayerType{} |
| for packetData := range somehowGetPacketData() { |
| if err := parser.DecodeLayers(packetData, &decoded); err != nil { |
| fmt.Fprintf(os.Stderr, "Could not decode layers: %v\n", err) |
| continue |
| } |
| for _, layerType := range decoded { |
| switch layerType { |
| case layers.LayerTypeIPv6: |
| fmt.Println(" IP6 ", ip6.SrcIP, ip6.DstIP) |
| case layers.LayerTypeIPv4: |
| fmt.Println(" IP4 ", ip4.SrcIP, ip4.DstIP) |
| } |
| } |
| } |
| } |
| |
| The important thing to note here is that the parser is modifying the passed in |
| layers (eth, ip4, ip6, tcp) instead of allocating new ones, thus greatly |
| speeding up the decoding process. It's even branching based on layer type... |
| it'll handle an (eth, ip4, tcp) or (eth, ip6, tcp) stack. However, it won't |
| handle any other type... since no other decoders were passed in, an (eth, ip4, |
| udp) stack will stop decoding after ip4, and only pass back [LayerTypeEthernet, |
| LayerTypeIPv4] through the 'decoded' slice (along with an error saying it can't |
| decode a UDP packet). |
| |
| Unfortunately, not all layers can be used by DecodingLayerParser... only those |
| implementing the DecodingLayer interface are usable. Also, it's possible to |
| create DecodingLayers that are not themselves Layers... see |
| layers.IPv6ExtensionSkipper for an example of this. |
| |
| |
| Creating Packet Data |
| |
| As well as offering the ability to decode packet data, gopacket will allow you |
| to create packets from scratch, as well. A number of gopacket layers implement |
| the SerializableLayer interface; these layers can be serialized to a []byte in |
| the following manner: |
| |
| ip := &layers.IPv4{ |
| SrcIP: net.IP{1, 2, 3, 4}, |
| DstIP: net.IP{5, 6, 7, 8}, |
| // etc... |
| } |
| buf := gopacket.NewSerializeBuffer() |
| opts := gopacket.SerializeOptions{} // See SerializeOptions for more details. |
| err := ip.SerializeTo(buf, opts) |
| if err != nil { panic(err) } |
| fmt.Println(buf.Bytes()) // prints out a byte slice containing the serialized IPv4 layer. |
| |
| SerializeTo PREPENDS the given layer onto the SerializeBuffer, and they treat |
| the current buffer's Bytes() slice as the payload of the serializing layer. |
| Therefore, you can serialize an entire packet by serializing a set of layers in |
| reverse order (Payload, then TCP, then IP, then Ethernet, for example). The |
| SerializeBuffer's SerializeLayers function is a helper that does exactly that. |
| |
| To generate a (empty and useless, because no fields are set) |
| Ethernet(IPv4(TCP(Payload))) packet, for example, you can run: |
| |
| buf := gopacket.NewSerializeBuffer() |
| opts := gopacket.SerializeOptions{} |
| gopacket.SerializeLayers(buf, opts, |
| &layers.Ethernet{}, |
| &layers.IPv4{}, |
| &layers.TCP{}, |
| gopacket.Payload([]byte{1, 2, 3, 4})) |
| packetData := buf.Bytes() |
| |
| A Final Note |
| |
| If you use gopacket, you'll almost definitely want to make sure gopacket/layers |
| is imported, since when imported it sets all the LayerType variables and fills |
| in a lot of interesting variables/maps (DecodersByLayerName, etc). Therefore, |
| it's recommended that even if you don't use any layers functions directly, you still import with: |
| |
| import ( |
| _ "github.com/google/gopacket/layers" |
| ) |
| */ |
| package gopacket |