cidrgen

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 10, 2026 License: MIT Imports: 7 Imported by: 0

README

cidrgen

Generate non-overlapping IPv4 CIDR blocks from a parent superset and previously allocated CIDR list.

API not yet stable. cidrgen is at v0.x; the exported API may change in any release. See docs/releasing.md.

How

Create a Generator for a parent CIDR, then, given the blocks already carved out of it, Generate returns the lowest-address, correctly-aligned free block of a requested size. The size is given directly as a prefix length, or indirectly through a classification name that maps to one.

import "github.com/Mitsuwa/cidrgen"

// Explicit size.
g, err := cidrgen.New("10.0.0.0/16", nil)
p, err := g.Generate(cidrgen.Request{
    Allocated: []string{"10.0.0.0/24", "10.0.2.0/24"},
    Netmask:   24,
})

// will generate
// p == 10.0.1.0/24

// Size by classification.
classes := map[string]int{"datanode": 28, "computenode": 27}
g, err = cidrgen.New("10.0.0.0/24", classes)
p, err = g.Generate(cidrgen.Request{
    Allocated:      []string{"10.0.0.0/28"},
    Classification: "datanode",
})

// will generate
// p == 10.0.0.16/28

Netmask wins over Classification when both are set. The classification map can be loaded from YAML:

# classifications.yaml
classifications:
  datanode: 28
  computenode: 27
f, _ := os.Open("classifications.yaml")
classes, err := cidrgen.LoadClassifications(f)
g, err := cidrgen.New("10.0.0.0/16", classes)

Behavior

  • Immutable after New. The parent and classification map are fixed at construction; a Generator is safe for concurrent use. Re-supply Allocated on every call; append each result before requesting the next block.
  • IPv4 only.
  • CIDR strings with host bits set (10.0.0.5/24) are accepted and canonicalized.
  • First-fit: the returned block is the lowest-address aligned gap that fits.

Errors

All errors match one of the package sentinels with errors.Is: ErrNoSizeSpecified, ErrUnknownClassification, ErrOverlappingInput, ErrOutOfParent, ErrInvalidPrefix, ErrNoSpace.

Documentation

docs/design.md The problem and the design decisions
docs/api.md Full API reference
docs/algorithm.md How first-fit allocation works
docs/classifications.md Classification YAML and size resolution
docs/development.md Local workflow and CI
docs/releasing.md How a version is tagged and released

Development

See CLAUDE.md for the working agreement, docs/development.md for the workflow, and ISSUES/ for the planned work.

go vet ./...
go test ./... -race

Documentation

Overview

Package cidrgen allocates non-overlapping IPv4 CIDR blocks from a parent pool.

A Generator is created with New from a parent CIDR and a classification map. Given the list of CIDRs already carved out of the parent, Generator.Generate returns the lowest-address, correctly-aligned free block of a requested size. The size is given either directly as a prefix length (Request.Netmask) or indirectly through a classification name that maps to a prefix length (Request.Classification, resolved against the map passed to New). The classification map can be loaded from a YAML document with LoadClassifications.

A Generator is immutable after New and safe for concurrent use. The package holds no state: callers re-supply the full Allocated list on every call and append each result to it before requesting the next block.

Only IPv4 is supported.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNoSizeSpecified is returned when a Request supplies neither Netmask nor
	// Classification.
	ErrNoSizeSpecified = errors.New("cidrgen: neither Netmask nor Classification specified")

	// ErrUnknownClassification is returned when Request.Classification is not a
	// key in the classification map passed to New.
	ErrUnknownClassification = errors.New("cidrgen: classification not found in Classifications map")

	// ErrOverlappingInput is returned when two entries in Request.Allocated
	// overlap each other.
	ErrOverlappingInput = errors.New("cidrgen: allocated CIDRs overlap each other")

	// ErrOutOfParent is returned when an entry in Request.Allocated is not fully
	// contained within the Generator's parent.
	ErrOutOfParent = errors.New("cidrgen: allocated CIDR is not contained within Parent")

	// ErrInvalidPrefix is returned for an unparseable CIDR string, a non-IPv4
	// CIDR, or a requested prefix length that is not strictly longer than the
	// parent prefix and within 1..32.
	ErrInvalidPrefix = errors.New("cidrgen: invalid CIDR or prefix length")

	// ErrNoSpace is returned when no free aligned block of the requested size
	// fits within the Generator's parent.
	ErrNoSpace = errors.New("cidrgen: no free aligned block of the requested size fits within Parent")
)

Sentinel errors returned by New, Generator.Generate, and LoadClassifications. Callers can match them with errors.Is.

Functions

func LoadClassifications

func LoadClassifications(r io.Reader) (map[string]int, error)

LoadClassifications parses a YAML document mapping classification names to prefix lengths and returns it as a map suitable for the classifications argument of New:

classifications:
  datanode: 28
  computenode: 27

Unknown top-level keys, a missing "classifications" key, a non-integer value, or a prefix length outside 0..32 are errors.

Types

type Generator

type Generator struct {
	// contains filtered or unexported fields
}

Generator allocates non-overlapping CIDRs from a fixed parent pool. It is created with New, is immutable afterward, and is safe for concurrent use by multiple goroutines as long as each call supplies its own Request.

func New

func New(parent string, classifications map[string]int) (*Generator, error)

New builds a Generator for the given parent pool (e.g. "10.0.0.0/16") and classification map. The parent is parsed and canonicalized now, so a CIDR string with host bits set is accepted and an unparseable or non-IPv4 parent is an ErrInvalidPrefix returned here rather than from Generate.

classifications maps a classification name to a prefix length; it may be nil when callers only ever request an explicit Netmask. New copies the map, so later mutation by the caller does not affect the Generator. Values are not range-checked here: a length that cannot sit inside the parent is reported by Generate as ErrInvalidPrefix, and an unknown name as ErrUnknownClassification.

func (*Generator) Generate

func (g *Generator) Generate(req Request) (netip.Prefix, error)

Generate returns the lowest-address, correctly-aligned CIDR of the requested size that fits within the Generator's parent without overlapping any entry in req.Allocated.

The requested size comes from req.Netmask when it is non-zero, otherwise from looking up req.Classification in the Generator's classification map. It is an error to supply neither.

Errors are one of the sentinels declared in this package, wrapped with context; match them with errors.Is.

type Request

type Request struct {
	// Allocated lists the CIDRs already carved from the Generator's parent. Each
	// must be inside the parent, and no two may overlap. CIDR strings with host
	// bits set are accepted and canonicalized.
	Allocated []string

	// Netmask is the prefix length for the new CIDR. When non-zero it takes
	// precedence over Classification.
	Netmask int

	// Classification names an entry in the Generator's classification map; used
	// when Netmask is 0.
	Classification string
}

Request is the per-call input to Generator.Generate. The parent pool and the classification map are fixed for the life of a Generator and are supplied to New; only the fields here change between calls. The package holds no state, so the caller supplies Allocated in full on every call.

Jump to

Keyboard shortcuts

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