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  }