/
GPPacket.go
225 lines (188 loc) · 6.52 KB
/
GPPacket.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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/////////////////////////////////////////////////////////////////////////////////
//
// GPPacket.go
//
// Main packet Interface that provides the datastructure that is passed around
// every channel within the program. Contains the necessary information that a flow
// needs
//
// Written by Lennart Elsen lel@open.ch, May 2014
// Copyright (c) 2014 Open Systems AG, Switzerland
// All Rights Reserved.
//
/////////////////////////////////////////////////////////////////////////////////
package goProbe
import (
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
var (
BYTE_ARR_1_ZERO = byte(0x00)
BYTE_ARR_2_ZERO = [2]byte{0x00, 0x00}
BYTE_ARR_4_ZERO = [4]byte{0x00, 0x00, 0x00, 0x00}
BYTE_ARR_16_ZERO = [16]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
BYTE_ARR_37_ZERO = [37]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
)
const (
TCP byte = 6
UDP = 17
ESP = 50
)
// typedef that allows us to replace the type of hash
type EPHash [37]byte
type GPPacket struct {
// core fields
sip [16]byte
dip [16]byte
sport [2]byte
dport [2]byte
protocol byte
l7payload [4]byte
l7payloadSize uint16
numBytes uint16
// direction indicator fields
tcpFlags byte
// packet descriptors
epHash EPHash
epHashReverse EPHash
dirInbound bool // packet inbound or outbound on interface
}
func (p *GPPacket) computeEPHash() {
// carve out the ports
dport := uint16(p.dport[0])<<8 | uint16(p.dport[1])
sport := uint16(p.sport[0])<<8 | uint16(p.sport[1])
// prepare byte arrays:
// include different fields into the hashing arrays in order to
// discern between session based traffic and udp traffic. When
// session based traffic is observed, the source port is taken
// into account. A major exception is traffic over port 53 as
// considering every single DNS request/response would
// significantly fill up the flow map
copy(p.epHash[0:], p.sip[:])
copy(p.epHash[16:], p.dip[:])
copy(p.epHash[32:], p.dport[:])
if p.protocol == 6 && dport != 53 && sport != 53 {
copy(p.epHash[34:], p.sport[:])
} else {
p.epHash[34], p.epHash[35] = 0, 0
}
p.epHash[36] = p.protocol
copy(p.epHashReverse[0:], p.dip[:])
copy(p.epHashReverse[16:], p.sip[:])
copy(p.epHashReverse[32:], p.sport[:])
if p.protocol == 6 && dport != 53 && sport != 53 {
copy(p.epHashReverse[34:], p.dport[:])
} else {
p.epHashReverse[34], p.epHashReverse[35] = 0, 0
}
p.epHashReverse[36] = p.protocol
}
// Populate takes a raw packet and populates a GPPacket structure from it.
func (p *GPPacket) Populate(srcPacket gopacket.Packet) error {
// first things first: reset packet from previous run
p.reset()
// size helper vars
var nlHeaderSize, tpHeaderSize uint16
// process metadata
p.numBytes = uint16(srcPacket.Metadata().CaptureInfo.Length)
// read the direction from which the packet entered the interface
p.dirInbound = false
if srcPacket.Metadata().CaptureInfo.Inbound == 1 {
p.dirInbound = true
}
// for ESP traffic (which lacks a transport layer)
var skipTransport bool
// decode packet
if srcPacket.NetworkLayer() != nil {
nw_l := srcPacket.NetworkLayer().LayerContents()
nlHeaderSize = uint16(len(nw_l))
// exit if layer is available but the bytes aren't captured by the layer
// contents
if nlHeaderSize == 0 {
return fmt.Errorf("Network layer header not available")
}
// get ip info
ipsrc, ipdst := srcPacket.NetworkLayer().NetworkFlow().Endpoints()
copy(p.sip[:], ipsrc.Raw())
copy(p.dip[:], ipdst.Raw())
// read out the next layer protocol
// the default value is reserved by IANA and thus will never occur unless
// the protocol could not be correctly identified
p.protocol = 0xFF
switch srcPacket.NetworkLayer().LayerType() {
case layers.LayerTypeIPv4:
p.protocol = nw_l[9]
// only run the fragmentation checks on fragmented TCP/UDP packets. For
// ESP, we don't have any transport layer information so there's no
// need to distinguish between ESP fragments or other ESP traffic
//
// Note: an ESP fragment will carry fragmentation information like any
// other IP packet. The fragment offset will of be MTU - 20 bytes (IP layer).
if p.protocol == ESP {
skipTransport = true
} else {
// check for IP fragmentation
fragBits := (0xe0 & nw_l[6]) >> 5
fragOffset := (uint16(0x1f&nw_l[6]) << 8) | uint16(nw_l[7])
// return decoding error if the packet carries anything other than the
// first fragment, i.e. if the packet lacks a transport layer header
if fragOffset != 0 {
return fmt.Errorf("Fragmented IP packet: offset: %d flags: %d", fragOffset, fragBits)
}
}
case layers.LayerTypeIPv6:
p.protocol = nw_l[6]
}
if !skipTransport && srcPacket.TransportLayer() != nil {
// get layer contents
tp_l := srcPacket.TransportLayer().LayerContents()
tpHeaderSize = uint16(len(tp_l))
if tpHeaderSize == 0 {
return fmt.Errorf("Transport layer header not available")
}
// get port bytes
psrc, dsrc := srcPacket.TransportLayer().TransportFlow().Endpoints()
// only get raw bytes if we actually have TCP or UDP
if p.protocol == TCP || p.protocol == UDP {
copy(p.sport[:], psrc.Raw())
copy(p.dport[:], dsrc.Raw())
}
// if the protocol is TCP, grab the flag information
if p.protocol == TCP {
if tpHeaderSize < 14 {
return fmt.Errorf("Incomplete TCP header: %d", tp_l)
}
p.tcpFlags = tp_l[13] // we are primarily interested in SYN, ACK and FIN
}
}
} else {
// extract error if available
if err := srcPacket.ErrorLayer(); err != nil {
// enrich it with concrete info about which layer failed
var layers string
for _, layer := range srcPacket.Layers() {
layers += layer.LayerType().String() + "/"
}
layers = layers[:len(layers)-1]
return fmt.Errorf("%s: %s", layers, err.Error())
}
// if the error layer is nil, the packet belongs to a protocol which does not contain
// IP layers and hence no useful information for goquery
return nil
}
p.computeEPHash()
return nil
}
func (p *GPPacket) reset() {
p.sip = BYTE_ARR_16_ZERO
p.dip = BYTE_ARR_16_ZERO
p.dport = BYTE_ARR_2_ZERO
p.sport = BYTE_ARR_2_ZERO
p.protocol = BYTE_ARR_1_ZERO
p.numBytes = uint16(0)
p.tcpFlags = BYTE_ARR_1_ZERO
p.epHash = BYTE_ARR_37_ZERO
p.epHashReverse = BYTE_ARR_37_ZERO
p.dirInbound = false
}