Source file
src/net/addrselect.go
1
2
3
4
5
6
7 package net
8
9 import (
10 "net/netip"
11 "slices"
12 )
13
14 func sortByRFC6724(addrs []IPAddr) {
15 if len(addrs) < 2 {
16 return
17 }
18 sortByRFC6724withSrcs(addrs, srcAddrs(addrs))
19 }
20
21 func sortByRFC6724withSrcs(addrs []IPAddr, srcs []netip.Addr) {
22 if len(addrs) != len(srcs) {
23 panic("internal error")
24 }
25 addrInfos := make([]byRFC6724Info, len(addrs))
26 for i, v := range addrs {
27 addrAttrIP, _ := netip.AddrFromSlice(v.IP)
28 addrInfos[i] = byRFC6724Info{
29 addr: addrs[i],
30 addrAttr: ipAttrOf(addrAttrIP),
31 src: srcs[i],
32 srcAttr: ipAttrOf(srcs[i]),
33 }
34 }
35 slices.SortStableFunc(addrInfos, compareByRFC6724)
36 for i := range addrInfos {
37 addrs[i] = addrInfos[i].addr
38 }
39 }
40
41
42
43
44 func srcAddrs(addrs []IPAddr) []netip.Addr {
45 srcs := make([]netip.Addr, len(addrs))
46 dst := UDPAddr{Port: 53}
47 for i := range addrs {
48 dst.IP = addrs[i].IP
49 dst.Zone = addrs[i].Zone
50 c, err := DialUDP("udp", nil, &dst)
51 if err == nil {
52 if src, ok := c.LocalAddr().(*UDPAddr); ok {
53 srcs[i], _ = netip.AddrFromSlice(src.IP)
54 }
55 c.Close()
56 }
57 }
58 return srcs
59 }
60
61 type ipAttr struct {
62 Scope scope
63 Precedence uint8
64 Label uint8
65 }
66
67 func ipAttrOf(ip netip.Addr) ipAttr {
68 if !ip.IsValid() {
69 return ipAttr{}
70 }
71 match := rfc6724policyTable.Classify(ip)
72 return ipAttr{
73 Scope: classifyScope(ip),
74 Precedence: match.Precedence,
75 Label: match.Label,
76 }
77 }
78
79 type byRFC6724Info struct {
80 addr IPAddr
81 addrAttr ipAttr
82 src netip.Addr
83 srcAttr ipAttr
84 }
85
86
87
88
89
90 func compareByRFC6724(a, b byRFC6724Info) int {
91 DA := a.addr.IP
92 DB := b.addr.IP
93 SourceDA := a.src
94 SourceDB := b.src
95 attrDA := &a.addrAttr
96 attrDB := &b.addrAttr
97 attrSourceDA := &a.srcAttr
98 attrSourceDB := &b.srcAttr
99
100 const preferDA = -1
101 const preferDB = 1
102
103
104
105
106
107 if !SourceDA.IsValid() && !SourceDB.IsValid() {
108 return 0
109 }
110 if !SourceDB.IsValid() {
111 return preferDA
112 }
113 if !SourceDA.IsValid() {
114 return preferDB
115 }
116
117
118
119
120
121 if attrDA.Scope == attrSourceDA.Scope && attrDB.Scope != attrSourceDB.Scope {
122 return preferDA
123 }
124 if attrDA.Scope != attrSourceDA.Scope && attrDB.Scope == attrSourceDB.Scope {
125 return preferDB
126 }
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147 if attrSourceDA.Label == attrDA.Label &&
148 attrSourceDB.Label != attrDB.Label {
149 return preferDA
150 }
151 if attrSourceDA.Label != attrDA.Label &&
152 attrSourceDB.Label == attrDB.Label {
153 return preferDB
154 }
155
156
157
158
159 if attrDA.Precedence > attrDB.Precedence {
160 return preferDA
161 }
162 if attrDA.Precedence < attrDB.Precedence {
163 return preferDB
164 }
165
166
167
168
169
170
171
172
173
174
175
176 if attrDA.Scope < attrDB.Scope {
177 return preferDA
178 }
179 if attrDA.Scope > attrDB.Scope {
180 return preferDB
181 }
182
183
184
185
186
187
188
189
190
191
192 if DA.To4() == nil && DB.To4() == nil {
193 commonA := commonPrefixLen(SourceDA, DA)
194 commonB := commonPrefixLen(SourceDB, DB)
195
196 if commonA > commonB {
197 return preferDA
198 }
199 if commonA < commonB {
200 return preferDB
201 }
202 }
203
204
205
206
207 return 0
208 }
209
210 type policyTableEntry struct {
211 Prefix netip.Prefix
212 Precedence uint8
213 Label uint8
214 }
215
216 type policyTable []policyTableEntry
217
218
219
220 var rfc6724policyTable = policyTable{
221 {
222
223 Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}), 128),
224 Precedence: 50,
225 Label: 0,
226 },
227 {
228
229
230 Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}), 96),
231 Precedence: 35,
232 Label: 4,
233 },
234 {
235
236 Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 96),
237 Precedence: 1,
238 Label: 3,
239 },
240 {
241
242
243 Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0x20, 0x01}), 32),
244 Precedence: 5,
245 Label: 5,
246 },
247 {
248
249
250 Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0x20, 0x02}), 16),
251 Precedence: 30,
252 Label: 2,
253 },
254 {
255
256 Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0x3f, 0xfe}), 16),
257 Precedence: 1,
258 Label: 12,
259 },
260 {
261
262 Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0xfe, 0xc0}), 10),
263 Precedence: 1,
264 Label: 11,
265 },
266 {
267
268 Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0xfc}), 7),
269 Precedence: 3,
270 Label: 13,
271 },
272 {
273
274 Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 0),
275 Precedence: 40,
276 Label: 1,
277 },
278 }
279
280
281
282
283 func (t policyTable) Classify(ip netip.Addr) policyTableEntry {
284
285 if ip.Is4() {
286 ip = netip.AddrFrom16(ip.As16())
287 }
288 for _, ent := range t {
289 if ent.Prefix.Contains(ip) {
290 return ent
291 }
292 }
293 return policyTableEntry{}
294 }
295
296
297 type scope uint8
298
299 const (
300 scopeInterfaceLocal scope = 0x1
301 scopeLinkLocal scope = 0x2
302 scopeAdminLocal scope = 0x4
303 scopeSiteLocal scope = 0x5
304 scopeOrgLocal scope = 0x8
305 scopeGlobal scope = 0xe
306 )
307
308 func classifyScope(ip netip.Addr) scope {
309 if ip.IsLoopback() || ip.IsLinkLocalUnicast() {
310 return scopeLinkLocal
311 }
312 ipv6 := ip.Is6() && !ip.Is4In6()
313 ipv6AsBytes := ip.As16()
314 if ipv6 && ip.IsMulticast() {
315 return scope(ipv6AsBytes[1] & 0xf)
316 }
317
318
319 if ipv6 && ipv6AsBytes[0] == 0xfe && ipv6AsBytes[1]&0xc0 == 0xc0 {
320 return scopeSiteLocal
321 }
322 return scopeGlobal
323 }
324
325
326
327
328
329
330
331
332
333
334
335 func commonPrefixLen(a netip.Addr, b IP) (cpl int) {
336 if b4 := b.To4(); b4 != nil {
337 b = b4
338 }
339 aAsSlice := a.AsSlice()
340 if len(aAsSlice) != len(b) {
341 return 0
342 }
343
344 if len(aAsSlice) > 8 {
345 aAsSlice = aAsSlice[:8]
346 b = b[:8]
347 }
348 for len(aAsSlice) > 0 {
349 if aAsSlice[0] == b[0] {
350 cpl += 8
351 aAsSlice = aAsSlice[1:]
352 b = b[1:]
353 continue
354 }
355 bits := 8
356 ab, bb := aAsSlice[0], b[0]
357 for {
358 ab >>= 1
359 bb >>= 1
360 bits--
361 if ab == bb {
362 cpl += bits
363 return
364 }
365 }
366 }
367 return
368 }
369
View as plain text