Documentation
¶
Overview ¶
Discrete sets and ranges of IP addresses.
Set Types ¶
Interface hierarchy: Block set is Interval set is Discrete set.
NewBlock creates a RFC-4632 CIDR Block set. NewSingle creates a Block set from a single address.
NewInterval creates contiguous Interval set of addresses from lower and upper bounds.
NewDiscrete creates Discrete set as union of other address sets. Use NewDiscrete to create the empty set.
Functions return a struct that conforms to the most specialized interface possible.
Iteration ¶
Use Discrete.Addresses to iterate over constituent addresses.
Use Discrete.Intervals to iterate over constituent Interval sets.
Use the Blocks function to iterate over constituent Block sets within Interval sets.
References ¶
Index ¶
- func Adjacent[A ip.Int[A]](i0, i1 Interval[A]) bool
- func Blocks[A ip.Int[A]](set Interval[A]) iter.Seq[Block[A]]
- func Contiguous[A ip.Int[A]](i0, i1 Interval[A]) bool
- func Eq[A ip.Int[A]](set0, set1 Discrete[A]) (equal bool)
- func Intersect[A ip.Int[A]](i0, i1 Interval[A]) bool
- func Subnets[A ip.Int[A]](b Block[A], maskBits int) iter.Seq[Block[A]]
- type Block
- type Builder
- type Discrete
- type Interval
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Adjacent ¶
Tests if IP address ranges are one element from overlap.
Does not overflow - the maximum value is not considered adjacent to the minimum.
func Blocks ¶
Subdivides Interval into CIDR Block sets
Example ¶
package main
import (
"github.com/ipfreely-uk/go/ip"
"github.com/ipfreely-uk/go/ipset"
)
func main() {
first := ip.V4().MustFromBytes(192, 0, 2, 101)
last := ip.V4().MustFromBytes(192, 0, 2, 240)
freeAddresses := ipset.NewInterval(first, last)
printCidrBlocksIn(freeAddresses)
}
func printCidrBlocksIn[A ip.Int[A]](addressRange ipset.Interval[A]) {
for block := range ipset.Blocks(addressRange) {
println(block.String())
}
}
func Contiguous ¶
func Eq ¶
Tests if two discrete sets are equal. Iterates each Discrete set's Interval sets comparing first and last elements.
func Subnets ¶
Splits Block into subnets of a given size.
Panics on illegal mask bits. maskBits must be greater or equal to Block.MaskSize and less than or equal to the bit width of the address family.
Example ¶
package main
import (
"fmt"
"math/big"
"github.com/ipfreely-uk/go/ip"
"github.com/ipfreely-uk/go/ipmask"
"github.com/ipfreely-uk/go/ipset"
"github.com/ipfreely-uk/go/txt"
)
func maskRequiredFor[A ip.Int[A]](f ip.Family[A], allocateableAddresses *big.Int) (bits int) {
var min *big.Int
if f.Version() == ip.V4().Version() {
two := big.NewInt(2)
min = two.Add(two, allocateableAddresses)
} else {
min = allocateableAddresses
}
width := f.Width()
for m := width; m >= 0; m-- {
sizeForMask := ipmask.Size(f, m)
if sizeForMask.Cmp(min) >= 0 {
return m
}
}
formatted := txt.CommaDelim(allocateableAddresses)
msg := fmt.Sprintf("%s is larger than family %s", formatted, f.String())
panic(msg)
}
func main() {
oneHundredAddresses := big.NewInt(100)
mask := maskRequiredFor(ip.V4(), oneHundredAddresses)
netAddr, bits, _ := ipmask.ParseCIDRNotation(ip.V4(), "203.0.113.0/24")
block := ipset.NewBlock(netAddr, bits)
println(fmt.Sprintf("Dividing %s into blocks of at least %s addresses", block.CidrNotation(), oneHundredAddresses.String()))
for sub := range ipset.Subnets(block, mask) {
println(sub.String())
}
}
Types ¶
type Block ¶
type Block[A ip.Int[A]] interface { Interval[A] // Mask size in bits MaskSize() (bits int) // Mask as IP address Mask() (address A) // The block in CIDR notation. CidrNotation() string }
Immutable RFC-4632 CIDR block. Roughly equivalent to the [netip.Prefix] type.
Example ¶
package main
import (
"crypto/rand"
"github.com/ipfreely-uk/go/ip"
"github.com/ipfreely-uk/go/ipset"
)
func main() {
netAddress := ip.MustParse(ip.V6(), "2001:db8:cafe::")
block := ipset.NewBlock(netAddress, 56)
for i := 0; i < 3; i++ {
randomAddr := randomAddressFrom(block)
println("Random address from", block.String(), "=", randomAddr.String())
}
}
// Pick random address from block
func randomAddressFrom[A ip.Int[A]](netBlock ipset.Block[A]) (address A) {
netAddr := netBlock.First()
family := netAddr.Family()
inverseMask := netBlock.Mask().Not()
return random(family).And(inverseMask).Or(netAddr)
}
// Generate a random address for given family
func random[A ip.Int[A]](f ip.Family[A]) (address A) {
slice := make([]byte, f.Width()/8)
_, _ = rand.Read(slice)
return f.MustFromBytes(slice...)
}
func NewBlock ¶
Creates Block set.
Panics if mask does not cover network address or is out of range for address family.
Example ¶
package main
import (
"github.com/ipfreely-uk/go/ip"
"github.com/ipfreely-uk/go/ipset"
"github.com/ipfreely-uk/go/txt"
)
func main() {
network := ip.MustParse(ip.V6(), "2001:db8::")
block := ipset.NewBlock(network, 32)
println("Block", block.String())
println("First", block.First().String())
println("Last", block.Last().String())
println("Size", txt.CommaDelim(block.Size()))
}
Example (Second) ¶
package main
import (
"github.com/ipfreely-uk/go/ip"
"github.com/ipfreely-uk/go/ipmask"
"github.com/ipfreely-uk/go/ipset"
)
func main() {
network := ip.MustParse(ip.V4(), "192.168.0.0")
mask := ip.MustParse(ip.V4(), "255.255.255.0")
subnet := block(network, mask)
println(subnet.String())
}
func block[A ip.Int[A]](network, mask A) ipset.Block[A] {
bits := ipmask.Bits(mask)
return ipset.NewBlock(network, bits)
}
type Builder ¶
Discrete set builder.
The only advantage over using a slice and NewDiscrete is that this type will attempt to reduce the buffer over a certain threshold.
Example ¶
package main
import (
"github.com/ipfreely-uk/go/ip"
"github.com/ipfreely-uk/go/ipmask"
"github.com/ipfreely-uk/go/ipset"
)
func main() {
addr, bits, err := ipmask.ParseCIDRNotation(ip.V4(), "10.0.0.0/15")
if err != nil {
panic(err.Error())
}
network := ipset.NewBlock(addr, bits)
lucky := removeUnlucky(network)
println(lucky.String())
}
var thirteen = ip.V4().FromInt(13)
var maskComplement = ipmask.For(ip.V4(), 24).Not()
func removeUnlucky(addresses ipset.Discrete[ip.Addr4]) (lucky ipset.Discrete[ip.Addr4]) {
bldr := ipset.Builder[ip.Addr4]{}
for a := range addresses.Addresses() {
if !unlucky(a) {
bldr.Union(ipset.NewSingle(a))
}
}
return bldr.Build()
}
func unlucky(a ip.Addr4) bool {
lastDigits := a.And(maskComplement)
return ip.Eq(lastDigits, thirteen)
}
type Discrete ¶
type Discrete[A ip.Int[A]] interface { // Tests if address in set Contains(address A) bool // Number of unique addresses. Size() (cardinality *big.Int) // Tests for empty set Empty() bool // Unique addresses from least to greatest Addresses() iter.Seq[A] // Contents as distinct [Interval] sets. // Intervals do not [Intersect] and are not [Adjacent]. // Intervals are returned from least address to greatest. Intervals() iter.Seq[Interval[A]] // Informational only String() string }
Immutable discrete ordered set of IP addresses. Seq types provided by implementations are reusable.
func NewDiscrete ¶
Creates Discrete set as a union of addresses from the operand elements.
If set is contiguous range returns result of NewInterval function. If set is CIDR range returns result of NewBlock function. Zero-length slice returns the empty set.
Example ¶
package main
import (
"github.com/ipfreely-uk/go/ip"
"github.com/ipfreely-uk/go/ipset"
)
func main() {
set := func(first, last string) ipset.Interval[ip.Addr4] {
v4 := ip.V4()
p := ip.MustParse[ip.Addr4]
return ipset.NewInterval(p(v4, first), p(v4, last))
}
r0 := set("192.0.2.0", "192.0.2.100")
r1 := set("192.0.2.101", "192.0.2.111")
r2 := set("192.0.2.200", "192.0.2.200")
union := ipset.NewDiscrete(r0, r1, r2)
println(r0.String(), "\u222A", r1.String(), "\u222A", r2.String(), "=", union.String())
}
Example (Second) ¶
package main
import (
"github.com/ipfreely-uk/go/ip"
"github.com/ipfreely-uk/go/ipset"
)
func main() {
printEmptySetFor(ip.V4())
printEmptySetFor(ip.V6())
}
func printEmptySetFor[A ip.Int[A]](f ip.Family[A]) {
empty := ipset.NewDiscrete[A]()
println(f.String(), empty.String())
}
Example (Third) ¶
package main
import (
"fmt"
"github.com/ipfreely-uk/go/ip"
"github.com/ipfreely-uk/go/ipmask"
"github.com/ipfreely-uk/go/ipset"
)
func main() {
s0 := parseV4("192.0.2.0/32")
s1 := parseV4("192.0.2.11/32")
s2 := parseV4("192.0.2.12/32")
printSetType(s0)
printSetType(s1, s2)
printSetType(s0, s1, s2)
}
func parseV4(notation string) ipset.Block[ip.Addr4] {
a, m, err := ipmask.ParseCIDRNotation(ip.V4(), notation)
if err != nil {
panic(err)
}
return ipset.NewBlock(a, m)
}
func printSetType[A ip.Int[A]](sets ...ipset.Discrete[A]) {
union := ipset.NewDiscrete(sets...)
switch s := union.(type) {
case ipset.Block[A]:
println(fmt.Sprintf("%s is a block set", s.String()))
case ipset.Interval[A]:
println(fmt.Sprintf("%s is an interval set", s.String()))
case ipset.Discrete[A]:
println(fmt.Sprintf("%s is a discrete set", s.String()))
}
}
type Interval ¶
type Interval[A ip.Int[A]] interface { Discrete[A] // Least address First() (address A) // Greatest address Last() (address A) }
Immutable set of IP addresses between first and last inclusive.
A range of one or more IP addresses. The name interval was chosen because range is a keyword in Go. Interval is a term from mathematical set theory.
func Extremes ¶
Creates Interval using least and greatest values from each
Example ¶
package main
import (
"github.com/ipfreely-uk/go/ip"
"github.com/ipfreely-uk/go/ipset"
)
func main() {
r0 := parseV6Interval("2001:db8::", "2001:db8::100")
r1 := parseV6Interval("2001:db8::10", "2001:db8::ffff:ffff:ffff")
if ipset.Contiguous(r0, r1) {
r2 := ipset.Extremes(r0, r1)
println(r2.String())
}
}
func parseV6Interval(first, last string) ipset.Interval[ip.Addr6] {
v6 := ip.V6()
p := ip.MustParse[ip.Addr6]
return ipset.NewInterval(p(v6, first), p(v6, last))
}
func NewInterval ¶
Creates Interval set.
If range is valid CIDR block returns value from NewBlock instead.
Example ¶
package main
import (
"github.com/ipfreely-uk/go/ip"
"github.com/ipfreely-uk/go/ipset"
)
func main() {
// 1st address in IPv4 subnet is network address.
// Last address in IPv4 subnet is broadcast address.
// The rest can be used for unicast.
sub := ipset.NewBlock(ip.V4().MustFromBytes(203, 0, 113, 8), 29)
first := ip.Next(sub.First())
last := ip.Prev(sub.Last())
allocations := ipset.NewInterval(first, last)
println("Assignable addresses in ", sub.String())
for a := range allocations.Addresses() {
println(a.String())
}
}