forked from gopacket/gopacket
-
Notifications
You must be signed in to change notification settings - Fork 0
/
flows.go
236 lines (206 loc) · 7.69 KB
/
flows.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
226
227
228
229
230
231
232
233
234
235
236
// Copyright 2012 Google, Inc. 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 gopacket
import (
"bytes"
"fmt"
"strconv"
)
// MaxEndpointSize determines the maximum size in bytes of an endpoint address.
//
// Endpoints/Flows have a problem: They need to be hashable. Therefore, they
// can't use a byte slice. The two obvious choices are to use a string or a
// byte array. Strings work great, but string creation requires memory
// allocation, which can be slow. Arrays work great, but have a fixed size. We
// originally used the former, now we've switched to the latter. Use of a fixed
// byte-array doubles the speed of constructing a flow (due to not needing to
// allocate). This is a huge increase... too much for us to pass up.
//
// The end result of this, though, is that an endpoint/flow can't be created
// using more than MaxEndpointSize bytes per address.
const MaxEndpointSize = 16
// Endpoint is the set of bytes used to address packets at various layers.
// See LinkLayer, NetworkLayer, and TransportLayer specifications.
// Endpoints are usable as map keys.
type Endpoint struct {
typ EndpointType
len int
raw [MaxEndpointSize]byte
}
// EndpointType returns the endpoint type associated with this endpoint.
func (a Endpoint) EndpointType() EndpointType { return a.typ }
// Raw returns the raw bytes of this endpoint. These aren't human-readable
// most of the time, but they are faster than calling String.
func (a Endpoint) Raw() []byte { return a.raw[:a.len] }
// LessThan provides a stable ordering for all endpoints. It sorts first based
// on the EndpointType of an endpoint, then based on the raw bytes of that
// endpoint.
//
// For some endpoints, the actual comparison may not make sense, however this
// ordering does provide useful information for most Endpoint types.
// Ordering is based first on endpoint type, then on raw endpoint bytes.
// Endpoint bytes are sorted lexicographically.
func (a Endpoint) LessThan(b Endpoint) bool {
return a.typ < b.typ || (a.typ == b.typ && bytes.Compare(a.raw[:a.len], b.raw[:b.len]) < 0)
}
// fnvHash is used by our FastHash functions, and implements the FNV hash
// created by Glenn Fowler, Landon Curt Noll, and Phong Vo.
// See http://isthe.com/chongo/tech/comp/fnv/.
func fnvHash(s []byte) (h uint64) {
h = fnvBasis
for i := 0; i < len(s); i++ {
h ^= uint64(s[i])
h *= fnvPrime
}
return
}
const fnvBasis = 14695981039346656037
const fnvPrime = 1099511628211
// FastHash provides a quick hashing function for an endpoint, useful if you'd
// like to split up endpoints by modulos or other load-balancing techniques.
// It uses a variant of Fowler-Noll-Vo hashing.
//
// The output of FastHash is not guaranteed to remain the same through future
// code revisions, so should not be used to key values in persistent storage.
func (a Endpoint) FastHash() (h uint64) {
h = fnvHash(a.raw[:a.len])
h ^= uint64(a.typ)
h *= fnvPrime
return
}
// NewEndpoint creates a new Endpoint object.
//
// The size of raw must be less than MaxEndpointSize, otherwise this function
// will panic.
func NewEndpoint(typ EndpointType, raw []byte) (e Endpoint) {
e.len = len(raw)
if e.len > MaxEndpointSize {
panic("raw byte length greater than MaxEndpointSize")
}
e.typ = typ
copy(e.raw[:], raw)
return
}
// EndpointTypeMetadata is used to register a new endpoint type.
type EndpointTypeMetadata struct {
// Name is the string returned by an EndpointType's String function.
Name string
// Formatter is called from an Endpoint's String function to format the raw
// bytes in an Endpoint into a human-readable string.
Formatter func([]byte) string
}
// EndpointType is the type of a gopacket Endpoint. This type determines how
// the bytes stored in the endpoint should be interpreted.
type EndpointType int64
var endpointTypes = map[EndpointType]EndpointTypeMetadata{}
// RegisterEndpointType creates a new EndpointType and registers it globally.
// It MUST be passed a unique number, or it will panic. Numbers 0-999 are
// reserved for gopacket's use.
func RegisterEndpointType(num int, meta EndpointTypeMetadata) EndpointType {
t := EndpointType(num)
if _, ok := endpointTypes[t]; ok {
panic("Endpoint type number already in use")
}
endpointTypes[t] = meta
return t
}
func (e EndpointType) String() string {
if t, ok := endpointTypes[e]; ok {
return t.Name
}
return strconv.Itoa(int(e))
}
func (a Endpoint) String() string {
if t, ok := endpointTypes[a.typ]; ok && t.Formatter != nil {
return t.Formatter(a.raw[:a.len])
}
return fmt.Sprintf("%v:%v", a.typ, a.raw)
}
// Flow represents the direction of traffic for a packet layer, as a source and destination Endpoint.
// Flows are usable as map keys.
type Flow struct {
typ EndpointType
slen, dlen int
src, dst [MaxEndpointSize]byte
}
// FlowFromEndpoints creates a new flow by pasting together two endpoints.
// The endpoints must have the same EndpointType, or this function will return
// an error.
func FlowFromEndpoints(src, dst Endpoint) (_ Flow, err error) {
if src.typ != dst.typ {
err = fmt.Errorf("Mismatched endpoint types: %v->%v", src.typ, dst.typ)
return
}
return Flow{src.typ, src.len, dst.len, src.raw, dst.raw}, nil
}
// FastHash provides a quick hashing function for a flow, useful if you'd
// like to split up flows by modulos or other load-balancing techniques.
// It uses a variant of Fowler-Noll-Vo hashing, and is guaranteed to collide
// with its reverse flow. IE: the flow A->B will have the same hash as the flow
// B->A.
//
// The output of FastHash is not guaranteed to remain the same through future
// code revisions, so should not be used to key values in persistent storage.
func (f Flow) FastHash() (h uint64) {
// This combination must be commutative. We don't use ^, since that would
// give the same hash for all A->A flows.
h = fnvHash(f.src[:f.slen]) + fnvHash(f.dst[:f.dlen])
h ^= uint64(f.typ)
h *= fnvPrime
return
}
// String returns a human-readable representation of this flow, in the form
// "Src->Dst"
func (f Flow) String() string {
s, d := f.Endpoints()
return fmt.Sprintf("%v->%v", s, d)
}
// EndpointType returns the EndpointType for this Flow.
func (f Flow) EndpointType() EndpointType {
return f.typ
}
// Endpoints returns the two Endpoints for this flow.
func (f Flow) Endpoints() (src, dst Endpoint) {
return Endpoint{f.typ, f.slen, f.src}, Endpoint{f.typ, f.dlen, f.dst}
}
// Src returns the source Endpoint for this flow.
func (f Flow) Src() (src Endpoint) {
src, _ = f.Endpoints()
return
}
// Dst returns the destination Endpoint for this flow.
func (f Flow) Dst() (dst Endpoint) {
_, dst = f.Endpoints()
return
}
// Reverse returns a new flow with endpoints reversed.
func (f Flow) Reverse() Flow {
return Flow{f.typ, f.dlen, f.slen, f.dst, f.src}
}
// NewFlow creates a new flow.
//
// src and dst must have length <= MaxEndpointSize, otherwise NewFlow will
// panic.
func NewFlow(t EndpointType, src, dst []byte) (f Flow) {
f.slen = len(src)
f.dlen = len(dst)
if f.slen > MaxEndpointSize || f.dlen > MaxEndpointSize {
panic("flow raw byte length greater than MaxEndpointSize")
}
f.typ = t
copy(f.src[:], src)
copy(f.dst[:], dst)
return
}
// EndpointInvalid is an endpoint type used for invalid endpoints, IE endpoints
// that are specified incorrectly during creation.
var EndpointInvalid = RegisterEndpointType(0, EndpointTypeMetadata{Name: "invalid", Formatter: func(b []byte) string {
return fmt.Sprintf("%v", b)
}})
// InvalidEndpoint is a singleton Endpoint of type EndpointInvalid.
var InvalidEndpoint = NewEndpoint(EndpointInvalid, nil)
// InvalidFlow is a singleton Flow of type EndpointInvalid.
var InvalidFlow = NewFlow(EndpointInvalid, nil, nil)