net: RFC 6724 address selection

At least the most important parts, I think.

Fixes #10552

Change-Id: I1a03c5405bdbef337e0245d226e9247d3d067393
Reviewed-on: https://go-review.googlesource.com/12246
Reviewed-by: Andrew Gerrand <adg@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/src/net/addrselect.go b/src/net/addrselect.go
new file mode 100644
index 0000000..56a618a
--- /dev/null
+++ b/src/net/addrselect.go
@@ -0,0 +1,381 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+
+// Minimal RFC 6724 address selection.
+
+package net
+
+import "sort"
+
+func sortByRFC6724(addrs []IPAddr) {
+	if len(addrs) < 2 {
+		return
+	}
+	sortByRFC6724withSrcs(addrs, srcAddrs(addrs))
+}
+
+func sortByRFC6724withSrcs(addrs []IPAddr, srcs []IP) {
+	if len(addrs) != len(srcs) {
+		panic("internal error")
+	}
+	addrAttr := make([]ipAttr, len(addrs))
+	srcAttr := make([]ipAttr, len(srcs))
+	for i, v := range addrs {
+		addrAttr[i] = ipAttrOf(v.IP)
+		srcAttr[i] = ipAttrOf(srcs[i])
+	}
+	sort.Stable(&byRFC6724{
+		addrs:    addrs,
+		addrAttr: addrAttr,
+		srcs:     srcs,
+		srcAttr:  srcAttr,
+	})
+}
+
+// srcsAddrs tries to UDP-connect to each address to see if it has a
+// route. (This doesn't send any packets). The destination port
+// number is irrelevant.
+func srcAddrs(addrs []IPAddr) []IP {
+	srcs := make([]IP, len(addrs))
+	for i := range addrs {
+		conn, err := Dial("udp", JoinHostPort(addrs[i].IP.String(), "1234"))
+		if err == nil {
+			if ua, ok := conn.LocalAddr().(*UDPAddr); ok {
+				srcs[i] = ua.IP
+			}
+			conn.Close()
+		}
+	}
+	return srcs
+}
+
+type ipAttr struct {
+	Scope      scope
+	Precedence uint8
+	Label      uint8
+}
+
+func ipAttrOf(ip IP) ipAttr {
+	if ip == nil {
+		return ipAttr{}
+	}
+	match := rfc6724policyTable.Classify(ip)
+	return ipAttr{
+		Scope:      classifyScope(ip),
+		Precedence: match.Precedence,
+		Label:      match.Label,
+	}
+}
+
+type byRFC6724 struct {
+	addrs    []IPAddr // addrs to sort
+	addrAttr []ipAttr
+	srcs     []IP // or nil if unreachable
+	srcAttr  []ipAttr
+}
+
+func (s *byRFC6724) Len() int { return len(s.addrs) }
+
+func (s *byRFC6724) Swap(i, j int) {
+	s.addrs[i], s.addrs[j] = s.addrs[j], s.addrs[i]
+	s.srcs[i], s.srcs[j] = s.srcs[j], s.srcs[i]
+	s.addrAttr[i], s.addrAttr[j] = s.addrAttr[j], s.addrAttr[i]
+	s.srcAttr[i], s.srcAttr[j] = s.srcAttr[j], s.srcAttr[i]
+}
+
+// Less reports whether i is a better destination address for this
+// host than j.
+//
+// The algorithm and variable names comes from RFC 6724 section 6.
+func (s *byRFC6724) Less(i, j int) bool {
+	DA := s.addrs[i].IP
+	DB := s.addrs[j].IP
+	SourceDA := s.srcs[i]
+	SourceDB := s.srcs[j]
+	attrDA := &s.addrAttr[i]
+	attrDB := &s.addrAttr[j]
+	attrSourceDA := &s.srcAttr[i]
+	attrSourceDB := &s.srcAttr[j]
+
+	const preferDA = true
+	const preferDB = false
+
+	// Rule 1: Avoid unusable destinations.
+	// If DB is known to be unreachable or if Source(DB) is undefined, then
+	// prefer DA.  Similarly, if DA is known to be unreachable or if
+	// Source(DA) is undefined, then prefer DB.
+	if SourceDA == nil && SourceDB == nil {
+		return false // "equal"
+	}
+	if SourceDB == nil {
+		return preferDA
+	}
+	if SourceDA == nil {
+		return preferDB
+	}
+
+	// Rule 2: Prefer matching scope.
+	// If Scope(DA) = Scope(Source(DA)) and Scope(DB) <> Scope(Source(DB)),
+	// then prefer DA.  Similarly, if Scope(DA) <> Scope(Source(DA)) and
+	// Scope(DB) = Scope(Source(DB)), then prefer DB.
+	if attrDA.Scope == attrSourceDA.Scope && attrDB.Scope != attrSourceDB.Scope {
+		return preferDA
+	}
+	if attrDA.Scope != attrSourceDA.Scope && attrDB.Scope == attrSourceDB.Scope {
+		return preferDB
+	}
+
+	// Rule 3: Avoid deprecated addresses.
+	// If Source(DA) is deprecated and Source(DB) is not, then prefer DB.
+	// Similarly, if Source(DA) is not deprecated and Source(DB) is
+	// deprecated, then prefer DA.
+
+	// TODO(bradfitz): implement? low priority for now.
+
+	// Rule 4: Prefer home addresses.
+	// If Source(DA) is simultaneously a home address and care-of address
+	// and Source(DB) is not, then prefer DA.  Similarly, if Source(DB) is
+	// simultaneously a home address and care-of address and Source(DA) is
+	// not, then prefer DB.
+
+	// TODO(bradfitz): implement? low priority for now.
+
+	// Rule 5: Prefer matching label.
+	// If Label(Source(DA)) = Label(DA) and Label(Source(DB)) <> Label(DB),
+	// then prefer DA.  Similarly, if Label(Source(DA)) <> Label(DA) and
+	// Label(Source(DB)) = Label(DB), then prefer DB.
+	if attrSourceDA.Label == attrDA.Label &&
+		attrSourceDB.Label != attrDB.Label {
+		return preferDA
+	}
+	if attrSourceDA.Label != attrDA.Label &&
+		attrSourceDB.Label == attrDB.Label {
+		return preferDB
+	}
+
+	// Rule 6: Prefer higher precedence.
+	// If Precedence(DA) > Precedence(DB), then prefer DA.  Similarly, if
+	// Precedence(DA) < Precedence(DB), then prefer DB.
+	if attrDA.Precedence > attrDB.Precedence {
+		return preferDA
+	}
+	if attrDA.Precedence < attrDB.Precedence {
+		return preferDB
+	}
+
+	// Rule 7: Prefer native transport.
+	// If DA is reached via an encapsulating transition mechanism (e.g.,
+	// IPv6 in IPv4) and DB is not, then prefer DB.  Similarly, if DB is
+	// reached via encapsulation and DA is not, then prefer DA.
+
+	// TODO(bradfitz): implement? low priority for now.
+
+	// Rule 8: Prefer smaller scope.
+	// If Scope(DA) < Scope(DB), then prefer DA.  Similarly, if Scope(DA) >
+	// Scope(DB), then prefer DB.
+	if attrDA.Scope < attrDB.Scope {
+		return preferDA
+	}
+	if attrDA.Scope > attrDB.Scope {
+		return preferDB
+	}
+
+	// Rule 9: Use longest matching prefix.
+	// When DA and DB belong to the same address family (both are IPv6 or
+	// both are IPv4): If CommonPrefixLen(Source(DA), DA) >
+	// CommonPrefixLen(Source(DB), DB), then prefer DA.  Similarly, if
+	// CommonPrefixLen(Source(DA), DA) < CommonPrefixLen(Source(DB), DB),
+	// then prefer DB.
+	da4 := DA.To4() != nil
+	db4 := DB.To4() != nil
+	if da4 == db4 {
+		commonA := commonPrefixLen(SourceDA, DA)
+		commonB := commonPrefixLen(SourceDB, DB)
+		if commonA > commonB {
+			return preferDA
+		}
+		if commonA < commonB {
+			return preferDB
+		}
+	}
+
+	// Rule 10: Otherwise, leave the order unchanged.
+	// If DA preceded DB in the original list, prefer DA.
+	// Otherwise, prefer DB.
+	return false // "equal"
+}
+
+type policyTableEntry struct {
+	Prefix     *IPNet
+	Precedence uint8
+	Label      uint8
+}
+
+type policyTable []policyTableEntry
+
+// RFC 6724 section 2.1.
+var rfc6724policyTable = policyTable{
+	{
+		Prefix:     mustCIDR("::1/128"),
+		Precedence: 50,
+		Label:      0,
+	},
+	{
+		Prefix:     mustCIDR("::/0"),
+		Precedence: 40,
+		Label:      1,
+	},
+	{
+		// IPv4-compatible, etc.
+		Prefix:     mustCIDR("::ffff:0:0/96"),
+		Precedence: 35,
+		Label:      4,
+	},
+	{
+		// 6to4
+		Prefix:     mustCIDR("2002::/16"),
+		Precedence: 30,
+		Label:      2,
+	},
+	{
+		// Teredo
+		Prefix:     mustCIDR("2001::/32"),
+		Precedence: 5,
+		Label:      5,
+	},
+	{
+		Prefix:     mustCIDR("fc00::/7"),
+		Precedence: 3,
+		Label:      13,
+	},
+	{
+		Prefix:     mustCIDR("::/96"),
+		Precedence: 1,
+		Label:      3,
+	},
+	{
+		Prefix:     mustCIDR("fec0::/10"),
+		Precedence: 1,
+		Label:      11,
+	},
+	{
+		Prefix:     mustCIDR("3ffe::/16"),
+		Precedence: 1,
+		Label:      12,
+	},
+}
+
+func init() {
+	sort.Sort(sort.Reverse(byMaskLength(rfc6724policyTable)))
+}
+
+// byMaskLength sorts policyTableEntry by the size of their Prefix.Mask.Size,
+// from smallest mask, to largest.
+type byMaskLength []policyTableEntry
+
+func (s byMaskLength) Len() int      { return len(s) }
+func (s byMaskLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s byMaskLength) Less(i, j int) bool {
+	isize, _ := s[i].Prefix.Mask.Size()
+	jsize, _ := s[j].Prefix.Mask.Size()
+	return isize < jsize
+}
+
+// mustCIDR calls ParseCIDR and panics on any error, or if the network
+// is not IPv6.
+func mustCIDR(s string) *IPNet {
+	ip, ipNet, err := ParseCIDR(s)
+	if err != nil {
+		panic(err.Error())
+	}
+	if len(ip) != IPv6len {
+		panic("unexpected IP length")
+	}
+	return ipNet
+}
+
+// Classify returns the policyTableEntry of the entry with the longest
+// matching prefix that contains ip.
+// The table t must be sorted from largest mask size to smallest.
+func (t policyTable) Classify(ip IP) policyTableEntry {
+	for _, ent := range t {
+		if ent.Prefix.Contains(ip) {
+			return ent
+		}
+	}
+	return policyTableEntry{}
+}
+
+// RFC 6724 section 3.1.
+type scope uint8
+
+const (
+	scopeInterfaceLocal scope = 0x1
+	scopeLinkLocal      scope = 0x2
+	scopeAdminLocal     scope = 0x4
+	scopeSiteLocal      scope = 0x5
+	scopeOrgLocal       scope = 0x8
+	scopeGlobal         scope = 0xe
+)
+
+func classifyScope(ip IP) scope {
+	if ip.IsLoopback() || ip.IsLinkLocalUnicast() {
+		return scopeLinkLocal
+	}
+	if len(ip) == IPv6len && ip.To4() == nil && ip.IsMulticast() {
+		return scope(ip[1] & 0xf)
+	}
+	// TODO: are there unicast scopeAdminLocal, scopeSiteLocal,
+	// scopeOrgLocal? Better question: are those even used?
+	return scopeGlobal
+}
+
+// commonPrefixLen reports the length of the longest prefix (looking
+// at the most significant, or leftmost, bits) that the
+// two addresses have in common, up to the length of a's prefix (i.e.,
+// the portion of the address not including the interface ID).
+//
+// If a or b is an IPv4 address as an IPv6 address, the IPv4 addresses
+// are compared (with max common prefix length of 32).
+// If a and b are different IP versions, 0 is returned.
+//
+// See https://tools.ietf.org/html/rfc6724#section-2.2
+func commonPrefixLen(a, b IP) (cpl int) {
+	if a4 := a.To4(); a4 != nil {
+		a = a4
+	}
+	if b4 := b.To4(); b4 != nil {
+		b = b4
+	}
+	if len(a) != len(b) {
+		return 0
+	}
+	// If IPv6, only up to the prefix (first 64 bits)
+	if len(a) > 8 {
+		a = a[:8]
+		b = b[:8]
+	}
+	for len(a) > 0 {
+		if a[0] == b[0] {
+			cpl += 8
+			a = a[1:]
+			b = b[1:]
+			continue
+		}
+		bits := 8
+		ab, bb := a[0], b[0]
+		for {
+			ab >>= 1
+			bb >>= 1
+			bits--
+			if ab == bb {
+				cpl += bits
+				return
+			}
+		}
+	}
+	return
+}