Skip to main content
Version: Next

go-ipam

Actions GoDoc Go Report Card codecov License

go-ipam is a module to handle IP address management. It can operate on networks, prefixes and IPs.

It also comes as a ready to go microservice which offers a grpc api.

IP

Most obvious this library is all about IP management. The main purpose is to acquire and release an IP, or a bunch of IP's from prefixes.

Prefix

A prefix is a network with IP and mask, typically in the form of 192.168.0.0/24. To be able to manage IPs you have to create a prefix first.

Library Example usage:


package main

import (
"context"
"fmt"
"time"

goipam "github.com/metal-stack/go-ipam"
)

func main() {
// The background context
bgCtx := context.Background()

// Create a ipamer with in memory storage
ipam := goipam.New(bgCtx)

// Optionally, we can pass around a context for a given namespace
namespace := "tenant-a"
err := ipam.CreateNamespace(bgCtx, namespace)
if err != nil {
panic(err)
}
ctx := goipam.NewContextWithNamespace(bgCtx, namespace)
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

// Create a prefix to manage some IPs
prefix, err := ipam.NewPrefix(ctx, "192.168.0.0/24")
if err != nil {
panic(err)
}

// Acquire and release an IP with this prefix
ip, err := ipam.AcquireIP(ctx, prefix.Cidr)
if err != nil {
panic(err)
}
fmt.Printf("got IP: %s\n", ip.IP)

prefix, err = ipam.ReleaseIP(ctx, ip)
if err != nil {
panic(err)
}
fmt.Printf("IP: %s released.\n", ip.IP)

// Now a IPv6 Super Prefix with Child Prefixes
prefix, err = ipam.NewPrefix(ctx, "2001:aabb::/48")
if err != nil {
panic(err)
}

cp1, err := ipam.AcquireChildPrefix(ctx, prefix.Cidr, 64)
if err != nil {
panic(err)
}
fmt.Printf("got Prefix: %s\n", cp1)

cp2, err := ipam.AcquireChildPrefix(ctx, prefix.Cidr, 72)
if err != nil {
panic(err)
}
fmt.Printf("got Prefix: %s\n", cp2)
ip21, err := ipam.AcquireIP(ctx, cp2.Cidr)
if err != nil {
panic(err)
}
fmt.Printf("got IP: %s\n", ip21.IP)
}

GRPC Service

First start the go-ipam container with the database backend of your choice already up and running. For example if you have a postgres database for storing the ipam data, you could run the grpc service like so:

docker run -it --rm ghcr.io/metal-stack/go-ipam postgres

From a client perspective you can now talk to this service via grpc.

GRPC Example usage:

package main

import (
"http"

"github.com/bufbuild/connect-go"
v1 "github.com/metal-stack/go-ipam/api/v1"
"github.com/metal-stack/go-ipam/api/v1/apiv1connect"
)
func main() {

c := apiv1connect.NewIpamServiceClient(
http.DefaultClient,
"http://localhost:9090",
connect.WithGRPC(),
)

bgCtx := context.Background()

// Optional with Namespace
ctx := goipam.NewContextWithNamespace(bgCtx, "tenant-a")

result, err := c.CreatePrefix(ctx, connect.NewRequest(&v1.CreatePrefixRequest{Cidr: "192.168.0.0/16",}))
if err != nil {
panic(err)
}
fmt.Println("Prefix:%q created", result.Msg.GetPrefix().GetCidr())
}

GRPC client

There is also a cli provided in the container which can be used to make calls to the grpc endpoint manually:

docker run -it --rm --entrypoint /cli ghcr.io/metal-stack/go-ipam

Metrics

http://localhost:2112/metrics

pprof

go tool pprof -http :8080 localhost:2113/debug/pprof/heap
go tool pprof -http :8080 localhost:2113/debug/pprof/goroutine

Docker Compose example

Ensure you have docker with compose support installed. Then execute the following command:

docker compose up -d

# check if up and running
docker compose ps

NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
go-ipam-ipam-1 go-ipam "/server postgres" ipam 14 seconds ago Up 13 seconds (healthy) 0.0.0.0:9090->9090/tcp, :::9090->9090/tcp
go-ipam-postgres-1 postgres:alpine "docker-entrypoint.s…" postgres 8 minutes ago Up 13 seconds 5432/tcp


# Then execute the cli to create prefixes and acquire ips

docker compose exec ipam /cli prefix create --cidr 192.168.0.0/16
prefix:"192.168.0.0/16" created

docker compose exec ipam /cli ip acquire --prefix 192.168.0.0/16
ip:"192.168.0.1" acquired

# Queries can also made against the Rest api like so:

curl -v -X POST -d '{}' -H 'Content-Type: application/json' localhost:9090/api.v1.IpamService/ListPrefixes

Supported Databases & Performance

DatabaseAcquire Child PrefixAcquire IPNew PrefixPrefix OverlapProduction-ReadyGeo-Redundant
In-Memory106,861/sec196,687/sec330,578/sec248/secNN
FileNN
KeyDB777/sec975/sec2,271/secYY
Redis773/sec958/sec2,349/secYN
MongoDB415/sec682/sec772/secYY
Etcd258/sec368/sec533/secYN
Postgres203/sec331/sec472/secYN
CockroachDB170/sec300/sec470/secYY

The benchmarks above were performed using:

  • cpu: Intel(R) Xeon(R) Platinum 8370C CPU @ 2.80GHz
  • postgres:17-alpine
  • cockroach:v24.1.0
  • redis:7.4-alpine
  • keydb:alpine_x86_64_v6.3.1
  • etcd:v3.5.15
  • mongodb:7

Database Version Compatibility

DatabaseDetails
KeyDB
Redis
MongoDBmongodb-go compatibility
Etcd
Postgres
CockroachDB

Testing individual Backends

It is possible to test a individual backend only to speed up development roundtrip.

backend can be one of Memory, Postgres, Cockroach, Etcd, Redis, and MongoDB.

BACKEND=backend make test