Repository: ghedo/go.pkt Branch: master Commit: c97f47ad982f Files: 63 Total size: 261.3 KB Directory structure: gitextract_y_3guso3/ ├── .travis.yml ├── COPYING ├── README.md ├── capture/ │ ├── capture.go │ ├── file/ │ │ ├── capture.go │ │ ├── capture_test.go │ │ └── capture_test.pcap │ └── pcap/ │ ├── capture.go │ └── capture_test.go ├── examples/ │ ├── arp/ │ │ └── main.go │ ├── bpf_asm/ │ │ ├── Makefile │ │ ├── bpf_asm.l │ │ └── bpf_asm.y │ ├── dump/ │ │ └── main.go │ ├── ping/ │ │ └── main.go │ ├── route/ │ │ └── main.go │ ├── syn_scan/ │ │ └── main.go │ ├── tracereply/ │ │ └── main.go │ └── traceroute/ │ └── main.go ├── filter/ │ ├── bpf_builder.go │ ├── bpf_builder_test.go │ ├── bpf_filter.c │ ├── bpf_filter.go │ ├── bpf_filter.h │ ├── bpf_filter_test.go │ └── pcap.go ├── go.mod ├── go.sum ├── layers/ │ ├── layers.go │ └── layers_test.go ├── network/ │ └── network.go ├── packet/ │ ├── arp/ │ │ ├── pkt.go │ │ └── pkt_test.go │ ├── buffer.go │ ├── eth/ │ │ ├── pkt.go │ │ └── pkt_test.go │ ├── icmpv4/ │ │ ├── pkt.go │ │ └── pkt_test.go │ ├── icmpv6/ │ │ ├── pkt.go │ │ └── pkt_test.go │ ├── ipv4/ │ │ ├── pkt.go │ │ └── pkt_test.go │ ├── ipv6/ │ │ ├── pkt.go │ │ └── pkt_test.go │ ├── llc/ │ │ ├── pkt.go │ │ └── pkt_test.go │ ├── packet.go │ ├── radiotap/ │ │ ├── pkt.go │ │ └── pkt_test.go │ ├── raw/ │ │ ├── pkt.go │ │ └── pkt_test.go │ ├── sll/ │ │ ├── pkt.go │ │ └── pkt_test.go │ ├── snap/ │ │ ├── pkt.go │ │ └── pkt_test.go │ ├── tcp/ │ │ ├── pkt.go │ │ └── pkt_test.go │ ├── udp/ │ │ ├── pkt.go │ │ └── pkt_test.go │ └── vlan/ │ ├── pkt.go │ └── pkt_test.go └── routing/ ├── routing.go └── routing_linux.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .travis.yml ================================================ language: go sudo: false addons: apt: packages: - libpcap0.8-dev go: - "1.11" - "tip" env: - GO111MODULE=on script: - go build ./... - go test ./... ================================================ FILE: COPYING ================================================ Copyright (c) 2014, Alessandro Ghedini All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ go.pkt ====== ![Travis CI](https://secure.travis-ci.org/ghedo/go.pkt.png) **go.pkt** provides Go libraries for capturing, injecting, filtering, encoding and decoding network packets. * [capture][capture]: provides the basic interface for packet capturing and injection. Different implementations ("pcap", "file", ...) are provided as subpackages. * [filter][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][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][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][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][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. [capture]: http://godoc.org/github.com/ghedo/go.pkt/capture [filter]: http://godoc.org/github.com/ghedo/go.pkt/filter [packet]: http://godoc.org/github.com/ghedo/go.pkt/packet [layers]: http://godoc.org/github.com/ghedo/go.pkt/layers [network]: http://godoc.org/github.com/ghedo/go.pkt/network [routing]: http://godoc.org/github.com/ghedo/go.pkt/routing ## Getting Started ### Capturing 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. ```go 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 } ``` ### Injection 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). ```go 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) } ``` ### Filtering 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. ```go // 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 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`). ```go // 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) ``` ### Decoding 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). ```go // 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) ``` ### Network 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. ```go 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) ``` ### Routing TODO For more examples have a look at the [examples](examples/) directory in the source repository. ## Dependencies * `libpcap` ## Copyright Copyright (C) 2014 Alessandro Ghedini See COPYING for the license. ================================================ FILE: capture/capture.go ================================================ /* * Network packet analysis framework. * * Copyright (c) 2014, Alessandro Ghedini * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // Provides the basic interface for packet capturing and injection. Different // implementations ("pcap", "file", ...) are provided as subpackages. package capture import "github.com/ghedo/go.pkt/filter" import "github.com/ghedo/go.pkt/packet" type Handle interface { LinkType() packet.Type SetMTU(mtu int) error SetPromiscMode(promisc bool) error SetMonitorMode(monitor bool) error ApplyFilter(filter *filter.Filter) error Activate() error Capture() ([]byte, error) Inject(buf []byte) error Close() } ================================================ FILE: capture/file/capture.go ================================================ /* * Network packet analysis framework. * * Copyright (c) 2014, Alessandro Ghedini * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // Provides native packet capturing and injection on pcap dump files without // requiring the libpcap library. package file import "bytes" import "encoding/binary" import "fmt" import "io" import "os" import "github.com/ghedo/go.pkt/filter" import "github.com/ghedo/go.pkt/packet" type Handle struct { File string file *os.File out *os.File order binary.ByteOrder link uint32 mtu uint32 filter *filter.Filter } var BigEndian = []byte{0xa1, 0xb2, 0xc3, 0xd4} var LittleEndian = []byte{0xd4, 0xc3, 0xb2, 0xa1} // Create a new capture handle from the given dump file. This will either open // the file if it exists, or create a new one. func Open(file_name string) (*Handle, error) { handle := &Handle{ File: file_name } open := open_file if _, err := os.Stat(file_name); os.IsNotExist(err) { open = create_file } file, err := open(file_name) if err != nil { return nil, err } handle.file = file handle.file.Seek(0, 0) magic := make([]byte, 4) file.Read(magic) switch { case bytes.Equal(magic, BigEndian): handle.order = binary.BigEndian case bytes.Equal(magic, LittleEndian): handle.order = binary.LittleEndian default: handle.file.Close() return nil, fmt.Errorf("Invalid file") } var ver_maj, ver_min uint16 var discard, mtu, link_type uint32 binary.Read(file, handle.order, &ver_maj) binary.Read(file, handle.order, &ver_min) binary.Read(file, handle.order, &discard) binary.Read(file, handle.order, &discard) binary.Read(file, handle.order, &mtu) binary.Read(file, handle.order, &link_type) handle.link = link_type handle.mtu = mtu /* * Use a different file handle for injecting packages so that we don't * need to seek back and forth for capturing and injecting */ handle.out, _ = open_file(file_name) handle.out.Seek(0, 2) return handle, nil } func create_file(file_name string) (*os.File, error) { file, err := os.Create(file_name) if err != nil { return nil, fmt.Errorf("Could not create file: %s", err) } file.Write(BigEndian) /* endiannes */ binary.Write(file, binary.BigEndian, uint16(2)) /* ver major */ binary.Write(file, binary.BigEndian, uint16(4)) /* ver minor */ binary.Write(file, binary.BigEndian, uint32(0)) binary.Write(file, binary.BigEndian, uint32(0)) binary.Write(file, binary.BigEndian, uint32(0x7fff)) /* MTU */ binary.Write(file, binary.BigEndian, uint32(1)) /* link type */ return file, nil } func open_file(file_name string) (*os.File, error) { file, err := os.OpenFile(file_name, os.O_RDWR, 0644); if err != nil { return nil, fmt.Errorf("Could not open file: %s", err) } return file, nil } // Return the link type of the capture handle (that is, the type of packets that // come out of the packet source). func (h *Handle) LinkType() packet.Type { return packet.LinkType(h.link) } // Not supported. func (h *Handle) SetMTU(mtu int) error { return fmt.Errorf("Unsupported") } // Not supported. func (h *Handle) SetPromiscMode(promisc bool) error { return fmt.Errorf("Unsupported") } // Not supported. func (h *Handle) SetMonitorMode(monitor bool) error { return fmt.Errorf("Unsupported") } // Apply the given filter it to the packet source. Only packets that match this // filter will be captured. func (h *Handle) ApplyFilter(filter *filter.Filter) error { if !filter.Validate() { return fmt.Errorf("Invalid filter") } h.filter = filter return nil } // Activate the capture handle (this is not needed for the file capture handle, // but you may want to call it anyway in order to make switching to different // packet sources easier). func (h *Handle) Activate() error { return nil } // Capture a single packet from the packet source. If no packet is available // (i.e. if the end of the dump file has been reached) it will return a nil // slice. func (h *Handle) Capture() ([]byte, error) { var buf []byte var sec, usec, caplen, wirelen uint32 for { binary.Read(h.file, h.order, &sec) binary.Read(h.file, h.order, &usec) binary.Read(h.file, h.order, &caplen) binary.Read(h.file, h.order, &wirelen) if caplen == 0 { return nil, nil } buf = make([]byte, int(caplen)) _, err := h.file.Read(buf) if err == io.EOF { return nil, nil } if err != nil { return nil, fmt.Errorf("Could not capture: %s", err) } if h.filter != nil && !h.filter.Match(buf) { continue } break } return buf, nil } // Inject a packet in the packet source. This will automatically append packets // at the end of the dump file, instead of truncating it. func (h *Handle) Inject(buf []byte) error { var sec, usec, caplen, wirelen uint32 sec = 0 usec = 0 caplen = uint32(len(buf)) wirelen = caplen binary.Write(h.out, h.order, sec) binary.Write(h.out, h.order, usec) binary.Write(h.out, h.order, caplen) binary.Write(h.out, h.order, wirelen) n, err := h.out.Write(buf) if err != nil || n < len(buf) { return fmt.Errorf("Could not write packet: %s", err) } return nil } // Close the packet source. func (h *Handle) Close() { h.file.Close() h.out.Close() } ================================================ FILE: capture/file/capture_test.go ================================================ /* * Network packet analysis framework. * * Copyright (c) 2014, Alessandro Ghedini * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package file_test import "log" import "testing" import "github.com/ghedo/go.pkt/capture/file" import "github.com/ghedo/go.pkt/filter" func TestCapture(t *testing.T) { src, err := file.Open("capture_test.pcap") if err != nil { t.Fatalf("Error opening: %s", err) } defer src.Close() var count uint64 for { buf, err := src.Capture() if err != nil { t.Fatalf("Error reading: %s", err) } if buf == nil { break } count++ } if count != 16 { t.Fatalf("Count mismatch: %d", count) } } func TestCaptureFilter(t *testing.T) { src, err := file.Open("capture_test.pcap") if err != nil { t.Fatalf("Error opening: %s", err) } defer src.Close() flt, err := filter.Compile("arp", src.LinkType(), false) if err != nil { t.Fatalf("Error parsing filter: %s", err) } defer flt.Cleanup() err = src.ApplyFilter(flt) if err != nil { t.Fatalf("Error applying filter: %s", err) } var count uint64 for { buf, err := src.Capture() if err != nil { t.Fatalf("Error reading: %s %d", err, count) } if buf == nil { break } count++ } if count != 2 { t.Fatalf("Count mismatch: %d", count) } } func TestInject(t *testing.T) { src, err := file.Open("capture_test.pcap") if err != nil { t.Fatalf("Error opening: %s", err) } defer src.Close() dst, err := file.Open("inject_test.pcap") if err != nil { t.Fatalf("Error opening: %s", err) } defer dst.Close() var count uint64 for { buf, err := src.Capture() if err != nil { t.Fatalf("Error reading: %s", err) } if buf == nil { break } err = dst.Inject(buf) if err != nil { t.Fatalf("Error writing: %s", err) } count++ } if count != 16 { t.Fatalf("Count mismatch: %d", count) } } func ExampleCapture() { src, err := file.Open("/path/to/file/dump.pcap") 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) } if buf == nil { break } log.Println("PACKET!!!") // do something with the packet } } func ExampleInject() { dst, err := file.Open("/path/to/file/dump.pcap") 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) } } ================================================ FILE: capture/pcap/capture.go ================================================ /* * Network packet analysis framework. * * Copyright (c) 2014, Alessandro Ghedini * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // Provides packet capturing and injection on live network interfaces via // libpcap. package pcap // #cgo LDFLAGS: -lpcap // #include // #include import "C" import "fmt" import "unsafe" import "github.com/ghedo/go.pkt/filter" import "github.com/ghedo/go.pkt/packet" type Handle struct { Device string pcap *C.pcap_t } // Create a new capture handle from the given network interface. Noe that this // may require root privileges. func Open(dev_name string) (*Handle, error) { handle := &Handle{ Device: dev_name } dev_str := C.CString(dev_name) defer C.free(unsafe.Pointer(dev_str)) err_str := (*C.char)(C.calloc(256, 1)) defer C.free(unsafe.Pointer(err_str)) handle.pcap = C.pcap_create(dev_str, err_str) if handle == nil { return nil, fmt.Errorf( "Could not open device: %s", C.GoString(err_str), ) } return handle, nil } // Return the link type of the capture handle (that is, the type of packets that // come out of the packet source). func (h *Handle) LinkType() packet.Type { return packet.LinkType(uint32(C.pcap_datalink(h.pcap))) } func (h *Handle) SetMTU(mtu int) error { err := C.pcap_set_snaplen(h.pcap, C.int(mtu)) if err < 0 { return fmt.Errorf("Handle already active") } return nil } // Enable/disable promiscuous mode. func (h *Handle) SetPromiscMode(promisc bool) error { var promisc_int C.int if promisc { promisc_int = 1 } else { promisc_int = 0 } err := C.pcap_set_promisc(h.pcap, promisc_int) if err < 0 { return fmt.Errorf("Handle already active") } return nil } // Enable/disable monitor mode. This is only relevant to RF-based packet sources // (e.g. a WiFi or Bluetooth network interface) func (h *Handle) SetMonitorMode(monitor bool) error { var rfmon_int C.int if monitor { rfmon_int = 1 } else { rfmon_int = 0 } err := C.pcap_set_rfmon(h.pcap, rfmon_int) if err < 0 { return fmt.Errorf("Handle already active") } return nil } // Apply the given filter it to the packet source. Only packets that match this // filter will be captured. func (h *Handle) ApplyFilter(filter *filter.Filter) error { if !filter.Validate() { return fmt.Errorf("Invalid filter") } err_str := (*C.char)(C.calloc(256, 1)) defer C.free(unsafe.Pointer(err_str)) dev_str := C.CString(h.Device) defer C.free(unsafe.Pointer(dev_str)) err := C.pcap_setfilter(h.pcap, (*C.struct_bpf_program)(filter.Program())) if err < 0 { return fmt.Errorf("Could not set filter: %s", h.get_error()) } return nil } // Activate the packet source. Note that after calling this method it will not // be possible to change the packet source configuration (MTU, promiscuous mode, // monitor mode, ...) func (h *Handle) Activate() error { err := C.pcap_activate(h.pcap) if err < 0 { return fmt.Errorf("Could not activate: %s", h.get_error()) } return nil } // Capture a single packet from the packet source. This will block until a // packet is received. func (h *Handle) Capture() ([]byte, error) { var buf *C.u_char var pkt_hdr *C.struct_pcap_pkthdr for { err := C.pcap_next_ex(h.pcap, &pkt_hdr, &buf) switch err { case -2: return nil, nil case -1: return nil, fmt.Errorf( "Could not read packet: %s", h.get_error(), ) case 0: continue case 1: return C.GoBytes(unsafe.Pointer(buf), C.int(pkt_hdr.len)), nil } } return nil, fmt.Errorf("WTF") } // Inject a packet in the packet source. func (h *Handle) Inject(buf []byte) error { cbuf := (*C.u_char)(&buf[0]) blen := C.int(len(buf)) err := C.pcap_sendpacket(h.pcap, cbuf, blen) if err < 0 { return fmt.Errorf("Could not inject packet: %s", h.get_error()) } return nil } // Close the packet source. func (h *Handle) Close() { C.pcap_close(h.pcap) } func (h *Handle) get_error() error { err_str := C.pcap_geterr(h.pcap) return fmt.Errorf(C.GoString(err_str)) } ================================================ FILE: capture/pcap/capture_test.go ================================================ /* * Network packet analysis framework. * * Copyright (c) 2014, Alessandro Ghedini * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package pcap_test import "log" import "github.com/ghedo/go.pkt/capture/pcap" func ExampleCapture() { 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.Printf("PACKET!!! %v", buf) // do something with the packet } } func ExampleInject() { 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) } } ================================================ FILE: examples/arp/main.go ================================================ /* * Network packet analysis framework. * * Copyright (c) 2014, Alessandro Ghedini * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package main import "log" import "net" import "time" import "github.com/docopt/docopt-go" import "github.com/ghedo/go.pkt/capture/pcap" import "github.com/ghedo/go.pkt/packet/eth" import "github.com/ghedo/go.pkt/packet/arp" import "github.com/ghedo/go.pkt/network" import "github.com/ghedo/go.pkt/routing" func main() { log.SetFlags(0) usage := `Usage: arp Resolve the given IP address using ARP.` args, err := docopt.Parse(usage, nil, true, "", false) if err != nil { log.Fatalf("Invalid arguments: %s", err) } addr := args[""].(string) addr_ip := net.ParseIP(addr) timeout := 5 * time.Second route, err := routing.RouteTo(addr_ip) if err != nil { log.Fatalf("Error: %s", err) } if route == nil { log.Println("No route found") } c, err := pcap.Open(route.Iface.Name) if err != nil { log.Fatalf("Error opening interface: %s", err) } defer c.Close() err = c.Activate() if err != nil { log.Fatalf("Error activating source: %s", err) } eth_pkt := eth.Make() eth_pkt.SrcAddr = route.Iface.HardwareAddr eth_pkt.DstAddr, _ = net.ParseMAC("ff:ff:ff:ff:ff:ff") arp_pkt := arp.Make() arp_pkt.HWSrcAddr = route.Iface.HardwareAddr arp_pkt.HWDstAddr, _ = net.ParseMAC("00:00:00:00:00:00") arp_pkt.ProtoSrcAddr, _ = route.GetIfaceIPv4Addr() arp_pkt.ProtoDstAddr = addr_ip pkt, err := network.SendRecv(c, timeout, eth_pkt, arp_pkt) if err != nil { log.Fatal(err) } log.Println(pkt.Payload().(*arp.Packet).HWSrcAddr) } ================================================ FILE: examples/bpf_asm/Makefile ================================================ all: bpf_asm bpf_asm: bpf_asm.nn.go y.go go build -o bpf_asm bpf_asm.nn.go y.go y.go: bpf_asm.y go tool yacc bpf_asm.y bpf_asm.nn.go: bpf_asm.l nex bpf_asm.l ================================================ FILE: examples/bpf_asm/bpf_asm.l ================================================ /ldb/ { return OP_LDB } /ldh/ { return OP_LDH } /ld/ { return OP_LD } /ldi/ { return OP_LDI } /ldx/ { return OP_LDX } /ldxi/ { return OP_LDXI } /ldxb/ { return OP_LDXB } /st/ { return OP_ST } /stx/ { return OP_STX } /jmp/ { return OP_JMP } /ja/ { return OP_JMP } /jeq/ { return OP_JEQ } /jneq/ { return OP_JNEQ } /jne/ { return OP_JNEQ } /jlt/ { return OP_JLT } /jle/ { return OP_JLE } /jgt/ { return OP_JGT } /jge/ { return OP_JGE } /jset/ { return OP_JSET } /add/ { return OP_ADD } /sub/ { return OP_SUB } /mul/ { return OP_MUL } /div/ { return OP_DIV } /neg/ { return OP_NEG } /and/ { return OP_AND } /or/ { return OP_OR } /lsh/ { return OP_LSH } /rsh/ { return OP_RSH } /ret/ { return OP_RET } /tax/ { return OP_TAX } /txa/ { return OP_TXA } /#?len/ { return K_PKT_LEN } /#?proto/ { return K_PROTO } /#?type/ { return K_TYPE } /#?poff/ { return K_POFF } /#?ifidx/ { return K_IFIDX } /#?nla/ { return K_NLATTR } /#?nlan/ { return K_NLATTR_NEST } /#?mark/ { return K_MARK } /#?queue/ { return K_QUEUE } /#?hatype/ { return K_HATYPE } /#?rxhash/ { return K_RXHASH } /#?cpu/ { return K_CPU } /#?vlan_tci/ { return K_VLANT } /#?vlan_pr/ { return K_VLANP } /:/ { return ':' } /,/ { return ',' } /#/ { return '#' } /%/ { return '%' } /\[/ { return '[' } /\]/ { return ']' } /\(/ { return '(' } /\)/ { return ')' } /x/ { return 'x' } /a/ { return 'a' } /\+/ { return '+' } /M/ { return 'M' } /\*/ { return '*' } /&/ { return '&' } /([0][x][a-fA-F0-9]+)/ { str := strings.TrimPrefix(yylex.Text(), "0x") num, _ := strconv.ParseUint(str, 16, 32) lval.number = uint32(num) return number } /([0][b][0-1]+)/ { str := strings.TrimPrefix(yylex.Text(), "0b") num, _ := strconv.ParseUint(str, 2, 32) lval.number = uint32(num) return number } /(([0])|([-+]?[1-9][0-9]*))/ { num, _ := strconv.ParseUint(yylex.Text(), 10, 32) lval.number = uint32(num) return number } /([0][0-9]+)/ { str := strings.TrimPrefix(yylex.Text(), "0") num, _ := strconv.ParseUint(str, 8, 32) lval.number = uint32(num) return number } /[a-zA-Z_][a-zA-Z0-9_]+/ { lval.label = yylex.Text() return label } // package main import "log" import "strconv" import "os" import "github.com/docopt/docopt-go" import "github.com/ghedo/go.pkt/filter" var bld *filter.Builder func main() { log.SetFlags(0) usage := `Usage: bpf_asm ` args, err := docopt.Parse(usage, nil, true, "", false) if err != nil { log.Fatalf("Invalid arguments: %s", err) } file, err := os.Open(args[""].(string)) if err != nil { log.Fatalf("Error opening file: %s", err) } bld = filter.NewBuilder() lex := NewLexer(file) yyParse(lex) flt := bld.Build() if !flt.Validate() { log.Fatalf("Invalid filter") } log.Println(flt) } ================================================ FILE: examples/bpf_asm/bpf_asm.y ================================================ %{ package main import "github.com/ghedo/go.pkt/filter" %} %union { label string number uint32 } %token OP_LDB %token OP_LDH %token OP_LD %token OP_LDI %token OP_LDX %token OP_LDXI %token OP_LDXB %token OP_ST %token OP_STX %token OP_JMP %token OP_JEQ %token OP_JNEQ %token OP_JLT %token OP_JLE %token OP_JGT %token OP_JGE %token OP_JSET %token OP_ADD %token OP_SUB %token OP_MUL %token OP_DIV %token OP_NEG %token OP_AND %token OP_OR %token OP_LSH %token OP_RSH %token OP_RET %token OP_TAX %token OP_TXA %token K_PKT_LEN %token K_PROTO %token K_TYPE %token K_POFF %token K_IFIDX %token K_NLATTR %token K_NLATTR_NEST %token K_MARK %token K_QUEUE %token K_HATYPE %token K_RXHASH %token K_CPU %token K_VLANT %token K_VLANP %token number %token label %token jtl %token jfl %token jkl %token ':' ',' '[' ']' '(' ')' 'x' 'a' '+' 'M' '*' '&' '#' '%' %type