forked from google/gopacket
/
snoop.go
170 lines (143 loc) · 4.89 KB
/
snoop.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Copyright 2019 The GoPacket Authors. 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 pcapgo
import (
"encoding/binary"
"errors"
"fmt"
"io"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
const snoopMagic uint64 = 0x736e6f6f70000000 //8 byte in big endian
const snoopVersion uint32 = 2
const maxCaptureLen int = 4096
// Errors
const unknownMagic = "Unknown Snoop Magic Bytes"
const unknownVersion = "Unknown Snoop Format Version"
const unkownLinkType = "Unknown Link Type"
const originalLenExceeded = "Capture length exceeds original packet length"
const captureLenExceeded = "Capture length exceeds max capture length"
type snoopHeader struct {
Version uint32
linkType uint32
}
// SnoopReader wraps an underlying io.SnoopReader to read packet data in SNOOP
// format. See https://tools.ietf.org/html/rfc1761
// for information on the file format.
// We currenty read v2 file format and convert microsecond to nanoseconds
// byte order in big-endian encoding.
type SnoopReader struct {
r io.Reader
header snoopHeader
//reuseable
pad int
packetBuf []byte
buf [24]byte
}
var (
layerTypes = map[uint32]layers.LinkType{
0: layers.LinkTypeEthernet, // IEEE 802.3
2: layers.LinkTypeTokenRing, // IEEE 802.5 Token Ring
4: layers.LinkTypeEthernet, // Ethernet
5: layers.LinkTypeC_HDLC, // HDLC
8: layers.LinkTypeFDDI, // FDDI
/*
10 - 4294967295 Unassigned
not supported:
1 - IEEE 802.4 Token Bus
3 - IEEE 802.6 Metro Net
6 - Character Synchronous
7 - IBM Channel-to-Channel
9 - Other
*/
}
)
// LinkType return the mapped gopacket LinkType
func (r *SnoopReader) LinkType() (*layers.LinkType, error) {
if _, ok := layerTypes[r.header.linkType]; ok {
lt := layerTypes[r.header.linkType]
return <, nil
}
return nil, fmt.Errorf("%s, Code:%d", unkownLinkType, r.header.linkType)
}
// NewSnoopReader returns a new SnoopReader object, for reading packet data from
// the given SnoopReader. The SnoopReader must be open and header data is
// read from it at this point.
// If the file format is not supported an error is returned
func NewSnoopReader(r io.Reader) (*SnoopReader, error) {
ret := SnoopReader{r: r}
if err := ret.readHeader(); err != nil {
return nil, err
}
return &ret, nil
}
func (r *SnoopReader) readHeader() error {
buf := make([]byte, 16)
if n, err := io.ReadFull(r.r, buf); err != nil {
return err
} else if n < 16 {
return errors.New("Not enough data for read")
}
if magic := binary.BigEndian.Uint64(buf[0:8]); magic != snoopMagic {
return fmt.Errorf("%s: %x", unknownMagic, magic)
}
if r.header.Version = binary.BigEndian.Uint32(buf[8:12]); r.header.Version != snoopVersion {
return fmt.Errorf("%s: %d", unknownVersion, r.header.Version)
}
if r.header.linkType = binary.BigEndian.Uint32(buf[12:16]); r.header.linkType > 10 {
return fmt.Errorf("%s, Code:%d", unkownLinkType, r.header.linkType)
}
return nil
}
func (r *SnoopReader) readPacketHeader() (ci gopacket.CaptureInfo, err error) {
if _, err = io.ReadFull(r.r, r.buf[:]); err != nil {
return
}
// OriginalLength uint32 4
// IncludedLength uint32 8
// PacketRecordLength uint32 12
// CumulativeDrops uint32 16
// TimestampSeconds uint32 20
// TimestampMicroseconds uint32 24
ci.Timestamp = time.Unix(int64(binary.BigEndian.Uint32(r.buf[16:20])), int64(binary.BigEndian.Uint32(r.buf[20:24])*1000)).UTC()
ci.Length = int(binary.BigEndian.Uint32(r.buf[0:4]))
ci.CaptureLength = int(binary.BigEndian.Uint32(r.buf[4:8]))
r.pad = int(binary.BigEndian.Uint32(r.buf[8:12])) - (24 + ci.Length)
if ci.CaptureLength > ci.Length {
err = errors.New(originalLenExceeded)
return
}
if ci.CaptureLength > maxCaptureLen {
err = errors.New(captureLenExceeded)
}
return
}
// ReadPacketData reads next packet data.
func (r *SnoopReader) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
if ci, err = r.readPacketHeader(); err != nil {
return
}
data = make([]byte, ci.CaptureLength+r.pad)
_, err = io.ReadFull(r.r, data)
return data[:ci.CaptureLength], ci, err
}
// ZeroCopyReadPacketData reads next packet data. The data buffer is owned by the SnoopReader,
// and each call to ZeroCopyReadPacketData invalidates data returned by the previous one.
//
// It is not true zero copy, as data is still copied from the underlying SnoopReader. However,
// this method avoids allocating heap memory for every packet.
func (r *SnoopReader) ZeroCopyReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
if ci, err = r.readPacketHeader(); err != nil {
return
}
if cap(r.packetBuf) < ci.CaptureLength+r.pad {
r.packetBuf = make([]byte, ci.CaptureLength+r.pad)
}
_, err = io.ReadFull(r.r, r.packetBuf[:ci.CaptureLength+r.pad])
return r.packetBuf[:ci.CaptureLength], ci, err
}