go.pkt provides Go libraries for capturing, injecting, filtering, encoding and decoding network packets.
-
capture: provides the basic interface for packet capturing and injection. Different implementations ("pcap", "file", ...) are provided as subpackages.
-
filter: provides an API for compiling and manipulating BPF filters. A filter can be either compiled from tcpdump-like expressions, or created from basic BPF instructions. Filters can then be either applied to packet sources (see the capture package) or directly run against binary data.
-
packet: provides the interfaces for implementing packet encoders and decoders. Every supported protocol implements the Packet interface as a submodule of this package (e.g. packet/ipv4, packet/tcp, ...).
-
layers: provides utility functions for encoding and decoding packets to/from binary data. Differently from the basic "packet" interface, this can encode and decode complete "stacks" of packets, instead of manipulating single ones.
-
network: provides utility functions for sending and receiving packets over the network. Basically, it hides some of the complexity of using the capture and layers packages together.
-
routing: provides network routing information about the system. It can either return all available routes or select a specific route depending on a destination address.
Packet capturing is done using a packet "source" such as a network interface or a dump file.
In the following example we create a "pcap" capture handle using the eth0
network interface, we activate it and then capture packets using the Capture()
method.
src, err := pcap.Open("eth0")
if err != nil {
log.Fatal(err)
}
defer src.Close()
// you may configure the source further, e.g. by activating
// promiscuous mode.
err = src.Activate()
if err != nil {
log.Fatal(err)
}
for {
buf, err := src.Capture()
if err != nil {
log.Fatal(err)
}
log.Println("PACKET!!!")
// do something with the packet
}
Similarly to packet capturing, packet injection requires a capture handle.
In the following example we create a capture handle like before and then use
the Inject()
method to send some data (we'll see later how to encode data in
the propert formats).
dst, err := pcap.Open("eth0")
if err != nil {
log.Fatal(err)
}
defer dst.Close()
// you may configure the source further, e.g. by activating
// promiscuous mode.
err = dst.Activate()
if err != nil {
log.Fatal(err)
}
err = dst.Inject([]byte("random data"))
if err != nil {
log.Fatal(err)
}
Packet filtering is done by creating a filter (e.g. by compiling it from an
expression) which can be either applied to a capture handle (by using the
ApplyFilter()
method) or used directly against a data buffer.
In the following example we create a filter by compiling a tcpdump-like expression and then try to match some data against it.
// Match UDP or TCP packets on top of Ethernet
flt, err := filter.Compile("udp or tcp", packet.Eth)
if err != nil {
log.Fatal(err)
}
if flt.Match([]byte("random data")) {
log.Println("MATCH!!!")
}
Encoding packets is done by using the functions provided by the layers
package.
In the following example we create an ARP packet on top of an Ethernet packet
and we encode them to binary data by using the Pack()
method. Note that you'll
need to import the packages of the protocols used (packet/eth
and packet/arp
).
// Create an Ethernet packet
eth_pkt := eth.Make()
eth_pkt.SrcAddr, _ = net.ParseMAC("4c:72:b9:54:e5:3d")
eth_pkt.DstAddr, _ = net.ParseMAC("ff:ff:ff:ff:ff:ff")
// Create an ARP packet
arp_pkt := arp.Make()
arp_pkt.HWSrcAddr, _ = net.ParseMAC("4c:72:b9:54:e5:3d")
arp_pkt.HWDstAddr, _ = net.ParseMAC("00:00:00:00:00:00")
arp_pkt.ProtoSrcAddr = net.ParseIP("192.168.1.135")
arp_pkt.ProtoDstAddr = net.ParseIP("192.168.1.254")
buf, err := layers.Pack(eth_pkt, arp_pkt)
if err != nil {
log.Fatal(err)
}
// do something with the packet
log.Println(buf)
Like encoding, decoding is done by using the functions provided by the layers
package.
The following example uses the UnpackAll()
function to decode a whole chain of
packets (e.g. ethernet -> ipv4 -> udp).
// Create the buf data
buf := []byte("random data")
// Assume Ethernet as datalink layer
pkt, err := layers.UnpackAll(buf, packet.Eth)
if err != nil {
log.Fatal(err)
}
log.Println(pkt)
Instead of using the layers and capture packages together, the network package can be used instead.
The following example creates an ARP request packet and uses SendRecv()
to
send it and receive a suitable answer.
c, err := pcap.Open("eth0")
if err != nil {
log.Fatal(err)
}
defer c.Close()
err = c.Activate()
if err != nil {
log.Fatal(err)
}
// Create an Ethernet packet
eth_pkt := eth.Make()
eth_pkt.SrcAddr, _ = net.ParseMAC("4c:72:b9:54:e5:3d")
eth_pkt.DstAddr, _ = net.ParseMAC("ff:ff:ff:ff:ff:ff")
// Create an ARP packet
arp_pkt := arp.Make()
arp_pkt.HWSrcAddr, _ = net.ParseMAC("4c:72:b9:54:e5:3d")
arp_pkt.HWDstAddr, _ = net.ParseMAC("00:00:00:00:00:00")
arp_pkt.ProtoSrcAddr = net.ParseIP("192.168.1.135")
arp_pkt.ProtoDstAddr = net.ParseIP("192.168.1.254")
rsp_pkt, err := network.SendRecv(c, 0, eth_pkt, arp_pkt)
if err != nil {
log.Fatal(err)
}
log.Println(rsp_pkt)
TODO
For more examples have a look at the examples directory in the source repository.
libpcap
Copyright (C) 2014 Alessandro Ghedini [email protected]
See COPYING for the license.