Documentation
¶
Overview ¶
Package netx provides a production-ready IPv4 and IPv6 network subnetting toolkit built exclusively on the Go standard library.
netx is designed for backend infrastructure services, DevOps tooling, Kubernetes networking automation, and cloud infrastructure management where programmatic subnet calculation, allocation, and validation are required.
Data Structures ¶
The central type is Subnet, which captures all addressing attributes of a network block:
- NetworkAddress — the lowest address (all host bits zero)
- BroadcastAddress — the highest address (all host bits one)
- FirstHost — first usable host address
- LastHost — last usable host address
- TotalHosts — number of usable addresses (*big.Int, handles IPv6)
- Prefix — subnet prefix length (e.g. 24 for a /24)
All fields are unexported; read them using accessor methods.
CIDR Parsing ¶
sub, err := netx.ParseCIDR("192.168.1.0/24")
// sub.NetworkAddress() → 192.168.1.0
// sub.BroadcastAddress() → 192.168.1.255
// sub.FirstHost() → 192.168.1.1
// sub.LastHost() → 192.168.1.254
// sub.TotalHosts() → 254
// sub.Prefix() → 24
Fixed-Length Subnet Masking (FLSM) ¶
Split divides a network into equal-sized subnets:
base := netx.MustParseCIDR("10.0.0.0/24").IPNet()
subnets, err := netx.Split(base, 26)
// → [10.0.0.0/26, 10.0.0.64/26, 10.0.0.128/26, 10.0.0.192/26]
// Or split into exactly N equal parts (N must be a power of 2):
subnets, err = netx.SplitIntoN(base, 4)
Variable-Length Subnet Masking (VLSM) ¶
DivideByHosts allocates subnets sized to individual host requirements:
base := netx.MustParseCIDR("10.0.0.0/24").IPNet()
subnets, err := netx.DivideByHosts(base, []int{100, 50, 10})
// subnets[0]: 10.0.0.0/25 (126 hosts — satisfies 100)
// subnets[1]: 10.0.0.128/26 ( 62 hosts — satisfies 50)
// subnets[2]: 10.0.0.192/28 ( 14 hosts — satisfies 10)
Requirements are automatically sorted largest-first to minimise waste.
Utility Functions ¶
netx.Contains(network, ip) // true when ip is within network netx.Overlaps(netA, netB) // true when two networks share addresses netx.NetworkSize(ipnet) // total addresses (*big.Int, incl. network+broadcast) netx.HostCount(prefix, bits) // usable hosts for a given prefix netx.PrefixForHosts(n, bits) // smallest prefix providing ≥ n usable hosts netx.NextSubnet(ipnet, pfx) // next contiguous subnet of given prefix
Edge Case Handling ¶
The package correctly handles:
- /31 (RFC 3021 point-to-point): TotalHosts = 2, FirstHost = NetworkAddress
- /32 single-host: TotalHosts = 1, FirstHost = LastHost = NetworkAddress
- IPv6 subnets of any prefix length
- VLSM allocation failure when address space is exhausted
Cross-Platform Compatibility ¶
netx relies only on the Go standard library (net, math/big, sort) and produces identical results on Linux, macOS, and Windows.
Index ¶
- func AllocatedSubnetsToStrings(subnets []Subnet) []string
- func Contains(network *net.IPNet, ip net.IP) bool
- func HostCount(prefix, bits int) *big.Int
- func NetworkSize(ipnet *net.IPNet) *big.Int
- func NextSubnet(ipnet *net.IPNet, newPrefix int) (*net.IPNet, error)
- func Overlaps(netA, netB *net.IPNet) bool
- func PrefixForHosts(hosts, bits int) int
- func Split(network *net.IPNet, newPrefix int) ([]*net.IPNet, error)
- func SplitIntoN(network *net.IPNet, n int) ([]*net.IPNet, error)
- func SubnetsToStrings(nets []*net.IPNet) []string
- type Subnet
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AllocatedSubnetsToStrings ¶
AllocatedSubnetsToStrings converts a slice of Subnet to their CIDR string representations.
Parameters:
- `subnets`: the slice of Subnet values to convert.
Returns:
A slice of CIDR strings in the same order as the input.
Example:
base := netx.MustParseCIDR("10.0.0.0/24").IPNet()
subs, _ := netx.DivideByHosts(base, []int{100, 50, 10})
fmt.Println(netx.AllocatedSubnetsToStrings(subs))
func Contains ¶
Contains reports whether the given IP address belongs to the network.
This is a thin, descriptive wrapper around (*net.IPNet).Contains.
Parameters:
- `network`: the network to test against.
- `ip`: the IP address to look up.
Returns:
A boolean value: - true when ip falls within the network's address range; - false otherwise or when either argument is nil.
Example:
_, n, _ := net.ParseCIDR("10.0.0.0/8")
netx.Contains(n, net.ParseIP("10.1.2.3")) // true
netx.Contains(n, net.ParseIP("192.168.1.1")) // false
func HostCount ¶
HostCount returns the number of usable host addresses for a subnet with the given prefix length in a standard IPv4 (/8–/32) or IPv6 (/0–/128) network.
Special cases:
- prefix == bits (e.g. /32 for IPv4): returns 1.
- prefix == bits-1 (e.g. /31 for IPv4): returns 2.
- all other prefixes: returns 2^(bits-prefix) - 2.
bits must be either 32 (IPv4) or 128 (IPv6). If bits is neither, the function treats the prefix as IPv4.
Parameters:
- `prefix`: the subnet prefix length.
- `bits`: the total number of bits in the address family (32 or 128).
Returns:
A *big.Int representing the usable host count.
Example:
netx.HostCount(24, 32) // 254 netx.HostCount(31, 32) // 2 netx.HostCount(32, 32) // 1
func NetworkSize ¶
NetworkSize returns the total number of addresses in the network block (including network and broadcast addresses). For a /24 this is 256.
A *big.Int is used so that IPv6 networks, which can contain up to 2^128 addresses, are handled correctly.
Parameters:
- `ipnet`: the network whose size to compute.
Returns:
A *big.Int representing the total address count; 0 when ipnet is nil.
Example:
_, n, _ := net.ParseCIDR("192.168.1.0/24")
netx.NetworkSize(n) // 256
func NextSubnet ¶
NextSubnet returns the next contiguous subnet of the given prefix size that immediately follows ipnet.
For example, the next /26 after 10.0.0.0/26 is 10.0.0.64/26. The function does not check whether the returned subnet is within any enclosing block.
Parameters:
- `ipnet`: the current subnet.
- `newPrefix`: the prefix length for the next subnet.
Returns:
(*net.IPNet, error): the next subnet, or nil and a non-nil error when ipnet is nil or newPrefix is invalid.
Example:
base := netx.MustParseCIDR("10.0.0.0/26").IPNet()
next, err := netx.NextSubnet(base, 26)
fmt.Println(next) // "10.0.0.64/26"
func Overlaps ¶
Overlaps reports whether two network blocks share any address in common.
Two networks overlap when one contains the network address of the other, or when one is entirely contained within the other.
Parameters:
- `netA`: the first network.
- `netB`: the second network.
Returns:
A boolean value: - true when the two networks share at least one address; - false when they are disjoint or either argument is nil.
Example:
_, a, _ := net.ParseCIDR("10.0.0.0/24")
_, b, _ := net.ParseCIDR("10.0.0.128/25")
netx.Overlaps(a, b) // true
_, c, _ := net.ParseCIDR("192.168.0.0/24")
netx.Overlaps(a, c) // false
func PrefixForHosts ¶
PrefixForHosts returns the smallest prefix length that provides at least the requested number of usable host addresses.
The function searches from the most specific prefix toward the least specific, returning the first prefix for which HostCount(prefix, bits) ≥ hosts.
Parameters:
- `hosts`: the minimum number of usable host addresses required (≥ 1).
- `bits`: the address family bit width (32 for IPv4, 128 for IPv6).
Returns:
An int containing the prefix length, or -1 when no valid prefix can satisfy the requirement (e.g. more than 2^30 hosts requested for IPv4).
Example:
netx.PrefixForHosts(100, 32) // 25 (provides 126 usable hosts) netx.PrefixForHosts(254, 32) // 24 (provides 254 usable hosts) netx.PrefixForHosts(255, 32) // 23 (provides 510 usable hosts)
func Split ¶
Split divides a network block into equal-sized subnets, each with the given prefix length.
The function performs Fixed-Length Subnet Masking (FLSM): all resulting subnets are the same size, and together they exactly cover the original network without gaps or overlaps.
Parameters:
- `network`: the base network to split.
- `newPrefix`: the prefix length for each resulting subnet; must be strictly greater than network's prefix.
Returns:
([]*net.IPNet, error): an ordered slice of subnets covering the original network, or nil and a non-nil error when the split is invalid.
Errors:
- When newPrefix is not larger than the base prefix.
- When newPrefix exceeds the maximum for the address family (32 for IPv4, 128 for IPv6).
Example:
base := netx.MustParseCIDR("10.0.0.0/24").IPNet()
subnets, err := netx.Split(base, 26)
// subnets: [10.0.0.0/26, 10.0.0.64/26, 10.0.0.128/26, 10.0.0.192/26]
func SplitIntoN ¶
SplitIntoN divides a network block into exactly n equal-sized subnets.
SplitIntoN is a convenience wrapper around Split that automatically calculates the required prefix length. n must be a power of two.
Parameters:
- `network`: the base network to split.
- `n`: the number of subnets to produce; must be a power of 2 and at least 2.
Returns:
([]*net.IPNet, error): n equal-sized subnets, or nil and an error.
Example:
base := netx.MustParseCIDR("10.0.0.0/24").IPNet()
subnets, err := netx.SplitIntoN(base, 4)
// subnets: [10.0.0.0/26, 10.0.0.64/26, 10.0.0.128/26, 10.0.0.192/26]
func SubnetsToStrings ¶
SubnetsToStrings converts a slice of *net.IPNet to their CIDR string representations.
Parameters:
- `nets`: the slice of networks to convert.
Returns:
A slice of CIDR strings in the same order as the input.
Example:
base := netx.MustParseCIDR("10.0.0.0/24").IPNet()
subs, _ := netx.Split(base, 26)
fmt.Println(netx.SubnetsToStrings(subs))
// ["10.0.0.0/26" "10.0.0.64/26" "10.0.0.128/26" "10.0.0.192/26"]
Types ¶
type Subnet ¶
type Subnet struct {
// contains filtered or unexported fields
}
Subnet represents a fully computed IP network block, including all derived addressing attributes. It is the central data structure of the netx package.
A Subnet is created by ParseCIDR or returned from FLSM/VLSM allocation functions. All fields are computed automatically; use the accessor methods to read them.
Subnet is safe for concurrent reads after creation. It must not be modified after construction.
func DivideByHosts ¶
DivideByHosts allocates variable-length subnets from a base network to satisfy a list of host-count requirements, using Variable Length Subnet Masking (VLSM).
The algorithm:
- Sorts host requirements in descending order so that the largest subnets are allocated first (minimising wasted address space).
- For each requirement, determines the smallest prefix that provides at least the requested number of usable hosts.
- Allocates subnets sequentially from the base network address with no gaps.
- Returns an error if the base network does not have enough address space to satisfy all requirements.
Parameters:
- `base`: the network block from which subnets are allocated.
- `hostRequirements`: the number of usable hosts required for each subnet. Values must be ≥ 1.
Returns:
([]Subnet, error): allocated subnets in the order corresponding to the sorted requirements, or nil and a non-nil error when allocation fails.
Example:
base := netx.MustParseCIDR("10.0.0.0/24").IPNet()
subnets, err := netx.DivideByHosts(base, []int{100, 50, 10})
// subnets[0]: 10.0.0.0/25 (126 usable hosts)
// subnets[1]: 10.0.0.128/26 ( 62 usable hosts)
// subnets[2]: 10.0.0.192/28 ( 14 usable hosts)
func MustParseCIDR ¶
MustParseCIDR is like ParseCIDR but panics when the CIDR string is invalid.
It is intended for use in tests and program initialisation where an invalid CIDR is a programming error rather than a runtime condition.
Parameters:
- `cidr`: a CIDR notation string (e.g. "10.0.0.0/8").
Returns:
A fully computed Subnet.
Example:
sub := netx.MustParseCIDR("10.0.0.0/8")
fmt.Println(sub.NetworkAddress()) // 10.0.0.0
func ParseCIDR ¶
ParseCIDR parses a CIDR notation string and returns a fully populated Subnet with all addressing attributes calculated.
The CIDR string must be in standard notation, for example "192.168.1.0/24" or "2001:db8::/32". Both IPv4 and IPv6 are supported.
Unlike net.ParseCIDR, which silently masks the host bits, ParseCIDR always uses the network address derived from the mask — making it safe to pass host addresses such as "192.168.1.5/24" and receive the correct network.
Parameters:
- `cidr`: a CIDR notation string (e.g. "10.0.0.0/8").
Returns:
(Subnet, error): a fully computed Subnet on success, or a zero Subnet and a non-nil error when the input is malformed.
Example:
sub, err := netx.ParseCIDR("192.168.1.0/24")
if err != nil {
log.Fatal(err)
}
fmt.Println(sub.NetworkAddress()) // 192.168.1.0
fmt.Println(sub.BroadcastAddress()) // 192.168.1.255
fmt.Println(sub.FirstHost()) // 192.168.1.1
fmt.Println(sub.LastHost()) // 192.168.1.254
fmt.Println(sub.TotalHosts()) // 254
func (Subnet) BroadcastAddress ¶
BroadcastAddress returns the broadcast address of the subnet.
For IPv6 subnets and /31 or /32 blocks the value is still the bitwise all-ones host address, even though broadcast semantics differ.
Returns:
A net.IP containing the highest address of the block.
Example:
sub, _ := netx.ParseCIDR("10.0.0.0/24")
fmt.Println(sub.BroadcastAddress()) // "10.0.0.255"
func (Subnet) FirstHost ¶
FirstHost returns the first usable host address.
For /31 (RFC 3021) and /32 blocks the concept of "first host" maps to networkAddress and the single host address respectively.
Returns:
A net.IP containing the first usable address.
Example:
sub, _ := netx.ParseCIDR("10.0.0.0/24")
fmt.Println(sub.FirstHost()) // "10.0.0.1"
func (Subnet) IPNet ¶
IPNet returns the underlying *net.IPNet for this subnet.
Returns:
A pointer to the net.IPNet representing this network block.
func (Subnet) LastHost ¶
LastHost returns the last usable host address.
For /31 and /32 blocks see the note on FirstHost.
Returns:
A net.IP containing the last usable address.
Example:
sub, _ := netx.ParseCIDR("10.0.0.0/24")
fmt.Println(sub.LastHost()) // "10.0.0.254"
func (Subnet) NetworkAddress ¶
NetworkAddress returns the network (base) address of the subnet.
Returns:
A net.IP containing the lowest address of the block.
Example:
sub, _ := netx.ParseCIDR("10.0.0.0/24")
fmt.Println(sub.NetworkAddress()) // "10.0.0.0"
func (Subnet) Prefix ¶
Prefix returns the prefix length of the subnet (e.g. 24 for a /24).
Returns:
An int containing the prefix length.
Example:
sub, _ := netx.ParseCIDR("192.168.1.0/24")
fmt.Println(sub.Prefix()) // 24
func (Subnet) String ¶
String returns the CIDR notation of the subnet (e.g. "10.0.0.0/24").
Returns:
A string in CIDR notation.
func (Subnet) TotalHosts ¶
TotalHosts returns the number of usable host addresses in the subnet.
A *big.Int is returned to handle IPv6 subnets whose host counts exceed int64 range. For typical IPv4 subnets the value fits in int64.
Special cases:
- /31 returns 2 (RFC 3021 point-to-point link)
- /32 returns 1
Returns:
A *big.Int representing the usable host count.
Example:
sub, _ := netx.ParseCIDR("10.0.0.0/24")
fmt.Println(sub.TotalHosts()) // 254