Skip to content

Commit adb1e03

Browse files
committed
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 <[email protected]> Reviewed-by: Russ Cox <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]>
1 parent 29eb7d1 commit adb1e03

File tree

3 files changed

+602
-2
lines changed

3 files changed

+602
-2
lines changed

src/net/addrselect.go

+381
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,381 @@
1+
// Copyright 2015 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
6+
7+
// Minimal RFC 6724 address selection.
8+
9+
package net
10+
11+
import "sort"
12+
13+
func sortByRFC6724(addrs []IPAddr) {
14+
if len(addrs) < 2 {
15+
return
16+
}
17+
sortByRFC6724withSrcs(addrs, srcAddrs(addrs))
18+
}
19+
20+
func sortByRFC6724withSrcs(addrs []IPAddr, srcs []IP) {
21+
if len(addrs) != len(srcs) {
22+
panic("internal error")
23+
}
24+
addrAttr := make([]ipAttr, len(addrs))
25+
srcAttr := make([]ipAttr, len(srcs))
26+
for i, v := range addrs {
27+
addrAttr[i] = ipAttrOf(v.IP)
28+
srcAttr[i] = ipAttrOf(srcs[i])
29+
}
30+
sort.Stable(&byRFC6724{
31+
addrs: addrs,
32+
addrAttr: addrAttr,
33+
srcs: srcs,
34+
srcAttr: srcAttr,
35+
})
36+
}
37+
38+
// srcsAddrs tries to UDP-connect to each address to see if it has a
39+
// route. (This doesn't send any packets). The destination port
40+
// number is irrelevant.
41+
func srcAddrs(addrs []IPAddr) []IP {
42+
srcs := make([]IP, len(addrs))
43+
for i := range addrs {
44+
conn, err := Dial("udp", JoinHostPort(addrs[i].IP.String(), "1234"))
45+
if err == nil {
46+
if ua, ok := conn.LocalAddr().(*UDPAddr); ok {
47+
srcs[i] = ua.IP
48+
}
49+
conn.Close()
50+
}
51+
}
52+
return srcs
53+
}
54+
55+
type ipAttr struct {
56+
Scope scope
57+
Precedence uint8
58+
Label uint8
59+
}
60+
61+
func ipAttrOf(ip IP) ipAttr {
62+
if ip == nil {
63+
return ipAttr{}
64+
}
65+
match := rfc6724policyTable.Classify(ip)
66+
return ipAttr{
67+
Scope: classifyScope(ip),
68+
Precedence: match.Precedence,
69+
Label: match.Label,
70+
}
71+
}
72+
73+
type byRFC6724 struct {
74+
addrs []IPAddr // addrs to sort
75+
addrAttr []ipAttr
76+
srcs []IP // or nil if unreachable
77+
srcAttr []ipAttr
78+
}
79+
80+
func (s *byRFC6724) Len() int { return len(s.addrs) }
81+
82+
func (s *byRFC6724) Swap(i, j int) {
83+
s.addrs[i], s.addrs[j] = s.addrs[j], s.addrs[i]
84+
s.srcs[i], s.srcs[j] = s.srcs[j], s.srcs[i]
85+
s.addrAttr[i], s.addrAttr[j] = s.addrAttr[j], s.addrAttr[i]
86+
s.srcAttr[i], s.srcAttr[j] = s.srcAttr[j], s.srcAttr[i]
87+
}
88+
89+
// Less reports whether i is a better destination address for this
90+
// host than j.
91+
//
92+
// The algorithm and variable names comes from RFC 6724 section 6.
93+
func (s *byRFC6724) Less(i, j int) bool {
94+
DA := s.addrs[i].IP
95+
DB := s.addrs[j].IP
96+
SourceDA := s.srcs[i]
97+
SourceDB := s.srcs[j]
98+
attrDA := &s.addrAttr[i]
99+
attrDB := &s.addrAttr[j]
100+
attrSourceDA := &s.srcAttr[i]
101+
attrSourceDB := &s.srcAttr[j]
102+
103+
const preferDA = true
104+
const preferDB = false
105+
106+
// Rule 1: Avoid unusable destinations.
107+
// If DB is known to be unreachable or if Source(DB) is undefined, then
108+
// prefer DA. Similarly, if DA is known to be unreachable or if
109+
// Source(DA) is undefined, then prefer DB.
110+
if SourceDA == nil && SourceDB == nil {
111+
return false // "equal"
112+
}
113+
if SourceDB == nil {
114+
return preferDA
115+
}
116+
if SourceDA == nil {
117+
return preferDB
118+
}
119+
120+
// Rule 2: Prefer matching scope.
121+
// If Scope(DA) = Scope(Source(DA)) and Scope(DB) <> Scope(Source(DB)),
122+
// then prefer DA. Similarly, if Scope(DA) <> Scope(Source(DA)) and
123+
// Scope(DB) = Scope(Source(DB)), then prefer DB.
124+
if attrDA.Scope == attrSourceDA.Scope && attrDB.Scope != attrSourceDB.Scope {
125+
return preferDA
126+
}
127+
if attrDA.Scope != attrSourceDA.Scope && attrDB.Scope == attrSourceDB.Scope {
128+
return preferDB
129+
}
130+
131+
// Rule 3: Avoid deprecated addresses.
132+
// If Source(DA) is deprecated and Source(DB) is not, then prefer DB.
133+
// Similarly, if Source(DA) is not deprecated and Source(DB) is
134+
// deprecated, then prefer DA.
135+
136+
// TODO(bradfitz): implement? low priority for now.
137+
138+
// Rule 4: Prefer home addresses.
139+
// If Source(DA) is simultaneously a home address and care-of address
140+
// and Source(DB) is not, then prefer DA. Similarly, if Source(DB) is
141+
// simultaneously a home address and care-of address and Source(DA) is
142+
// not, then prefer DB.
143+
144+
// TODO(bradfitz): implement? low priority for now.
145+
146+
// Rule 5: Prefer matching label.
147+
// If Label(Source(DA)) = Label(DA) and Label(Source(DB)) <> Label(DB),
148+
// then prefer DA. Similarly, if Label(Source(DA)) <> Label(DA) and
149+
// Label(Source(DB)) = Label(DB), then prefer DB.
150+
if attrSourceDA.Label == attrDA.Label &&
151+
attrSourceDB.Label != attrDB.Label {
152+
return preferDA
153+
}
154+
if attrSourceDA.Label != attrDA.Label &&
155+
attrSourceDB.Label == attrDB.Label {
156+
return preferDB
157+
}
158+
159+
// Rule 6: Prefer higher precedence.
160+
// If Precedence(DA) > Precedence(DB), then prefer DA. Similarly, if
161+
// Precedence(DA) < Precedence(DB), then prefer DB.
162+
if attrDA.Precedence > attrDB.Precedence {
163+
return preferDA
164+
}
165+
if attrDA.Precedence < attrDB.Precedence {
166+
return preferDB
167+
}
168+
169+
// Rule 7: Prefer native transport.
170+
// If DA is reached via an encapsulating transition mechanism (e.g.,
171+
// IPv6 in IPv4) and DB is not, then prefer DB. Similarly, if DB is
172+
// reached via encapsulation and DA is not, then prefer DA.
173+
174+
// TODO(bradfitz): implement? low priority for now.
175+
176+
// Rule 8: Prefer smaller scope.
177+
// If Scope(DA) < Scope(DB), then prefer DA. Similarly, if Scope(DA) >
178+
// Scope(DB), then prefer DB.
179+
if attrDA.Scope < attrDB.Scope {
180+
return preferDA
181+
}
182+
if attrDA.Scope > attrDB.Scope {
183+
return preferDB
184+
}
185+
186+
// Rule 9: Use longest matching prefix.
187+
// When DA and DB belong to the same address family (both are IPv6 or
188+
// both are IPv4): If CommonPrefixLen(Source(DA), DA) >
189+
// CommonPrefixLen(Source(DB), DB), then prefer DA. Similarly, if
190+
// CommonPrefixLen(Source(DA), DA) < CommonPrefixLen(Source(DB), DB),
191+
// then prefer DB.
192+
da4 := DA.To4() != nil
193+
db4 := DB.To4() != nil
194+
if da4 == db4 {
195+
commonA := commonPrefixLen(SourceDA, DA)
196+
commonB := commonPrefixLen(SourceDB, DB)
197+
if commonA > commonB {
198+
return preferDA
199+
}
200+
if commonA < commonB {
201+
return preferDB
202+
}
203+
}
204+
205+
// Rule 10: Otherwise, leave the order unchanged.
206+
// If DA preceded DB in the original list, prefer DA.
207+
// Otherwise, prefer DB.
208+
return false // "equal"
209+
}
210+
211+
type policyTableEntry struct {
212+
Prefix *IPNet
213+
Precedence uint8
214+
Label uint8
215+
}
216+
217+
type policyTable []policyTableEntry
218+
219+
// RFC 6724 section 2.1.
220+
var rfc6724policyTable = policyTable{
221+
{
222+
Prefix: mustCIDR("::1/128"),
223+
Precedence: 50,
224+
Label: 0,
225+
},
226+
{
227+
Prefix: mustCIDR("::/0"),
228+
Precedence: 40,
229+
Label: 1,
230+
},
231+
{
232+
// IPv4-compatible, etc.
233+
Prefix: mustCIDR("::ffff:0:0/96"),
234+
Precedence: 35,
235+
Label: 4,
236+
},
237+
{
238+
// 6to4
239+
Prefix: mustCIDR("2002::/16"),
240+
Precedence: 30,
241+
Label: 2,
242+
},
243+
{
244+
// Teredo
245+
Prefix: mustCIDR("2001::/32"),
246+
Precedence: 5,
247+
Label: 5,
248+
},
249+
{
250+
Prefix: mustCIDR("fc00::/7"),
251+
Precedence: 3,
252+
Label: 13,
253+
},
254+
{
255+
Prefix: mustCIDR("::/96"),
256+
Precedence: 1,
257+
Label: 3,
258+
},
259+
{
260+
Prefix: mustCIDR("fec0::/10"),
261+
Precedence: 1,
262+
Label: 11,
263+
},
264+
{
265+
Prefix: mustCIDR("3ffe::/16"),
266+
Precedence: 1,
267+
Label: 12,
268+
},
269+
}
270+
271+
func init() {
272+
sort.Sort(sort.Reverse(byMaskLength(rfc6724policyTable)))
273+
}
274+
275+
// byMaskLength sorts policyTableEntry by the size of their Prefix.Mask.Size,
276+
// from smallest mask, to largest.
277+
type byMaskLength []policyTableEntry
278+
279+
func (s byMaskLength) Len() int { return len(s) }
280+
func (s byMaskLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
281+
func (s byMaskLength) Less(i, j int) bool {
282+
isize, _ := s[i].Prefix.Mask.Size()
283+
jsize, _ := s[j].Prefix.Mask.Size()
284+
return isize < jsize
285+
}
286+
287+
// mustCIDR calls ParseCIDR and panics on any error, or if the network
288+
// is not IPv6.
289+
func mustCIDR(s string) *IPNet {
290+
ip, ipNet, err := ParseCIDR(s)
291+
if err != nil {
292+
panic(err.Error())
293+
}
294+
if len(ip) != IPv6len {
295+
panic("unexpected IP length")
296+
}
297+
return ipNet
298+
}
299+
300+
// Classify returns the policyTableEntry of the entry with the longest
301+
// matching prefix that contains ip.
302+
// The table t must be sorted from largest mask size to smallest.
303+
func (t policyTable) Classify(ip IP) policyTableEntry {
304+
for _, ent := range t {
305+
if ent.Prefix.Contains(ip) {
306+
return ent
307+
}
308+
}
309+
return policyTableEntry{}
310+
}
311+
312+
// RFC 6724 section 3.1.
313+
type scope uint8
314+
315+
const (
316+
scopeInterfaceLocal scope = 0x1
317+
scopeLinkLocal scope = 0x2
318+
scopeAdminLocal scope = 0x4
319+
scopeSiteLocal scope = 0x5
320+
scopeOrgLocal scope = 0x8
321+
scopeGlobal scope = 0xe
322+
)
323+
324+
func classifyScope(ip IP) scope {
325+
if ip.IsLoopback() || ip.IsLinkLocalUnicast() {
326+
return scopeLinkLocal
327+
}
328+
if len(ip) == IPv6len && ip.To4() == nil && ip.IsMulticast() {
329+
return scope(ip[1] & 0xf)
330+
}
331+
// TODO: are there unicast scopeAdminLocal, scopeSiteLocal,
332+
// scopeOrgLocal? Better question: are those even used?
333+
return scopeGlobal
334+
}
335+
336+
// commonPrefixLen reports the length of the longest prefix (looking
337+
// at the most significant, or leftmost, bits) that the
338+
// two addresses have in common, up to the length of a's prefix (i.e.,
339+
// the portion of the address not including the interface ID).
340+
//
341+
// If a or b is an IPv4 address as an IPv6 address, the IPv4 addresses
342+
// are compared (with max common prefix length of 32).
343+
// If a and b are different IP versions, 0 is returned.
344+
//
345+
// See https://tools.ietf.org/html/rfc6724#section-2.2
346+
func commonPrefixLen(a, b IP) (cpl int) {
347+
if a4 := a.To4(); a4 != nil {
348+
a = a4
349+
}
350+
if b4 := b.To4(); b4 != nil {
351+
b = b4
352+
}
353+
if len(a) != len(b) {
354+
return 0
355+
}
356+
// If IPv6, only up to the prefix (first 64 bits)
357+
if len(a) > 8 {
358+
a = a[:8]
359+
b = b[:8]
360+
}
361+
for len(a) > 0 {
362+
if a[0] == b[0] {
363+
cpl += 8
364+
a = a[1:]
365+
b = b[1:]
366+
continue
367+
}
368+
bits := 8
369+
ab, bb := a[0], b[0]
370+
for {
371+
ab >>= 1
372+
bb >>= 1
373+
bits--
374+
if ab == bb {
375+
cpl += bits
376+
return
377+
}
378+
}
379+
}
380+
return
381+
}

0 commit comments

Comments
 (0)