1 // Copyright 2014 Damjan Cvetko. All rights reserved.
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the LICENSE file in the root of the source
18 "github.com/google/gopacket"
19 "github.com/google/gopacket/layers"
22 // Reader wraps an underlying io.Reader to read packet data in PCAP
23 // format. See http://wiki.wireshark.org/Development/LibpcapFileFormat
24 // for information on the file format.
26 // We currenty read v2.4 file format with nanosecond and microsecdond
27 // timestamp resolution in little-endian and big-endian encoding.
29 // If the PCAP data is gzip compressed it is transparently uncompressed
30 // by wrapping the given io.Reader with a gzip.Reader.
33 byteOrder binary.ByteOrder
40 linkType layers.LinkType
45 const magicNanoseconds = 0xA1B23C4D
46 const magicMicrosecondsBigendian = 0xD4C3B2A1
47 const magicNanosecondsBigendian = 0x4D3CB2A1
49 const magicGzip1 = 0x1f
50 const magicGzip2 = 0x8b
52 // NewReader returns a new reader object, for reading packet data from
53 // the given reader. The reader must be open and header data is
54 // read from it at this point.
55 // If the file format is not supported an error is returned
57 // // Create new reader:
58 // f, _ := os.Open("/tmp/file.pcap")
60 // r, err := NewReader(f)
61 // data, ci, err := r.ReadPacketData()
62 func NewReader(r io.Reader) (*Reader, error) {
64 if err := ret.readHeader(); err != nil {
70 func (r *Reader) readHeader() error {
71 br := bufio.NewReader(r.r)
72 gzipMagic, err := br.Peek(2)
77 if gzipMagic[0] == magicGzip1 && gzipMagic[1] == magicGzip2 {
78 if r.r, err = gzip.NewReader(br); err != nil {
85 buf := make([]byte, 24)
86 if n, err := io.ReadFull(r.r, buf); err != nil {
89 return errors.New("Not enough data for read")
91 if magic := binary.LittleEndian.Uint32(buf[0:4]); magic == magicNanoseconds {
92 r.byteOrder = binary.LittleEndian
94 } else if magic == magicNanosecondsBigendian {
95 r.byteOrder = binary.BigEndian
97 } else if magic == magicMicroseconds {
98 r.byteOrder = binary.LittleEndian
99 r.nanoSecsFactor = 1000
100 } else if magic == magicMicrosecondsBigendian {
101 r.byteOrder = binary.BigEndian
102 r.nanoSecsFactor = 1000
104 return fmt.Errorf("Unknown magic %x", magic)
106 if r.versionMajor = r.byteOrder.Uint16(buf[4:6]); r.versionMajor != versionMajor {
107 return fmt.Errorf("Unknown major version %d", r.versionMajor)
109 if r.versionMinor = r.byteOrder.Uint16(buf[6:8]); r.versionMinor != versionMinor {
110 return fmt.Errorf("Unknown minor version %d", r.versionMinor)
112 // ignore timezone 8:12 and sigfigs 12:16
113 r.snaplen = r.byteOrder.Uint32(buf[16:20])
114 r.linkType = layers.LinkType(r.byteOrder.Uint32(buf[20:24]))
118 // ReadPacketData reads next packet from file.
119 func (r *Reader) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
120 if ci, err = r.readPacketHeader(); err != nil {
123 if ci.CaptureLength > int(r.snaplen) {
124 err = fmt.Errorf("capture length exceeds snap length: %d > %d", 16+ci.CaptureLength, r.snaplen)
127 data = make([]byte, ci.CaptureLength)
128 _, err = io.ReadFull(r.r, data)
132 func (r *Reader) readPacketHeader() (ci gopacket.CaptureInfo, err error) {
133 if _, err = io.ReadFull(r.r, r.buf[:]); err != nil {
136 ci.Timestamp = time.Unix(int64(r.byteOrder.Uint32(r.buf[0:4])), int64(r.byteOrder.Uint32(r.buf[4:8])*r.nanoSecsFactor)).UTC()
137 ci.CaptureLength = int(r.byteOrder.Uint32(r.buf[8:12]))
138 ci.Length = int(r.byteOrder.Uint32(r.buf[12:16]))
142 // LinkType returns network, as a layers.LinkType.
143 func (r *Reader) LinkType() layers.LinkType {
147 // Snaplen returns the snapshot length of the capture file.
148 func (r *Reader) Snaplen() uint32 {
153 func (r *Reader) String() string {
154 return fmt.Sprintf("PcapFile maj: %x min: %x snaplen: %d linktype: %s", r.versionMajor, r.versionMinor, r.snaplen, r.linkType)