go_goutils/netx/funcs.go
brent saner e101758187
v1.10.3
ADDED:
* netx now has a ton of netmask conversion functions for IPv4 netmasks.
  (IPv6 doesn't really *have* netmasks, so it was intentionally
  excluded).
2025-10-13 15:56:07 -04:00

411 lines
7.2 KiB
Go

package netx
import (
`math/bits`
`net`
`net/netip`
`strconv`
`strings`
`go4.org/netipx`
)
/*
AddrRfc returns an RFC-friendly string from an IP address ([net/netip.Addr]).
If addr is an IPv4 address, it will simmply be the string representation (e.g. "203.0.113.1").
If addr is an IPv6 address, it will be enclosed in brackets (e.g. "[2001:db8::1]").
If the version can't be determined, rfcStr will be an empty string.
*/
func AddrRfc(addr netip.Addr) (rfcStr string) {
if addr.Is4() {
rfcStr = addr.String()
} else if addr.Is6() {
rfcStr = "[" + addr.String() + "]"
}
return
}
/*
Cidr4ToIPMask takes an IPv4 CIDR/bit size/prefix length and returns the [net.IPMask].
It's (essentially) the inverse of [net.IPMask.Size].
See also:
* [Cidr4ToMask]
* [Cidr4ToStr]
Inverse of [IPMask4ToCidr].
*/
func Cidr4ToIPMask(cidr uint8) (ipMask net.IPMask, err error) {
if cidr > 32 {
err = ErrBadNetFam
return
}
ipMask = net.CIDRMask(int(cidr), 32)
return
}
/*
Cidr4ToMask takes an IPv4 CIDR/bit size/prefix length and returns the netmask *in bitmask form*.
See also:
* [Cidr4ToIPMask]
* [Cidr4ToStr]
Inverse of [Mask4ToCidr].
*/
func Cidr4ToMask(cidr uint8) (mask uint32, err error) {
if cidr > 32 {
err = ErrBadNetFam
return
}
// COULD do (1 << 32) - (1 << (32 - ip.Bits())) instead but in EXTREME edge cases that could cause an overflow.
// We're basically converting the CIDR size ("number of bits"/"number of ones") to an integer mask ("number AS bits")
mask = uint32(0xffffffff) << uint32(32-cidr)
return
}
/*
Cidr4ToStr is a convenience wrapper around [IPMask4ToStr]([Cidr4ToMask](cidr)).
See also:
* [Cidr4ToIPMask]
* [Cidr4ToMask]
Inverse of [Mask4StrToCidr].
*/
func Cidr4ToStr(cidr uint8) (maskStr string, err error) {
var ipMask net.IPMask
if ipMask, err = Cidr4ToIPMask(cidr); err != nil {
return
}
if maskStr, err = IPMask4ToStr(ipMask); err != nil {
return
}
return
}
/*
GetAddrFamily returns the network family of a [net/netip.Addr].
See also [GetIpFamily].
If addr is not a "valid" IP address or the version can't be determined, family will be AFUnspec (usually 0x00/0).
*/
func GetAddrFamily(addr netip.Addr) (family uint16) {
family = AFUnspec
if !addr.IsValid() {
return
}
if addr.Is4() {
family = AFInet
} else if addr.Is6() {
family = AFInet6
} else {
return
}
return
}
/*
GetIpFamily returns the network family of a [net.IP].
See also [GetAddrFamily].
If ip is not a "valid" IP address or the version can't be determined,
family will be [golang.org/x/sys/unix.AF_UNSPEC] or [golang.org/x/sys/windows.AF_UNSPEC] depending on platform (usually 0x00/0).
*/
func GetIpFamily(ip net.IP) (family uint16) {
var ok bool
var addr netip.Addr
if addr, ok = netipx.FromStdIP(ip); !ok {
return
}
family = GetAddrFamily(addr)
return
}
/*
IpRfc returns an RFC-friendly string from an IP address ([net.IP]).
If ip is an IPv4 address, it will simmply be the string representation (e.g. "203.0.113.1").
If ip is an IPv6 address, it will be enclosed in brackets (e.g. "[2001:db8::1]").
If the version can't be determined, rfcStr will be an empty string.
*/
func IpRfc(ip net.IP) (rfcStr string) {
if ip.To4() != nil {
rfcStr = ip.To4().String()
} else if ip.To16() != nil {
rfcStr = "[" + ip.To16().String() + "]"
}
return
}
/*
IPMask4ToCidr returns a CIDR prefix size/bit size/bit length from a [net.IPMask].
See also:
* [IPMask4ToMask]
* [IPMask4ToStr]
Inverse of [Cidr4ToIPMask].
*/
func IPMask4ToCidr(ipMask net.IPMask) (cidr uint8, err error) {
var ones int
var total int
ones, total = ipMask.Size()
if total != 32 {
err = ErrBadNetFam
return
}
if ones > 32 {
err = ErrBadNetFam
return
}
cidr = uint8(ones)
return
}
/*
IPMask4ToMask returns the mask *in bitmask form* from a [net.IPMask].
See also:
* [IPMask4ToCidr]
* [IPMask4ToStr]
Inverse of [Mask4ToIPMask].
*/
func IPMask4ToMask(ipMask net.IPMask) (mask uint32, err error) {
var cidr uint8
if cidr, err = IPMask4ToCidr(ipMask); err != nil {
return
}
if mask, err = Cidr4ToMask(cidr); err != nil {
return
}
return
}
/*
IPMask4ToStr returns a string representation of an IPv4 netmask (e.g. "255.255.255.0" for a /24) from a [net.IPMask].
See also:
* [IPMask4ToCidr]
* [IPMask4ToMask]
Inverse of [Mask4StrToIPMask].
*/
func IPMask4ToStr(ipMask net.IPMask) (maskStr string, err error) {
var idx int
var b []byte
var quads []string = make([]string, 4)
b = []byte(ipMask)
if len(b) != 4 {
err = ErrBadNetFam
return
}
for idx = 0; idx < len(b); idx++ {
quads[idx] = strconv.Itoa(int(b[idx]))
}
maskStr = strings.Join(quads, ".")
return
}
/*
Mask4ToCidr converts an IPv4 netmask *in bitmask form* to a CIDR prefix size/bit size/bit length.
See also:
* [Mask4ToIPMask]
* [Mask4ToStr]
Inverse of [Cidr4ToMask].
*/
func Mask4ToCidr(mask uint32) (cidr uint8, err error) {
cidr = 32 - uint8(bits.LeadingZeros32(mask))
return
}
/*
Mask4ToIPMask returns mask *in bitmask form* as a [net.IPMask].
See also:
* [Mask4ToCidr]
* [Mask4ToStr]
Inverse of [IPMask4ToMask].
*/
func Mask4ToIPMask(mask uint32) (ipMask net.IPMask, err error) {
var cidr uint8
if cidr, err = Mask4ToCidr(mask); err != nil {
return
}
ipMask = net.CIDRMask(int(cidr), 32)
return
}
/*
Mask4ToStr returns a string representation of an IPv4 netmask (e.g. "255.255.255.0" for a /24) from a netmask *in bitmask form*.
See also:
* [Mask4ToCidr]
* [Mask4ToIPMask]
Inverse of [Mask4StrToMask].
*/
func Mask4ToStr(mask uint32) (maskStr string, err error) {
var ipMask net.IPMask
if ipMask, err = Mask4ToIPMask(mask); err != nil {
return
}
if maskStr, err = IPMask4ToStr(ipMask); err != nil {
return
}
return
}
/*
Mask4StrToCidr parses a "dotted-quad" IPv4 netmask (e.g. "255.255.255.0" for a /24) and returns am IPv4 CIDR/bit size/prefix length.
See also:
* [Mask4StrToIPMask]
* [Mask4StrToMask]
Inverse of [Cidr4ToMaskStr].
*/
func Mask4StrToCidr(maskStr string) (cidr uint8, err error) {
var ipMask net.IPMask
if ipMask, err = Mask4StrToIPMask(maskStr); err != nil {
return
}
if cidr, err = IPMask4ToCidr(ipMask); err != nil {
return
}
return
}
/*
Mask4StrToIPMask parses a "dotted-quad" IPv4 netmask (e.g. "255.255.255.0" for a /24) and returns a [net.IPMask].
See also:
* [Mask4StrToCidr]
* [Mask4StrToMask]
Inverse of [IPMask4ToStr].
*/
func Mask4StrToIPMask(maskStr string) (mask net.IPMask, err error) {
var idx int
var s string
var u64 uint64
var b []byte = make([]byte, 4)
var sl []string = strings.Split(maskStr, ".")
if len(sl) != 4 {
err = ErrBadMask4Str
return
}
// A net.IPMask is just a []byte.
for idx = 0; idx < len(sl); idx++ {
s = sl[idx]
if u64, err = strconv.ParseUint(s, 10, 8); err != nil {
return
}
b[idx] = byte(u64)
}
mask = net.IPMask(b)
return
}
/*
Mask4StrToMask parses a "dotted-quad" IPv4 netmask (e.g. "255.255.255.0" for a /24) and returns a netmask *in bitmask form*.
See also:
* [Mask4StrToCidr]
* [Mask4StrToIPMask]
Inverse of [Mask4ToStr].
*/
func Mask4StrToMask(maskStr string) (mask uint32, err error) {
var ipMask net.IPMask
if ipMask, err = Mask4StrToIPMask(maskStr); err != nil {
return
}
if mask, err = IPMask4ToMask(ipMask); err != nil {
return
}
return
}