├── Makefile ├── README ├── bpf.go ├── ethernet.go ├── file.go ├── file_test.go ├── frame.go ├── ip.go ├── mac.go ├── mac_test.go ├── packet.go ├── pcap.go ├── testdata └── snmp.pcap ├── types.go └── udp.go /Makefile: -------------------------------------------------------------------------------- 1 | include $(GOROOT)/src/Make.inc 2 | 3 | TARG=github.com/davecheney/pcap 4 | GOFILES=\ 5 | packet.go\ 6 | ethernet.go\ 7 | frame.go\ 8 | ip.go\ 9 | mac.go\ 10 | pcap.go\ 11 | udp.go\ 12 | file.go\ 13 | 14 | GOFILES_darwin=\ 15 | bpf.go\ 16 | 17 | GOFILES_amd64=\ 18 | ztypes_amd64.go\ 19 | 20 | GOFILES_386=\ 21 | ztypes_386.go\ 22 | 23 | GOFILES+=$(GOFILES_$(GOOS)) 24 | 25 | GOFILES+=$(GOFILES_$(GOARCH)) 26 | 27 | CLEANFILES+=ztypes_*.go 28 | 29 | include $(GOROOT)/src/Make.pkg 30 | 31 | ztypes_386.go: types.c 32 | godefs -gpcap -f -m32 $^ | gofmt > $@ 33 | 34 | ztypes_amd64.go: types.c 35 | godefs -gpcap -f -m64 $^ | gofmt > $@ 36 | 37 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davecheney/pcap/10760a170da6335ec1a48be06a86f494b0ef74ab/README -------------------------------------------------------------------------------- /bpf.go: -------------------------------------------------------------------------------- 1 | // +build darwin freebsd netbsd openbsd 2 | 3 | package pcap 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | "unsafe" 9 | ) 10 | 11 | const ( 12 | device = "/dev/bpf0" 13 | ) 14 | 15 | type reader struct { 16 | fd int 17 | buflen int // buffer size supplied by bpf 18 | } 19 | 20 | type Capture struct { 21 | header syscall.BpfHdr 22 | payload []byte 23 | } 24 | 25 | func (r *reader) ReadPacket() (*Capture, error) { 26 | buf := make([]byte, r.buflen) 27 | n, e := syscall.Read(r.fd, buf) 28 | if e != 0 { 29 | return nil, &os.PathError{"read", device, os.Errno(e)} 30 | } 31 | buf = buf[:n] 32 | header := *(*syscall.BpfHdr)(unsafe.Pointer(&buf[0])) 33 | capture := &Capture{ 34 | header: header, 35 | payload: buf[header.Hdrlen : uint32(header.Hdrlen)+header.Caplen], 36 | } 37 | return capture, nil 38 | } 39 | 40 | func (r *reader) Close() error { 41 | syscall.Close(r.fd) 42 | return nil // TODO(dfc) 43 | } 44 | 45 | func ioctl(fd int, request, argp uintptr) error { 46 | _, _, errorp := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), request, argp) 47 | return os.NewSyscallError("ioctl", int(errorp)) 48 | } 49 | 50 | func Open() (PacketReader, error) { 51 | fd, e := syscall.Open(device, os.O_RDONLY|syscall.O_CLOEXEC, 0666) 52 | if e != 0 { 53 | return nil, &os.PathError{"open", device, os.Errno(e)} 54 | } 55 | var data [16]byte 56 | data[0] = 'e' 57 | data[1] = 'n' 58 | data[2] = '0' 59 | 60 | var len uint32 61 | var immediate uint32 = 1 62 | var promisc uint32 = 1 63 | if err := ioctl(fd, syscall.BIOCGBLEN, uintptr(unsafe.Pointer(&len))); err != nil { 64 | return nil, err 65 | } 66 | if err := ioctl(fd, syscall.BIOCSBLEN, uintptr(unsafe.Pointer(&len))); err != nil { 67 | return nil, err 68 | } 69 | if err := ioctl(fd, syscall.BIOCIMMEDIATE, uintptr(unsafe.Pointer(&immediate))); err != nil { 70 | return nil, err 71 | } 72 | if err := ioctl(fd, syscall.BIOCSETIF, uintptr(unsafe.Pointer(&data[0]))); err != nil { 73 | return nil, err 74 | } 75 | if err := ioctl(fd, syscall.BIOCPROMISC, uintptr(unsafe.Pointer(&promisc))); err != nil { 76 | return nil, err 77 | } 78 | return &reader{fd, int(len)}, nil 79 | } 80 | -------------------------------------------------------------------------------- /ethernet.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | ) 7 | 8 | func ParseEthernetFrame(p Packet) (Frame, error) { 9 | data := p.Payload() 10 | return &EthernetFrame{ 11 | header: data[:17], 12 | payload: data[17:], 13 | }, nil 14 | } 15 | 16 | type EthernetFrame struct { 17 | header []byte 18 | payload []byte 19 | } 20 | 21 | func (e *EthernetFrame) Header() []byte { 22 | return e.header 23 | } 24 | 25 | func (e *EthernetFrame) Payload() []byte { 26 | return e.payload 27 | } 28 | 29 | func (e *EthernetFrame) Ethertype() uint16 { 30 | return binary.LittleEndian.Uint16(e.header[15:17]) 31 | } 32 | 33 | func (e *EthernetFrame) String() string { 34 | return fmt.Sprintf("Ethernet: payload=%d ethertype=%x", len(e.payload), e.Ethertype()) 35 | } 36 | -------------------------------------------------------------------------------- /file.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "io" 7 | "os" 8 | ) 9 | 10 | type PcapFile struct { 11 | io.ReadCloser 12 | hdr FileHeader 13 | } 14 | 15 | func (pcap *PcapFile) readFileHeader() error { 16 | return binary.Read(pcap.ReadCloser, binary.LittleEndian, &pcap.hdr) 17 | } 18 | 19 | type capture struct { 20 | hdr PacketHeader 21 | payload []byte 22 | } 23 | 24 | func (c *capture) String() string { 25 | return fmt.Sprintf("capture: payload=%d", c.hdr.Caplen) 26 | } 27 | 28 | func (c *capture) Payload() []byte { 29 | return c.payload 30 | } 31 | 32 | func (pcap *PcapFile) ReadPacket() (Packet, error) { 33 | var capture = new(capture) 34 | if err := binary.Read(pcap.ReadCloser, binary.LittleEndian, &capture.hdr); err != nil { 35 | return nil, err 36 | } 37 | capture.payload = make([]byte, capture.hdr.Caplen) 38 | _, err := pcap.ReadCloser.Read(capture.payload) 39 | return capture, err 40 | } 41 | 42 | func (h FileHeader) String() string { 43 | return fmt.Sprintf("Magic: %x, Version: %d.%d, Snaplen: %d", h.Magic, h.Major, h.Minor, h.Snaplen) 44 | } 45 | 46 | func Open(file string) (PacketReader, error) { 47 | r, err := os.Open(file) 48 | if err != nil { 49 | return nil, err 50 | } 51 | pcap := &PcapFile{io.ReadCloser: r} 52 | err = pcap.readFileHeader() 53 | if err != nil { 54 | return nil, err 55 | } 56 | return pcap, nil 57 | } 58 | -------------------------------------------------------------------------------- /file_test.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | import ( 4 | "io" 5 | "testing" 6 | ) 7 | 8 | func TestReadPcapFile(t *testing.T) { 9 | pcap, err := Open("testdata/snmp.pcap") 10 | if err != nil { 11 | t.Error(err) 12 | } 13 | defer pcap.Close() 14 | fhdr, _ := pcap.(*PcapFile) 15 | t.Log(fhdr.hdr) 16 | 17 | for { 18 | capture, err := pcap.ReadCapture() 19 | if err != nil { 20 | if err == io.EOF { 21 | return 22 | } 23 | t.Fatal(err) 24 | } 25 | t.Log(capture) 26 | frame, err := ParseEthernetFrame(capture) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | t.Log(frame) 31 | packet, err := ParseIP(frame) 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | t.Log(packet) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /frame.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | type Frame interface { 4 | Header() []byte 5 | Payload() []byte 6 | } 7 | -------------------------------------------------------------------------------- /ip.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | import "fmt" 4 | 5 | type IPPacket interface { 6 | Header() []byte 7 | Payload() []byte 8 | } 9 | 10 | type IPv4Packet struct { 11 | header []byte 12 | payload []byte 13 | } 14 | 15 | func (ip *IPv4Packet) Header() []byte { 16 | return ip.header 17 | } 18 | 19 | func (ip *IPv4Packet) Payload() []byte { 20 | return ip.payload 21 | } 22 | 23 | func (ip *IPv4Packet) String() string { 24 | return fmt.Sprintf("IPv4: payload=%d", len(ip.payload)) 25 | } 26 | 27 | func ParseIP(frame Frame) (IPPacket, error) { 28 | data := frame.Payload() 29 | ihl := (data[0] & 0xf) << 2 30 | return &IPv4Packet{ 31 | header: data[:ihl], 32 | payload: data[ihl:], 33 | }, nil 34 | } 35 | -------------------------------------------------------------------------------- /mac.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Mac []byte 8 | 9 | func (m Mac) String() string { 10 | return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", m[0], m[1], m[2], m[3], m[4], m[5]) 11 | } 12 | -------------------------------------------------------------------------------- /mac_test.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | const ( 8 | EXPECTED = "00:26:f2:5b:ac:4d" 9 | ) 10 | 11 | func TestMacPrinting(t *testing.T) { 12 | mac := Mac([]byte{0x0, 0x26, 0xf2, 0x5b, 0xac, 0x4d}) 13 | if mac.String() != EXPECTED { 14 | t.Fatalf("Expected %s, Actual %s", EXPECTED, mac.String()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packet.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | type Packet interface { 4 | Payload() []byte 5 | } 6 | -------------------------------------------------------------------------------- /pcap.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | import "io" 4 | 5 | type PacketReader interface { 6 | ReadPacket() (Packet, error) 7 | io.Closer 8 | } 9 | -------------------------------------------------------------------------------- /testdata/snmp.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davecheney/pcap/10760a170da6335ec1a48be06a86f494b0ef74ab/testdata/snmp.pcap -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | //#define _LARGEFILE_SOURCE 4 | //#define _LARGEFILE64_SOURCE 5 | //#define _FILE_OFFSET_BITS 64 6 | //#define _GNU_SOURCE 7 | // 8 | //#include 9 | //#include 10 | //#include 11 | //#include 12 | // 13 | //typedef struct pcap_file_header $FileHeader; 14 | //typedef struct pcap_pkthdr $PacketHeader; 15 | import "C" 16 | 17 | const ( 18 | Magic = 0xa1b2c3d4 19 | MagicSwapped = 0xd4c3b2a1 20 | ) 21 | 22 | type FileHeader struct { 23 | Magic uint32 24 | Major uint16 25 | Minor uint16 26 | Thiszone int32 27 | Sigfigs uint32 28 | Snaplen uint32 29 | Network uint32 30 | } 31 | 32 | type PacketHeader struct { 33 | Tssec uint32 34 | Tsusec uint32 35 | Caplen uint32 36 | Origlen uint32 37 | } 38 | -------------------------------------------------------------------------------- /udp.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | type UDPHeader struct { 4 | } 5 | 6 | type Datagram struct { 7 | header UDPHeader 8 | payload []byte 9 | } 10 | 11 | func ParseUDP(ip IPPacket) (*Datagram, error) { 12 | data := ip.Payload() 13 | return &Datagram{ 14 | payload: data[20:], 15 | }, nil 16 | 17 | } 18 | --------------------------------------------------------------------------------