// 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.// Minimal RFC 6724 address selection.package netimport ()func sortByRFC6724( []IPAddr) {iflen() < 2 {return }sortByRFC6724withSrcs(, srcAddrs())}func sortByRFC6724withSrcs( []IPAddr, []netip.Addr) {iflen() != len() {panic("internal error") } := make([]ipAttr, len()) := make([]ipAttr, len())for , := range { , := netip.AddrFromSlice(.IP) [] = ipAttrOf() [] = ipAttrOf([]) }sort.Stable(&byRFC6724{addrs: ,addrAttr: ,srcs: ,srcAttr: , })}// srcAddrs 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( []IPAddr) []netip.Addr { := make([]netip.Addr, len()) := UDPAddr{Port: 53}for := range { .IP = [].IP .Zone = [].Zone , := DialUDP("udp", nil, &)if == nil {if , := .LocalAddr().(*UDPAddr); { [], _ = netip.AddrFromSlice(.IP) } .Close() } }return}type ipAttr struct { Scope scope Precedence uint8 Label uint8}func ipAttrOf( netip.Addr) ipAttr {if !.IsValid() {returnipAttr{} } := rfc6724policyTable.Classify()returnipAttr{Scope: classifyScope(),Precedence: .Precedence,Label: .Label, }}type byRFC6724 struct { addrs []IPAddr// addrs to sort addrAttr []ipAttr srcs []netip.Addr// or not valid addr if unreachable srcAttr []ipAttr}func ( *byRFC6724) () int { returnlen(.addrs) }func ( *byRFC6724) (, int) { .addrs[], .addrs[] = .addrs[], .addrs[] .srcs[], .srcs[] = .srcs[], .srcs[] .addrAttr[], .addrAttr[] = .addrAttr[], .addrAttr[] .srcAttr[], .srcAttr[] = .srcAttr[], .srcAttr[]}// 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 ( *byRFC6724) (, int) bool { := .addrs[].IP := .addrs[].IP := .srcs[] := .srcs[] := &.addrAttr[] := &.addrAttr[] := &.srcAttr[] := &.srcAttr[]const = trueconst = 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 !.IsValid() && !.IsValid() {returnfalse// "equal" }if !.IsValid() {return }if !.IsValid() {return }// 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 .Scope == .Scope && .Scope != .Scope {return }if .Scope != .Scope && .Scope == .Scope {return }// 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 .Label == .Label && .Label != .Label {return }if .Label != .Label && .Label == .Label {return }// Rule 6: Prefer higher precedence. // If Precedence(DA) > Precedence(DB), then prefer DA. Similarly, if // Precedence(DA) < Precedence(DB), then prefer DB.if .Precedence > .Precedence {return }if .Precedence < .Precedence {return }// 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 .Scope < .Scope {return }if .Scope > .Scope {return }// Rule 9: Use the longest matching prefix. // When DA and DB belong to the same address family (both are IPv6 or // both are IPv4 [but see below]): 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. // // However, applying this rule to IPv4 addresses causes // problems (see issues 13283 and 18518), so limit to IPv6.if .To4() == nil && .To4() == nil { := commonPrefixLen(, ) := commonPrefixLen(, )if > {return }if < {return } }// Rule 10: Otherwise, leave the order unchanged. // If DA preceded DB in the original list, prefer DA. // Otherwise, prefer DB.returnfalse// "equal"}type policyTableEntry struct { Prefix netip.Prefix Precedence uint8 Label uint8}type policyTable []policyTableEntry// RFC 6724 section 2.1.// Items are sorted by the size of their Prefix.Mask.Size,var rfc6724policyTable = policyTable{ {// "::1/128"Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}), 128),Precedence: 50,Label: 0, }, {// "::ffff:0:0/96" // IPv4-compatible, etc.Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}), 96),Precedence: 35,Label: 4, }, {// "::/96"Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 96),Precedence: 1,Label: 3, }, {// "2001::/32" // TeredoPrefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0x20, 0x01}), 32),Precedence: 5,Label: 5, }, {// "2002::/16" // 6to4Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0x20, 0x02}), 16),Precedence: 30,Label: 2, }, {// "3ffe::/16"Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0x3f, 0xfe}), 16),Precedence: 1,Label: 12, }, {// "fec0::/10"Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0xfe, 0xc0}), 10),Precedence: 1,Label: 11, }, {// "fc00::/7"Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0xfc}), 7),Precedence: 3,Label: 13, }, {// "::/0"Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 0),Precedence: 40,Label: 1, },}// 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 ( policyTable) ( netip.Addr) policyTableEntry {// Prefix.Contains() will not match an IPv6 prefix for an IPv4 address.if .Is4() { = netip.AddrFrom16(.As16()) }for , := range {if .Prefix.Contains() {return } }returnpolicyTableEntry{}}// RFC 6724 section 3.1.type scope uint8const ( scopeInterfaceLocal scope = 0x1 scopeLinkLocal scope = 0x2 scopeAdminLocal scope = 0x4 scopeSiteLocal scope = 0x5 scopeOrgLocal scope = 0x8 scopeGlobal scope = 0xe)func classifyScope( netip.Addr) scope {if .IsLoopback() || .IsLinkLocalUnicast() {returnscopeLinkLocal } := .Is6() && !.Is4In6() := .As16()if && .IsMulticast() {returnscope([1] & 0xf) }// Site-local addresses are defined in RFC 3513 section 2.5.6 // (and deprecated in RFC 3879).if && [0] == 0xfe && [1]&0xc0 == 0xc0 {returnscopeSiteLocal }returnscopeGlobal}// 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.2func commonPrefixLen( netip.Addr, IP) ( int) {if := .To4(); != nil { = } := .AsSlice()iflen() != len() {return0 }// If IPv6, only up to the prefix (first 64 bits)iflen() > 8 { = [:8] = [:8] }forlen() > 0 {if [0] == [0] { += 8 = [1:] = [1:]continue } := 8 , := [0], [0]for { >>= 1 >>= 1 --if == { += return } } }return}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.