| // 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) |