1package consul
2
3import (
4	"encoding/binary"
5	"fmt"
6	"net"
7	"runtime"
8	"strconv"
9
10	"github.com/hashicorp/consul/agent/metadata"
11	"github.com/hashicorp/consul/agent/structs"
12	"github.com/hashicorp/go-version"
13	"github.com/hashicorp/serf/serf"
14)
15
16/*
17 * Contains an entry for each private block:
18 * 10.0.0.0/8
19 * 100.64.0.0/10
20 * 127.0.0.0/8
21 * 169.254.0.0/16
22 * 172.16.0.0/12
23 * 192.168.0.0/16
24 */
25var privateBlocks []*net.IPNet
26
27func init() {
28	// Add each private block
29	privateBlocks = make([]*net.IPNet, 6)
30
31	_, block, err := net.ParseCIDR("10.0.0.0/8")
32	if err != nil {
33		panic(fmt.Sprintf("Bad cidr. Got %v", err))
34	}
35	privateBlocks[0] = block
36
37	_, block, err = net.ParseCIDR("100.64.0.0/10")
38	if err != nil {
39		panic(fmt.Sprintf("Bad cidr. Got %v", err))
40	}
41	privateBlocks[1] = block
42
43	_, block, err = net.ParseCIDR("127.0.0.0/8")
44	if err != nil {
45		panic(fmt.Sprintf("Bad cidr. Got %v", err))
46	}
47	privateBlocks[2] = block
48
49	_, block, err = net.ParseCIDR("169.254.0.0/16")
50	if err != nil {
51		panic(fmt.Sprintf("Bad cidr. Got %v", err))
52	}
53	privateBlocks[3] = block
54
55	_, block, err = net.ParseCIDR("172.16.0.0/12")
56	if err != nil {
57		panic(fmt.Sprintf("Bad cidr. Got %v", err))
58	}
59	privateBlocks[4] = block
60
61	_, block, err = net.ParseCIDR("192.168.0.0/16")
62	if err != nil {
63		panic(fmt.Sprintf("Bad cidr. Got %v", err))
64	}
65	privateBlocks[5] = block
66}
67
68// CanServersUnderstandProtocol checks to see if all the servers in the given
69// list understand the given protocol version. If there are no servers in the
70// list then this will return false.
71func CanServersUnderstandProtocol(members []serf.Member, version uint8) (bool, error) {
72	numServers, numWhoGrok := 0, 0
73	for _, m := range members {
74		if m.Tags["role"] != "consul" {
75			continue
76		}
77		numServers++
78
79		vsnMin, err := strconv.Atoi(m.Tags["vsn_min"])
80		if err != nil {
81			return false, err
82		}
83
84		vsnMax, err := strconv.Atoi(m.Tags["vsn_max"])
85		if err != nil {
86			return false, err
87		}
88
89		v := int(version)
90		if (v >= vsnMin) && (v <= vsnMax) {
91			numWhoGrok++
92		}
93	}
94	return (numServers > 0) && (numWhoGrok == numServers), nil
95}
96
97// Returns if a member is a consul node. Returns a bool,
98// and the datacenter.
99func isConsulNode(m serf.Member) (bool, string) {
100	if m.Tags["role"] != "node" {
101		return false, ""
102	}
103	return true, m.Tags["dc"]
104}
105
106// Returns if the given IP is in a private block
107func isPrivateIP(ipStr string) bool {
108	ip := net.ParseIP(ipStr)
109	for _, priv := range privateBlocks {
110		if priv.Contains(ip) {
111			return true
112		}
113	}
114	return false
115}
116
117// Returns addresses from interfaces that is up
118func activeInterfaceAddresses() ([]net.Addr, error) {
119	var upAddrs []net.Addr
120	var loAddrs []net.Addr
121
122	interfaces, err := net.Interfaces()
123	if err != nil {
124		return nil, fmt.Errorf("Failed to get interfaces: %v", err)
125	}
126
127	for _, iface := range interfaces {
128		// Require interface to be up
129		if iface.Flags&net.FlagUp == 0 {
130			continue
131		}
132
133		addresses, err := iface.Addrs()
134		if err != nil {
135			return nil, fmt.Errorf("Failed to get interface addresses: %v", err)
136		}
137
138		if iface.Flags&net.FlagLoopback != 0 {
139			loAddrs = append(loAddrs, addresses...)
140			continue
141		}
142
143		upAddrs = append(upAddrs, addresses...)
144	}
145
146	if len(upAddrs) == 0 {
147		return loAddrs, nil
148	}
149
150	return upAddrs, nil
151}
152
153// GetPrivateIP is used to return the first private IP address
154// associated with an interface on the machine
155func GetPrivateIP() (net.IP, error) {
156	addresses, err := activeInterfaceAddresses()
157	if err != nil {
158		return nil, fmt.Errorf("Failed to get interface addresses: %v", err)
159	}
160
161	return getPrivateIP(addresses)
162}
163
164func getPrivateIP(addresses []net.Addr) (net.IP, error) {
165	var candidates []net.IP
166
167	// Find private IPv4 address
168	for _, rawAddr := range addresses {
169		var ip net.IP
170		switch addr := rawAddr.(type) {
171		case *net.IPAddr:
172			ip = addr.IP
173		case *net.IPNet:
174			ip = addr.IP
175		default:
176			continue
177		}
178
179		if ip.To4() == nil {
180			continue
181		}
182		if !isPrivateIP(ip.String()) {
183			continue
184		}
185		candidates = append(candidates, ip)
186	}
187	numIps := len(candidates)
188	switch numIps {
189	case 0:
190		return nil, fmt.Errorf("No private IP address found")
191	case 1:
192		return candidates[0], nil
193	default:
194		return nil, fmt.Errorf("Multiple private IPs found. Please configure one.")
195	}
196
197}
198
199// GetPublicIPv6 is used to return the first public IP address
200// associated with an interface on the machine
201func GetPublicIPv6() (net.IP, error) {
202	addresses, err := net.InterfaceAddrs()
203	if err != nil {
204		return nil, fmt.Errorf("Failed to get interface addresses: %v", err)
205	}
206
207	return getPublicIPv6(addresses)
208}
209
210func isUniqueLocalAddress(ip net.IP) bool {
211	return len(ip) == net.IPv6len && ip[0] == 0xfc && ip[1] == 0x00
212}
213
214func getPublicIPv6(addresses []net.Addr) (net.IP, error) {
215	var candidates []net.IP
216
217	// Find public IPv6 address
218	for _, rawAddr := range addresses {
219		var ip net.IP
220		switch addr := rawAddr.(type) {
221		case *net.IPAddr:
222			ip = addr.IP
223		case *net.IPNet:
224			ip = addr.IP
225		default:
226			continue
227		}
228
229		if ip.To4() != nil {
230			continue
231		}
232
233		if ip.IsLinkLocalUnicast() || isUniqueLocalAddress(ip) || ip.IsLoopback() {
234			continue
235		}
236		candidates = append(candidates, ip)
237	}
238	numIps := len(candidates)
239	switch numIps {
240	case 0:
241		return nil, fmt.Errorf("No public IPv6 address found")
242	case 1:
243		return candidates[0], nil
244	default:
245		return nil, fmt.Errorf("Multiple public IPv6 addresses found. Please configure one.")
246	}
247}
248
249// Converts bytes to an integer
250func bytesToUint64(b []byte) uint64 {
251	return binary.BigEndian.Uint64(b)
252}
253
254// Converts a uint to a byte slice
255func uint64ToBytes(u uint64) []byte {
256	buf := make([]byte, 8)
257	binary.BigEndian.PutUint64(buf, u)
258	return buf
259}
260
261// runtimeStats is used to return various runtime information
262func runtimeStats() map[string]string {
263	return map[string]string{
264		"os":         runtime.GOOS,
265		"arch":       runtime.GOARCH,
266		"version":    runtime.Version(),
267		"max_procs":  strconv.FormatInt(int64(runtime.GOMAXPROCS(0)), 10),
268		"goroutines": strconv.FormatInt(int64(runtime.NumGoroutine()), 10),
269		"cpu_count":  strconv.FormatInt(int64(runtime.NumCPU()), 10),
270	}
271}
272
273// ServersMeetMinimumVersion returns whether the given alive servers are at least on the
274// given Consul version
275func ServersMeetMinimumVersion(members []serf.Member, minVersion *version.Version) bool {
276	for _, member := range members {
277		if valid, parts := metadata.IsConsulServer(member); valid && parts.Status == serf.StatusAlive {
278			if parts.Build.LessThan(minVersion) {
279				return false
280			}
281		}
282	}
283
284	return true
285}
286
287func ServersGetACLMode(members []serf.Member, leader string, datacenter string) (numServers int, mode structs.ACLMode, leaderMode structs.ACLMode) {
288	numServers = 0
289	mode = structs.ACLModeEnabled
290	leaderMode = structs.ACLModeUnknown
291	for _, member := range members {
292		if valid, parts := metadata.IsConsulServer(member); valid {
293
294			if datacenter != "" && parts.Datacenter != datacenter {
295				continue
296			}
297
298			numServers += 1
299
300			if memberAddr := (&net.TCPAddr{IP: member.Addr, Port: parts.Port}).String(); memberAddr == leader {
301				leaderMode = parts.ACLs
302			}
303
304			switch parts.ACLs {
305			case structs.ACLModeDisabled:
306				// anything disabled means we cant enable ACLs
307				mode = structs.ACLModeDisabled
308			case structs.ACLModeEnabled:
309				// do nothing
310			case structs.ACLModeLegacy:
311				// This covers legacy mode and older server versions that don't advertise ACL support
312				if mode != structs.ACLModeDisabled && mode != structs.ACLModeUnknown {
313					mode = structs.ACLModeLegacy
314				}
315			default:
316				if mode != structs.ACLModeDisabled {
317					mode = structs.ACLModeUnknown
318				}
319			}
320		}
321	}
322
323	return
324}
325