github.com/gopacket/gopacket@v1.1.0/flows.go (about)

     1  // Copyright 2012 Google, Inc. All rights reserved.
     2  //
     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
     5  // tree.
     6  
     7  package gopacket
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"strconv"
    13  )
    14  
    15  // MaxEndpointSize determines the maximum size in bytes of an endpoint address.
    16  //
    17  // Endpoints/Flows have a problem:  They need to be hashable.  Therefore, they
    18  // can't use a byte slice.  The two obvious choices are to use a string or a
    19  // byte array.  Strings work great, but string creation requires memory
    20  // allocation, which can be slow.  Arrays work great, but have a fixed size.  We
    21  // originally used the former, now we've switched to the latter.  Use of a fixed
    22  // byte-array doubles the speed of constructing a flow (due to not needing to
    23  // allocate).  This is a huge increase... too much for us to pass up.
    24  //
    25  // The end result of this, though, is that an endpoint/flow can't be created
    26  // using more than MaxEndpointSize bytes per address.
    27  const MaxEndpointSize = 16
    28  
    29  // Endpoint is the set of bytes used to address packets at various layers.
    30  // See LinkLayer, NetworkLayer, and TransportLayer specifications.
    31  // Endpoints are usable as map keys.
    32  type Endpoint struct {
    33  	typ EndpointType
    34  	len int
    35  	raw [MaxEndpointSize]byte
    36  }
    37  
    38  // EndpointType returns the endpoint type associated with this endpoint.
    39  func (a Endpoint) EndpointType() EndpointType { return a.typ }
    40  
    41  // Raw returns the raw bytes of this endpoint.  These aren't human-readable
    42  // most of the time, but they are faster than calling String.
    43  func (a Endpoint) Raw() []byte { return a.raw[:a.len] }
    44  
    45  // LessThan provides a stable ordering for all endpoints.  It sorts first based
    46  // on the EndpointType of an endpoint, then based on the raw bytes of that
    47  // endpoint.
    48  //
    49  // For some endpoints, the actual comparison may not make sense, however this
    50  // ordering does provide useful information for most Endpoint types.
    51  // Ordering is based first on endpoint type, then on raw endpoint bytes.
    52  // Endpoint bytes are sorted lexicographically.
    53  func (a Endpoint) LessThan(b Endpoint) bool {
    54  	return a.typ < b.typ || (a.typ == b.typ && bytes.Compare(a.raw[:a.len], b.raw[:b.len]) < 0)
    55  }
    56  
    57  // fnvHash is used by our FastHash functions, and implements the FNV hash
    58  // created by Glenn Fowler, Landon Curt Noll, and Phong Vo.
    59  // See http://isthe.com/chongo/tech/comp/fnv/.
    60  func fnvHash(s []byte) (h uint64) {
    61  	h = fnvBasis
    62  	for i := 0; i < len(s); i++ {
    63  		h ^= uint64(s[i])
    64  		h *= fnvPrime
    65  	}
    66  	return
    67  }
    68  
    69  const fnvBasis = 14695981039346656037
    70  const fnvPrime = 1099511628211
    71  
    72  // FastHash provides a quick hashing function for an endpoint, useful if you'd
    73  // like to split up endpoints by modulos or other load-balancing techniques.
    74  // It uses a variant of Fowler-Noll-Vo hashing.
    75  //
    76  // The output of FastHash is not guaranteed to remain the same through future
    77  // code revisions, so should not be used to key values in persistent storage.
    78  func (a Endpoint) FastHash() (h uint64) {
    79  	h = fnvHash(a.raw[:a.len])
    80  	h ^= uint64(a.typ)
    81  	h *= fnvPrime
    82  	return
    83  }
    84  
    85  // NewEndpoint creates a new Endpoint object.
    86  //
    87  // The size of raw must be less than MaxEndpointSize, otherwise this function
    88  // will panic.
    89  func NewEndpoint(typ EndpointType, raw []byte) (e Endpoint) {
    90  	e.len = len(raw)
    91  	if e.len > MaxEndpointSize {
    92  		panic("raw byte length greater than MaxEndpointSize")
    93  	}
    94  	e.typ = typ
    95  	copy(e.raw[:], raw)
    96  	return
    97  }
    98  
    99  // EndpointTypeMetadata is used to register a new endpoint type.
   100  type EndpointTypeMetadata struct {
   101  	// Name is the string returned by an EndpointType's String function.
   102  	Name string
   103  	// Formatter is called from an Endpoint's String function to format the raw
   104  	// bytes in an Endpoint into a human-readable string.
   105  	Formatter func([]byte) string
   106  }
   107  
   108  // EndpointType is the type of a gopacket Endpoint.  This type determines how
   109  // the bytes stored in the endpoint should be interpreted.
   110  type EndpointType int64
   111  
   112  var endpointTypes = map[EndpointType]EndpointTypeMetadata{}
   113  
   114  // RegisterEndpointType creates a new EndpointType and registers it globally.
   115  // It MUST be passed a unique number, or it will panic.  Numbers 0-999 are
   116  // reserved for gopacket's use.
   117  func RegisterEndpointType(num int, meta EndpointTypeMetadata) EndpointType {
   118  	t := EndpointType(num)
   119  	if _, ok := endpointTypes[t]; ok {
   120  		panic("Endpoint type number already in use")
   121  	}
   122  	endpointTypes[t] = meta
   123  	return t
   124  }
   125  
   126  func (e EndpointType) String() string {
   127  	if t, ok := endpointTypes[e]; ok {
   128  		return t.Name
   129  	}
   130  	return strconv.Itoa(int(e))
   131  }
   132  
   133  func (a Endpoint) String() string {
   134  	if t, ok := endpointTypes[a.typ]; ok && t.Formatter != nil {
   135  		return t.Formatter(a.raw[:a.len])
   136  	}
   137  	return fmt.Sprintf("%v:%v", a.typ, a.raw)
   138  }
   139  
   140  // Flow represents the direction of traffic for a packet layer, as a source and destination Endpoint.
   141  // Flows are usable as map keys.
   142  type Flow struct {
   143  	typ        EndpointType
   144  	slen, dlen int
   145  	src, dst   [MaxEndpointSize]byte
   146  }
   147  
   148  // FlowFromEndpoints creates a new flow by pasting together two endpoints.
   149  // The endpoints must have the same EndpointType, or this function will return
   150  // an error.
   151  func FlowFromEndpoints(src, dst Endpoint) (_ Flow, err error) {
   152  	if src.typ != dst.typ {
   153  		err = fmt.Errorf("Mismatched endpoint types: %v->%v", src.typ, dst.typ)
   154  		return
   155  	}
   156  	return Flow{src.typ, src.len, dst.len, src.raw, dst.raw}, nil
   157  }
   158  
   159  // FastHash provides a quick hashing function for a flow, useful if you'd
   160  // like to split up flows by modulos or other load-balancing techniques.
   161  // It uses a variant of Fowler-Noll-Vo hashing, and is guaranteed to collide
   162  // with its reverse flow.  IE: the flow A->B will have the same hash as the flow
   163  // B->A.
   164  //
   165  // The output of FastHash is not guaranteed to remain the same through future
   166  // code revisions, so should not be used to key values in persistent storage.
   167  func (f Flow) FastHash() (h uint64) {
   168  	// This combination must be commutative.  We don't use ^, since that would
   169  	// give the same hash for all A->A flows.
   170  	h = fnvHash(f.src[:f.slen]) + fnvHash(f.dst[:f.dlen])
   171  	h ^= uint64(f.typ)
   172  	h *= fnvPrime
   173  	return
   174  }
   175  
   176  // String returns a human-readable representation of this flow, in the form
   177  // "Src->Dst"
   178  func (f Flow) String() string {
   179  	s, d := f.Endpoints()
   180  	return fmt.Sprintf("%v->%v", s, d)
   181  }
   182  
   183  // EndpointType returns the EndpointType for this Flow.
   184  func (f Flow) EndpointType() EndpointType {
   185  	return f.typ
   186  }
   187  
   188  // Endpoints returns the two Endpoints for this flow.
   189  func (f Flow) Endpoints() (src, dst Endpoint) {
   190  	return Endpoint{f.typ, f.slen, f.src}, Endpoint{f.typ, f.dlen, f.dst}
   191  }
   192  
   193  // Src returns the source Endpoint for this flow.
   194  func (f Flow) Src() (src Endpoint) {
   195  	src, _ = f.Endpoints()
   196  	return
   197  }
   198  
   199  // Dst returns the destination Endpoint for this flow.
   200  func (f Flow) Dst() (dst Endpoint) {
   201  	_, dst = f.Endpoints()
   202  	return
   203  }
   204  
   205  // Reverse returns a new flow with endpoints reversed.
   206  func (f Flow) Reverse() Flow {
   207  	return Flow{f.typ, f.dlen, f.slen, f.dst, f.src}
   208  }
   209  
   210  // NewFlow creates a new flow.
   211  //
   212  // src and dst must have length <= MaxEndpointSize, otherwise NewFlow will
   213  // panic.
   214  func NewFlow(t EndpointType, src, dst []byte) (f Flow) {
   215  	f.slen = len(src)
   216  	f.dlen = len(dst)
   217  	if f.slen > MaxEndpointSize || f.dlen > MaxEndpointSize {
   218  		panic("flow raw byte length greater than MaxEndpointSize")
   219  	}
   220  	f.typ = t
   221  	copy(f.src[:], src)
   222  	copy(f.dst[:], dst)
   223  	return
   224  }
   225  
   226  // EndpointInvalid is an endpoint type used for invalid endpoints, IE endpoints
   227  // that are specified incorrectly during creation.
   228  var EndpointInvalid = RegisterEndpointType(0, EndpointTypeMetadata{Name: "invalid", Formatter: func(b []byte) string {
   229  	return fmt.Sprintf("%v", b)
   230  }})
   231  
   232  // InvalidEndpoint is a singleton Endpoint of type EndpointInvalid.
   233  var InvalidEndpoint = NewEndpoint(EndpointInvalid, nil)
   234  
   235  // InvalidFlow is a singleton Flow of type EndpointInvalid.
   236  var InvalidFlow = NewFlow(EndpointInvalid, nil, nil)