github.com/haraldrudell/parl@v0.4.176/pnet/addr.go (about) 1 /* 2 © 2020–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package pnet 7 8 import ( 9 "net/netip" 10 "strconv" 11 12 "github.com/haraldrudell/parl/perrors" 13 ) 14 15 // IsNonGlobalIPv6 returns if addr is an IPv6 address that should have zone 16 func IsNonGlobalIPv6(addr netip.Addr) (needsZone bool) { 17 return addr.Is6() && ( // 18 addr.IsInterfaceLocalMulticast() || // 0xffx1:: 19 addr.IsLinkLocalMulticast() || // 0xffx2:: 20 addr.IsLinkLocalUnicast() || // 0xfe8:: 21 addr.IsPrivate()) 22 } 23 24 // EnsureZone adds IPv6 zone as applicable 25 // - only non-global IPv6 should have zone 26 // - if acceptNumeric true no index to interface-name translation attempts take place. 27 // otherwise interface-name zone is preferred 28 // - a non-numeric zone is attempted from: addr, ifName 29 // - number cinversion to interfa e is attempted: addr, ifIndex 30 // - acceptNumeric true leaves an existing numeric zone 31 func EnsureZone(addr netip.Addr, ifName string, ifIndex IfIndex, acceptNumeric ...bool) (addr2 netip.Addr, didChange, isNumeric bool, err error) { 32 addr2 = addr 33 var doNumeric bool 34 if len(acceptNumeric) > 0 { 35 doNumeric = acceptNumeric[0] 36 } 37 38 // does addr need zone? 39 if !IsNonGlobalIPv6(addr) { 40 return // this IPv6 address does not need zone return 41 } 42 43 // does addr already have non-numeric zone? 44 var zone = addr.Zone() 45 var ifi IfIndex 46 var hasNumericZone bool 47 if zone != "" { 48 var number int 49 var e error 50 number, e = strconv.Atoi(zone) 51 if e != nil { 52 return // addr already has non-numeric zone 53 } 54 var ifiTry IfIndex 55 ifiTry, e = NewIfIndexInt(number) 56 if hasNumericZone = e == nil && ifiTry.IsValid(); hasNumericZone { 57 if doNumeric { 58 isNumeric = true 59 return // zone is numeric and that should be used return 60 } 61 ifi = ifiTry 62 } 63 } 64 65 // - addr should have zone 66 // - addr has no zone or has numeric zone with doNumeric false 67 68 // use ifName 69 if didChange = ifName != ""; didChange { 70 addr2 = netip.Addr.WithZone(addr, ifName) 71 return 72 } 73 74 // use ifIndex if donumeric is true 75 if doNumeric { 76 if didChange = ifIndex.IsValid(); didChange { 77 isNumeric = true 78 addr2 = netip.Addr.WithZone(addr, ifIndex.String()) 79 return 80 } 81 err = perrors.NewPF("no zone in addr ifIndex ifName") 82 return 83 } 84 85 // attempt translation of ifi ifIndex 86 87 // translate addr numeric zone 88 if hasNumericZone { 89 var z string 90 var isNo bool 91 var e error 92 if z, isNo, e = ifi.Zone(); z != "" && !isNo && e == nil { 93 didChange = true 94 addr2 = netip.Addr.WithZone(addr, z) 95 return 96 } 97 } 98 99 // translate ifIndex 100 if ifIndex.IsValid() { 101 var z string 102 var isNo bool 103 var e error 104 if z, isNo, e = ifIndex.Zone(); z != "" && !isNo && e == nil { 105 didChange = true 106 addr2 = netip.Addr.WithZone(addr, z) 107 return 108 } 109 } 110 111 // no translation is available 112 // doNumeric is false 113 // fallback to any numeric 114 115 // numeric addr.Zone 116 if hasNumericZone { 117 return // best is the numeric zone already in addr return 118 } 119 120 // numeric ifIndex 121 if didChange = ifIndex.IsValid(); didChange { 122 isNumeric = true 123 addr2 = addr.WithZone(ifIndex.String()) 124 return 125 } 126 127 // it’s a failure 128 err = perrors.NewPF("no successful translation or zone in addr ifIndex ifName") 129 130 return 131 } 132 133 //var isDigits = regexp.MustCompile(`^[0-9]+$`).MatchString 134 135 // Zone examines the zone included in addr 136 // - no zone: hasZone: false, isNumeric: false 137 // - numeric zone “1”: hasZone: true, isNumeric: true. 138 // Number is network interface index. 139 // - interface-name zone “eth0”: hasZone: true, isNumeric false 140 func Zone(addr netip.Addr) (zone string, znum int, hasZone, isNumeric bool) { 141 zone = addr.Zone() 142 if hasZone = zone != ""; !hasZone { 143 return 144 } 145 var err error 146 znum, err = strconv.Atoi(zone) 147 isNumeric = err == nil 148 return 149 } 150 151 // Addr46 converts 4in6 IPv6 addresses to IPv4 for consistent IPv4/IPv6 152 // - IPv6 has a special class of addresses represnting an IPv4 address 153 // - IPv6 “::ffff:192.0.2.128” represents the IPv4 address “192.0.2.128” 154 // - Addr46 converts such addresses to IPv4 155 func Addr46(addr netip.Addr) (addr46 netip.Addr) { 156 if addr.Is4In6() { 157 addr46 = netip.AddrFrom4(addr.As4()) 158 } else { 159 addr46 = addr 160 } 161 return 162 }