123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- // 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.
- 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() {
- err := parser.DecodeLayers(packetData, &decoded)
- 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
|