File: peer_name_mac.go

package info (click to toggle)
golang-github-weaveworks-mesh 0%2Bgit20161024.3dd75b1-1~bpo8%2B1
  • links: PTS, VCS
  • area: main
  • in suites: jessie-backports
  • size: 412 kB
  • sloc: sh: 59; makefile: 7
file content (110 lines) | stat: -rw-r--r-- 3,100 bytes parent folder | download | duplicates (2)
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
// +build peer_name_mac !peer_name_alternative

package mesh

// The !peer_name_alternative effectively makes this the default,
// i.e. to choose an alternative, run
//
//   go build -tags 'peer_name_alternative peer_name_hash'
//
// Let peer names be MACs...
//
// MACs need to be unique across our network, or bad things will
// happen anyway. So they make pretty good candidates for peer
// names. And doing so is pretty efficient both computationally and
// network overhead wise.
//
// Note that we do not mandate *what* MAC should be used as the peer
// name. In particular it doesn't actually have to be the MAC of, say,
// the network interface the peer is sniffing on.

import (
	"fmt"
	"net"
)

// PeerName is used as a map key. Since net.HardwareAddr isn't suitable for
// that - it's a slice, and slices can't be map keys - we convert that to/from
// uint64.
type PeerName uint64

const (
	// PeerNameFlavour is the type of peer names we use.
	PeerNameFlavour = "mac"

	// NameSize is the number of bytes in a peer name.
	NameSize = 6

	// UnknownPeerName is used as a sentinel value.
	UnknownPeerName = PeerName(0)
)

// PeerNameFromUserInput parses PeerName from a user-provided string.
func PeerNameFromUserInput(userInput string) (PeerName, error) {
	return PeerNameFromString(userInput)
}

// PeerNameFromString parses PeerName from a generic string.
func PeerNameFromString(nameStr string) (PeerName, error) {
	var a, b, c, d, e, f uint64

	match := func(format string, args ...interface{}) bool {
		a, b, c, d, e, f = 0, 0, 0, 0, 0, 0
		n, err := fmt.Sscanf(nameStr+"\000", format+"\000", args...)
		return err == nil && n == len(args)
	}

	switch {
	case match("%2x:%2x:%2x:%2x:%2x:%2x", &a, &b, &c, &d, &e, &f):
	case match("::%2x:%2x:%2x:%2x", &c, &d, &e, &f):
	case match("%2x::%2x:%2x:%2x", &a, &d, &e, &f):
	case match("%2x:%2x::%2x:%2x", &a, &b, &e, &f):
	case match("%2x:%2x:%2x::%2x", &a, &b, &c, &f):
	case match("%2x:%2x:%2x:%2x::", &a, &b, &c, &d):
	case match("::%2x:%2x:%2x", &d, &e, &f):
	case match("%2x::%2x:%2x", &a, &e, &f):
	case match("%2x:%2x::%2x", &a, &b, &f):
	case match("%2x:%2x:%2x::", &a, &b, &c):
	case match("::%2x:%2x", &e, &f):
	case match("%2x::%2x", &a, &f):
	case match("%2x:%2x::", &a, &b):
	case match("::%2x", &f):
	case match("%2x::", &a):
	default:
		return UnknownPeerName, fmt.Errorf("invalid peer name format: %q", nameStr)
	}

	return PeerName(a<<40 | b<<32 | c<<24 | d<<16 | e<<8 | f), nil
}

// PeerNameFromBin parses PeerName from a byte slice.
func PeerNameFromBin(nameByte []byte) PeerName {
	return PeerName(macint(net.HardwareAddr(nameByte)))
}

// Bin encodes PeerName as a byte slice.
func (name PeerName) bytes() []byte {
	return intmac(uint64(name))
}

// String encodes PeerName as a string.
func (name PeerName) String() string {
	return intmac(uint64(name)).String()
}

func macint(mac net.HardwareAddr) (r uint64) {
	for _, b := range mac {
		r <<= 8
		r |= uint64(b)
	}
	return
}

func intmac(key uint64) (r net.HardwareAddr) {
	r = make([]byte, 6)
	for i := 5; i >= 0; i-- {
		r[i] = byte(key)
		key >>= 8
	}
	return
}