github.com/slackhq/nebula@v1.9.0/header/header.go (about)

     1  package header
     2  
     3  import (
     4  	"encoding/binary"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  )
     9  
    10  //Version 1 header:
    11  // 0                                                                       31
    12  // |-----------------------------------------------------------------------|
    13  // | Version (uint4) | Type (uint4) |  Subtype (uint8) | Reserved (uint16) | 32
    14  // |-----------------------------------------------------------------------|
    15  // |                        Remote index (uint32)                          | 64
    16  // |-----------------------------------------------------------------------|
    17  // |                           Message counter                             | 96
    18  // |                               (uint64)                                | 128
    19  // |-----------------------------------------------------------------------|
    20  // |                               payload...                              |
    21  
    22  type m map[string]interface{}
    23  
    24  const (
    25  	Version uint8 = 1
    26  	Len           = 16
    27  )
    28  
    29  type MessageType uint8
    30  type MessageSubType uint8
    31  
    32  const (
    33  	Handshake   MessageType = 0
    34  	Message     MessageType = 1
    35  	RecvError   MessageType = 2
    36  	LightHouse  MessageType = 3
    37  	Test        MessageType = 4
    38  	CloseTunnel MessageType = 5
    39  	Control     MessageType = 6
    40  )
    41  
    42  var typeMap = map[MessageType]string{
    43  	Handshake:   "handshake",
    44  	Message:     "message",
    45  	RecvError:   "recvError",
    46  	LightHouse:  "lightHouse",
    47  	Test:        "test",
    48  	CloseTunnel: "closeTunnel",
    49  	Control:     "control",
    50  }
    51  
    52  const (
    53  	MessageNone  MessageSubType = 0
    54  	MessageRelay MessageSubType = 1
    55  )
    56  
    57  const (
    58  	TestRequest MessageSubType = 0
    59  	TestReply   MessageSubType = 1
    60  )
    61  
    62  const (
    63  	HandshakeIXPSK0 MessageSubType = 0
    64  	HandshakeXXPSK0 MessageSubType = 1
    65  )
    66  
    67  var ErrHeaderTooShort = errors.New("header is too short")
    68  
    69  var subTypeTestMap = map[MessageSubType]string{
    70  	TestRequest: "testRequest",
    71  	TestReply:   "testReply",
    72  }
    73  
    74  var subTypeNoneMap = map[MessageSubType]string{0: "none"}
    75  
    76  var subTypeMap = map[MessageType]*map[MessageSubType]string{
    77  	Message: {
    78  		MessageNone:  "none",
    79  		MessageRelay: "relay",
    80  	},
    81  	RecvError:   &subTypeNoneMap,
    82  	LightHouse:  &subTypeNoneMap,
    83  	Test:        &subTypeTestMap,
    84  	CloseTunnel: &subTypeNoneMap,
    85  	Handshake: {
    86  		HandshakeIXPSK0: "ix_psk0",
    87  	},
    88  	Control: &subTypeNoneMap,
    89  }
    90  
    91  type H struct {
    92  	Version        uint8
    93  	Type           MessageType
    94  	Subtype        MessageSubType
    95  	Reserved       uint16
    96  	RemoteIndex    uint32
    97  	MessageCounter uint64
    98  }
    99  
   100  // Encode uses the provided byte array to encode the provided header values into.
   101  // Byte array must be capped higher than HeaderLen or this will panic
   102  func Encode(b []byte, v uint8, t MessageType, st MessageSubType, ri uint32, c uint64) []byte {
   103  	b = b[:Len]
   104  	b[0] = v<<4 | byte(t&0x0f)
   105  	b[1] = byte(st)
   106  	binary.BigEndian.PutUint16(b[2:4], 0)
   107  	binary.BigEndian.PutUint32(b[4:8], ri)
   108  	binary.BigEndian.PutUint64(b[8:16], c)
   109  	return b
   110  }
   111  
   112  // String creates a readable string representation of a header
   113  func (h *H) String() string {
   114  	if h == nil {
   115  		return "<nil>"
   116  	}
   117  	return fmt.Sprintf("ver=%d type=%s subtype=%s reserved=%#x remoteindex=%v messagecounter=%v",
   118  		h.Version, h.TypeName(), h.SubTypeName(), h.Reserved, h.RemoteIndex, h.MessageCounter)
   119  }
   120  
   121  // MarshalJSON creates a json string representation of a header
   122  func (h *H) MarshalJSON() ([]byte, error) {
   123  	return json.Marshal(m{
   124  		"version":        h.Version,
   125  		"type":           h.TypeName(),
   126  		"subType":        h.SubTypeName(),
   127  		"reserved":       h.Reserved,
   128  		"remoteIndex":    h.RemoteIndex,
   129  		"messageCounter": h.MessageCounter,
   130  	})
   131  }
   132  
   133  // Encode turns header into bytes
   134  func (h *H) Encode(b []byte) ([]byte, error) {
   135  	if h == nil {
   136  		return nil, errors.New("nil header")
   137  	}
   138  
   139  	return Encode(b, h.Version, h.Type, h.Subtype, h.RemoteIndex, h.MessageCounter), nil
   140  }
   141  
   142  // Parse is a helper function to parses given bytes into new Header struct
   143  func (h *H) Parse(b []byte) error {
   144  	if len(b) < Len {
   145  		return ErrHeaderTooShort
   146  	}
   147  	// get upper 4 bytes
   148  	h.Version = uint8((b[0] >> 4) & 0x0f)
   149  	// get lower 4 bytes
   150  	h.Type = MessageType(b[0] & 0x0f)
   151  	h.Subtype = MessageSubType(b[1])
   152  	h.Reserved = binary.BigEndian.Uint16(b[2:4])
   153  	h.RemoteIndex = binary.BigEndian.Uint32(b[4:8])
   154  	h.MessageCounter = binary.BigEndian.Uint64(b[8:16])
   155  	return nil
   156  }
   157  
   158  // TypeName will transform the headers message type into a human string
   159  func (h *H) TypeName() string {
   160  	return TypeName(h.Type)
   161  }
   162  
   163  // TypeName will transform a nebula message type into a human string
   164  func TypeName(t MessageType) string {
   165  	if n, ok := typeMap[t]; ok {
   166  		return n
   167  	}
   168  
   169  	return "unknown"
   170  }
   171  
   172  // SubTypeName will transform the headers message sub type into a human string
   173  func (h *H) SubTypeName() string {
   174  	return SubTypeName(h.Type, h.Subtype)
   175  }
   176  
   177  // SubTypeName will transform a nebula message sub type into a human string
   178  func SubTypeName(t MessageType, s MessageSubType) string {
   179  	if n, ok := subTypeMap[t]; ok {
   180  		if x, ok := (*n)[s]; ok {
   181  			return x
   182  		}
   183  	}
   184  
   185  	return "unknown"
   186  }
   187  
   188  // NewHeader turns bytes into a header
   189  func NewHeader(b []byte) (*H, error) {
   190  	h := new(H)
   191  	if err := h.Parse(b); err != nil {
   192  		return nil, err
   193  	}
   194  	return h, nil
   195  }