network

package
v0.0.14-alpha Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 3, 2024 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Network ranges and IP address sets.

NewRange creates contiguous AddressRange set. NewBlock creates RFC-4632 https://www.rfc-editor.org/rfc/rfc4632 CIDR Block set. NewSet creates non-contigous AddressSet.

Use [AddressSet.Addresses] to iterate constituent addresses. Use [AddressSet.Ranges] to iterate constituent AddressRange types. Use Blocks to iterate constituent Block types.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Adjacent

func Adjacent[A ip.Number[A]](r0, r1 AddressRange[A]) bool

Tests if ranges are one element from overlap

func Contiguous

func Contiguous[A ip.Number[A]](r0, r1 AddressRange[A]) bool

Tests if ranges either Intersect or are Adjacent

Example
package main

import (
	"github.com/ipfreely-uk/go/ip"
	"github.com/ipfreely-uk/go/ip/network"
)

func main() {
	r0 := makeRange(ip.V6(), "2001:db8::", "2001:db8::100")
	r1 := makeRange(ip.V6(), "2001:db8::10", "2001:db8::ffff:ffff:ffff")

	if network.Contiguous(r0, r1) {
		r2 := network.Join(r0, r1)
		println(r2.String())
	}
}

func makeRange[A ip.Number[A]](family ip.Family[A], first, last string) network.AddressRange[A] {
	f := ip.MustParse(family, first)
	l := ip.MustParse(family, last)
	return network.NewRange(f, l)
}
Output:

func Intersect

func Intersect[A ip.Number[A]](r0, r1 AddressRange[A]) bool

Test if ranges have common elements

func ParseCIDRNotation

func ParseCIDRNotation[A ip.Number[A]](f ip.Family[A], notation string) (netAddress A, maskBits int, err error)

Parses CIDR notation. Returns error if second argument is invalid CIDR notation.

Example
address, mask, _ := network.ParseCIDRNotation(ip.V6(), "2001:db8::/32")
reservedForDocumentation := network.NewBlock(address, mask)
printRangeDetails(reservedForDocumentation)
Output:

func ParseUnknownCIDRNotation

func ParseUnknownCIDRNotation(notation string) (netAddress ip.Address, maskBits int, err error)

Parses CIDR notation where IP address family is unknown. Returns error if argument is invalid CIDR notation.

Example
reservedForDocumentation := []string{
	"192.0.2.0/24",
	"198.51.100.0/24",
	"203.0.113.0/24",
	"2001:db8::/32",
}
for _, notation := range reservedForDocumentation {
	address, mask, err := network.ParseUnknownCIDRNotation(notation)
	if err != nil {
		panic(err)
	}
	switch a := address.(type) {
	case ip.Addr4:
		printRangeDetails(network.NewBlock(a, mask))
	case ip.Addr6:
		printRangeDetails(network.NewBlock(a, mask))
	}
}
Output:

Types

type AddressRange

type AddressRange[A ip.Number[A]] interface {
	AddressSet[A]
	// Least address
	First() (address A)
	// Greatest address
	Last() (address A)
}

Immutable contiguous range of one or more IP addresses.

func Join

func Join[A ip.Number[A]](r0, r1 AddressRange[A]) AddressRange[A]

Joins ranges using least and greatest elements from both. Ranges do not have to be contiguous.

func NewRange

func NewRange[A ip.Number[A]](first, last A) AddressRange[A]

Creates new AddressRange. Return value conforms to Block if possible.

type AddressSet

type AddressSet[A ip.Number[A]] interface {
	// Tests if address in set
	Contains(address A) bool
	// Number of unique addresses
	Size() *big.Int
	// Unique addresses from least to greatest
	Addresses() Iterator[A]
	// Non-contiguous ranges from least to greatest
	Ranges() Iterator[AddressRange[A]]
	// Informational only
	String() string
}

IP address set.

func NewSet

func NewSet[A ip.Number[A]](ranges ...AddressRange[A]) AddressSet[A]

Creates AddressSet from given IP address ranges. Ranges may overlap. If set reduces to contiguous range returns type that conforms to AddressRange.

Example
package main

import (
	"github.com/ipfreely-uk/go/ip"
	"github.com/ipfreely-uk/go/ip/network"
)

func main() {
	family := ip.V4()
	r0 := exampleRange(family, "192.0.2.0", "192.0.2.100")
	r1 := exampleRange(family, "192.0.2.101", "192.0.2.111")
	r2 := exampleRange(family, "192.0.2.200", "192.0.2.255")
	r3 := exampleRange(family, "203.0.113.0", "203.0.113.255")
	r4 := exampleRange(family, "192.0.2.0", "192.0.2.100")

	addresses := network.NewSet(r0, r1, r2, r3, r4)

	println("Rationalized ranges:")
	next := addresses.Ranges()
	for aRange, exists := next(); exists; aRange, exists = next() {
		println(aRange.String())
	}
}

func exampleRange[A ip.Number[A]](family ip.Family[A], first, last string) network.AddressRange[A] {
	a0 := ip.MustParse(family, first)
	a1 := ip.MustParse(family, last)
	return network.NewRange(a0, a1)
}
Output:

type Block

type Block[A ip.Number[A]] interface {
	AddressRange[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/ip/network"
)

func main() {
	netAddress := ip.MustParse(ip.V6(), "2001:db8:cafe::")
	block := network.NewBlock(netAddress, 56)

	randomAddr := randomAddressFrom(block)

	println("Random address from", block.String(), "=", randomAddr.String())
}

func randomAddressFrom[A ip.Number[A]](netBlock network.Block[A]) (address A) {
	netAddr := netBlock.First()
	family := netAddr.Family()
	inverseMask := netBlock.Mask().Not()

	return random(family).And(inverseMask).Or(netAddr)
}

func random[A ip.Number[A]](f ip.Family[A]) (address A) {
	slice := make([]byte, f.Width()/8)
	_, _ = rand.Read(slice)
	return f.MustFromBytes(slice...)
}
Output:

Example (Second)
package main

import (
	"github.com/ipfreely-uk/go/ip"
	"github.com/ipfreely-uk/go/ip/compare"
	"github.com/ipfreely-uk/go/ip/network"
)

func main() {
	netAddress := ip.MustParse(ip.V6(), "2001:db8:cafe::")
	block := network.NewBlock(netAddress, 56)

	next := split(block, 60)
	for subnet, exists := next(); exists; subnet, exists = next() {
		println(subnet.String())
	}
}

// Split subnet into smaller subnets
func split[A ip.Number[A]](b network.Block[A], newMaskSize int) network.Iterator[network.Block[A]] {
	if newMaskSize < b.MaskSize() {
		panic("invalid split size")
	}
	current := network.NewBlock(b.First(), newMaskSize)
	one := current.First().Family().FromInt(1)
	increment := current.Last().Subtract(current.First()).Add(one)
	exhausted := false

	return func() (element network.Block[A], exists bool) {
		if exhausted {
			return nil, false
		}
		result := current
		exhausted = compare.Eq(current.Last(), b.Last())
		if !exhausted {
			first := current.First().Add(increment)
			current = network.NewBlock(first, newMaskSize)
		}
		return result, true
	}
}
Output:

func NewBlock

func NewBlock[A ip.Number[A]](network A, mask int) Block[A]

Creates Block. Panics if mask does not cover network address or is out of range for address family.

Example
package main

import (
	humanize "github.com/dustin/go-humanize"
	"github.com/ipfreely-uk/go/ip"
	"github.com/ipfreely-uk/go/ip/network"
)

func main() {
	netAddress := ip.MustParse(ip.V6(), "2001:db8::")

	block := network.NewBlock(netAddress, 32)

	println("Block", block.String())
	println("First", block.First().String())
	println("Last", block.Last().String())
	println("Size", humanize.BigComma(block.Size()))
}
Output:

type Iterator

type Iterator[E any] func() (element E, exists bool)

Iterator function that returns whether element returned and element

Example
package main

import (
	"github.com/ipfreely-uk/go/ip"
	"github.com/ipfreely-uk/go/ip/network"
)

func main() {
	netaddr, mask, _ := network.ParseCIDRNotation(ip.V4(), "192.0.2.128/28")
	subnet := network.NewBlock(netaddr, mask)
	firstAssigneable := ip.Next(subnet.First())
	lastAssigneable := ip.Prev(subnet.Last())
	assignable := network.NewRange(firstAssigneable, lastAssigneable)

	// iterator of addresses
	next := assignable.Addresses()
	for address, exists := next(); exists; address, exists = next() {
		println(address.String())
	}
}
Output:

func Blocks

func Blocks[A ip.Number[A]](r AddressRange[A]) Iterator[Block[A]]

Subdivides AddressRange into valid CIDR blocks

Example
package main

import (
	"github.com/ipfreely-uk/go/ip"
	"github.com/ipfreely-uk/go/ip/network"
)

func main() {
	first := ip.V4().MustFromBytes(192, 0, 2, 101)
	last := ip.V4().MustFromBytes(192, 0, 2, 240)
	freeAddresses := network.NewRange(first, last)

	printCidrBlocksIn(freeAddresses)
}

func printCidrBlocksIn[A ip.Number[A]](addressRange network.AddressRange[A]) {
	next := network.Blocks(addressRange)
	for block, exists := next(); exists; block, exists = next() {
		println(block.String())
	}
}
Output:

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL