github.com/haraldrudell/parl@v0.4.176/pnet/pnet.go (about)

     1  /*
     2  © 2021–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  // Package pnet provides IP-related functions with few dependencies beyond the net package
     7  package pnet
     8  
     9  import (
    10  	"net"
    11  	"net/netip"
    12  	"strconv"
    13  	"strings"
    14  )
    15  
    16  const (
    17  	// zeroSuffix is used to shorten IPv4 addresses: “0.0.0.0/1” → “0/1”
    18  	zeroSuffix = ".0"
    19  )
    20  
    21  // DefaultRouteIPv4 is the default route “0/0” for IPv4
    22  var DefaultRouteIPv4 = netip.MustParsePrefix("0.0.0.0/0")
    23  
    24  // VPNRoute0IPv4 is overriding VPN route “0/1” for IPv4
    25  var VPNRoute0IPv4 = netip.MustParsePrefix("0.0.0.0/1")
    26  
    27  // VPNRoute128IPv4 is overriding VPN route “128/1” for IPv4
    28  var VPNRoute128IPv4 = netip.MustParsePrefix("128.0.0.0/1")
    29  
    30  // DefaultRouteIPv6 is the default route “::/0” for IPv6
    31  var DefaultRouteIPv6 = netip.MustParsePrefix("::/0")
    32  
    33  // VPNRouteIPv6 is overriding VPN route “::/3” for IPv6
    34  var VPNRouteIPv6 = netip.MustParsePrefix("::/3")
    35  
    36  // IsNetwork determines if IP is the network address (all zeros) for this Mask
    37  // for 1.2.3.4/24 the network address 1.2.3.0 returns true
    38  func IsNetwork(IP net.IP, IPMask net.IPMask) (isNetwork bool) {
    39  	if len(IP) != net.IPv4len && len(IP) != net.IPv6len {
    40  		return
    41  	}
    42  	isNetwork = IsZeros(IP.Mask(InvertMask(IPMask)))
    43  	return
    44  }
    45  
    46  // IsZeros determines if every byte of the IP address is zero
    47  func IsZeros(p net.IP) bool {
    48  	for i := 0; i < len(p); i++ {
    49  		if p[i] != 0 {
    50  			return false
    51  		}
    52  	}
    53  	return true
    54  }
    55  
    56  // IsDirect determines if the route is direct
    57  //   - a direct route has mask 32 or 128 bit length /32 /128
    58  func IsDirect(route netip.Prefix) bool {
    59  	return route.Addr().Is4() && route.Bits() == 32 ||
    60  		route.Addr().Is6() && route.Bits() == 128
    61  }
    62  
    63  // IsIPv4 determines if net.IP value is IPv4
    64  func IsIPv4(ip net.IP) (isIPv4 bool) {
    65  	isIPv4 = len(ip.To4()) == net.IPv4len
    66  	return
    67  }
    68  
    69  // IsIPv6 determines if net.IP value is IPv6
    70  func IsIPv6(ip net.IP) (isIPv6 bool) {
    71  	isIPv6 = len(ip.To4()) != net.IPv4len && len(ip) == net.IPv6len
    72  	return
    73  }
    74  
    75  // IsNzIP is ip set and not zero
    76  func IsNzIP(ip net.IP) bool {
    77  	return ip != nil && !ip.IsUnspecified()
    78  }
    79  
    80  // IsBroadcast determines whether addr is the last address for Mask
    81  //   - the last address is typically broadcast
    82  //   - for 1.2.3.4/24 the network address 1.2.3.255 returns true
    83  func IsBroadcast(addr netip.Addr, IPMask net.IPMask) (isBroadcast bool) {
    84  	if !addr.IsValid() {
    85  		return
    86  	}
    87  
    88  	// convert to net,.IP to use Mask
    89  	var netIP = net.IP(addr.AsSlice())
    90  
    91  	invertedMask := InvertMask(IPMask)
    92  	isBroadcast = netIP.Mask(invertedMask).Equal(net.IP(invertedMask))
    93  	return
    94  }
    95  
    96  // InvertMask inverts the bits of a mask
    97  // the mask for 1.2.3.4/24 is normally ffffff00 or []byte{255, 255, 255, 0}
    98  func InvertMask(IPMask net.IPMask) (out net.IPMask) {
    99  	out = make(net.IPMask, len(IPMask))
   100  	for i, b := range IPMask {
   101  		out[i] = ^b
   102  	}
   103  	return
   104  }
   105  
   106  // IPNetString is abbreviated form 0/0
   107  func IPNetString(ipNet net.IPNet) (s string) {
   108  	ones, _ := ipNet.Mask.Size() // the /24 or /32 of CIDR
   109  	s = shorten(ipNet.IP) + "/" + strconv.Itoa(ones)
   110  	return
   111  }
   112  
   113  func shorten(IP net.IP) (s string) {
   114  	s = IP.String()
   115  	if len(IP) != net.IPv4len {
   116  		return
   117  	}
   118  	for strings.HasSuffix(s, zeroSuffix) {
   119  		s = s[:len(s)-len(zeroSuffix)]
   120  	}
   121  	return
   122  }
   123  
   124  // IsErrClosed returns true if err is when waiting for accept and the socket is closed
   125  //   - can be used with net.Conn.Accept
   126  func IsErrClosed(err error) (isErrNetClosing bool) {
   127  	// if err is nil, ok is false
   128  	if netOpError, ok := err.(*net.OpError); ok { // error occured during the operation
   129  		isErrNetClosing = netOpError.Err == net.ErrClosed // and it is that the listener was closed
   130  	}
   131  	return
   132  }
   133  
   134  func NetworkPrefixBitCount(byts []byte) (bits int) {
   135  
   136  	// count bits that are 1 from the high order bit until a zero bit is found
   137  	for _, byt := range byts {
   138  		if byt == 255 {
   139  			bits += 8
   140  			continue
   141  		}
   142  		for byt != 0 {
   143  			if byt&128 != 0 {
   144  				bits++
   145  			}
   146  			byt <<= 1
   147  		}
   148  		break
   149  	}
   150  	return
   151  }