Documentation
¶
Overview ¶
Package ipx is a dependency-free IP networking toolkit built on net/netip.
It covers parsing and validation, classification against the IANA special-purpose registries, 128-bit address arithmetic, CIDR math (split, aggregate, subtract, supernet), address ranges, immutable IP sets with set algebra, a longest-prefix-match table, ACLs, an address allocator, reverse DNS names and SSRF-oriented security checks.
Design rules:
- Every operation on user input returns an error or a bool; nothing panics.
- IPv4-mapped IPv6 addresses (::ffff:a.b.c.d) are unmapped before classification, set membership and ACL matching, so an attacker cannot bypass an IPv4 rule by writing the address in IPv6 form.
- Values (Range, IPSet) are immutable and safe for concurrent reads. Mutable structures (Table, ACL, Allocator) document their own locking.
Index ¶
- Variables
- func Add(a netip.Addr, n int64) (netip.Addr, error)
- func AddBig(a netip.Addr, n *big.Int) (netip.Addr, error)
- func Adjacent(a, b netip.Prefix) bool
- func Aggregate(ps []netip.Prefix) []netip.Prefix
- func AggregateAddrs(as []netip.Addr) []netip.Prefix
- func Broadcast(p netip.Prefix) (netip.Addr, bool)
- func Children(p netip.Prefix) (lo, hi netip.Prefix, ok bool)
- func CommonAncestor(ps ...netip.Prefix) (netip.Prefix, error)
- func CommonPrefix(a, b netip.Addr) (netip.Prefix, error)
- func CompareAddr(a, b netip.Addr) int
- func CompareEndpoint(a, b netip.AddrPort) int
- func ComparePrefix(a, b netip.Prefix) int
- func CompareRange(a, b Range) int
- func ContainsPrefix(outer, inner netip.Prefix) bool
- func DedupAddrs(as []netip.Addr) []netip.Addr
- func Distance(a, b netip.Addr) (*big.Int, error)
- func EmbeddedIPv4(a netip.Addr) (netip.Addr, bool)
- func ExpandIPv6(a netip.Addr) string
- func Family(a netip.Addr) int
- func First(p netip.Prefix) netip.Addr
- func FirstUsable(p netip.Prefix) netip.Addr
- func FormatEndpoint(a netip.Addr, port uint16) string
- func FromBig(n *big.Int, ipv6 bool) (netip.Addr, error)
- func FromHex(s string) (netip.Addr, error)
- func FromIPNet(n *net.IPNet) (netip.Prefix, bool)
- func FromStdIP(ip net.IP) (netip.Addr, bool)
- func Hostmask(p netip.Prefix) netip.Addr
- func Hosts(p netip.Prefix) iter.Seq[netip.Addr]
- func IPv4ToMapped(a netip.Addr) (netip.Addr, error)
- func IPv4ToUint32(a netip.Addr) (uint32, error)
- func IntersectPrefix(a, b netip.Prefix) (netip.Prefix, bool)
- func IsBenchmarking(a netip.Addr) bool
- func IsCloudMetadata(a netip.Addr) bool
- func IsDocumentation(a netip.Addr) bool
- func IsGlobalUnicast(a netip.Addr) bool
- func IsIPv4(a netip.Addr) bool
- func IsIPv4Compatible(a netip.Addr) bool
- func IsIPv4Mapped(a netip.Addr) bool
- func IsIPv6(a netip.Addr) bool
- func IsInterfaceLocal(a netip.Addr) bool
- func IsInternal(a netip.Addr) bool
- func IsLinkLocal(a netip.Addr) bool
- func IsLoopback(a netip.Addr) bool
- func IsMulticast(a netip.Addr) bool
- func IsPrivate(a netip.Addr) bool
- func IsPublic(a netip.Addr) bool
- func IsReserved(a netip.Addr) bool
- func IsSafeTarget(a netip.Addr) bool
- func IsShared(a netip.Addr) bool
- func IsSpecial(a netip.Addr) bool
- func IsUnicast(a netip.Addr) bool
- func IsUnspecified(a netip.Addr) bool
- func IsValidAddr(s string) bool
- func IsValidPrefix(s string) bool
- func Last(p netip.Prefix) netip.Addr
- func LastUsable(p netip.Prefix) netip.Addr
- func MaxAddr(as []netip.Addr) (netip.Addr, bool)
- func MinAddr(as []netip.Addr) (netip.Addr, bool)
- func MustParseAddr(s string) netip.Addr
- func MustParsePrefix(s string) netip.Prefix
- func Netmask(p netip.Prefix) netip.Addr
- func Next(a netip.Addr) (netip.Addr, error)
- func Normalize(a netip.Addr) netip.Addr
- func NormalizeEndpoint(ap netip.AddrPort) netip.AddrPort
- func NormalizePrefix(p netip.Prefix) netip.Prefix
- func Nth(p netip.Prefix, n *big.Int) (netip.Addr, error)
- func Offset(p netip.Prefix, a netip.Addr) (*big.Int, error)
- func Parent(p netip.Prefix) (netip.Prefix, bool)
- func ParseAddr(s string) (netip.Addr, error)
- func ParseAddrs(ss []string) ([]netip.Addr, error)
- func ParseEndpoint(s string) (netip.AddrPort, error)
- func ParseHost(s string) (netip.Addr, error)
- func ParseNetwork(s string) (netip.Prefix, error)
- func ParsePort(s string) (uint16, error)
- func ParsePrefix(s string) (netip.Prefix, error)
- func ParsePrefixStrict(s string) (netip.Prefix, error)
- func ParsePrefixes(ss []string) ([]netip.Prefix, error)
- func ParseReverseName(s string) (netip.Addr, error)
- func PlanSubnets(parent netip.Prefix, lengths []int) ([]netip.Prefix, error)
- func PrefixFromMask(a, mask netip.Addr) (netip.Prefix, error)
- func PrefixLenForHosts(hosts uint64, ipv6 bool) (int, error)
- func Prev(a netip.Addr) (netip.Addr, error)
- func RFC2317Zone(p netip.Prefix) (string, error)
- func ReverseName(a netip.Addr) (string, error)
- func ReverseZones(p netip.Prefix) ([]string, error)
- func Sibling(p netip.Prefix) (netip.Prefix, bool)
- func Size(p netip.Prefix) *big.Int
- func SortAddrs(as []netip.Addr)
- func SortPrefixes(ps []netip.Prefix)
- func Split(p netip.Prefix, newBits int) (iter.Seq[netip.Prefix], error)
- func SplitHostPort(s string) (host string, port uint16, err error)
- func SplitN(p netip.Prefix, newBits, limit int) ([]netip.Prefix, error)
- func SubnetCount(p netip.Prefix, newBits int) (*big.Int, error)
- func SubnetIndex(p netip.Prefix, newBits int, a netip.Addr) (*big.Int, error)
- func SubtractPrefixes(from, remove []netip.Prefix) []netip.Prefix
- func Supernet(p netip.Prefix, bits int) (netip.Prefix, error)
- func ToBig(a netip.Addr) *big.Int
- func ToHex(a netip.Addr) string
- func ToIPNet(p netip.Prefix) *net.IPNet
- func ToStdIP(a netip.Addr) net.IP
- func Uint32ToIPv4(n uint32) netip.Addr
- func UsableHosts(p netip.Prefix) *big.Int
- type ACL
- type Action
- type AddrInfo
- type Allocator
- func (al *Allocator) Allocate() (netip.Addr, error)
- func (al *Allocator) AllocateN(n int) ([]netip.Addr, error)
- func (al *Allocator) AllocatePrev() (netip.Addr, error)
- func (al *Allocator) AllocateRandom() (netip.Addr, error)
- func (al *Allocator) Allocated() []netip.Addr
- func (al *Allocator) Available() *big.Int
- func (al *Allocator) Claim(a netip.Addr) error
- func (al *Allocator) IsFree(a netip.Addr) bool
- func (al *Allocator) Release(a netip.Addr) bool
- func (al *Allocator) Reserve(a netip.Addr) error
- type Builder
- func (b *Builder) AddAddr(a netip.Addr)
- func (b *Builder) AddPrefix(p netip.Prefix)
- func (b *Builder) AddRange(r Range)
- func (b *Builder) AddSet(s *IPSet)
- func (b *Builder) IPSet() *IPSet
- func (b *Builder) RemoveAddr(a netip.Addr)
- func (b *Builder) RemovePrefix(p netip.Prefix)
- func (b *Builder) RemoveRange(r Range)
- func (b *Builder) RemoveSet(s *IPSet)
- type CNAME
- type Category
- type IPSet
- func (s *IPSet) All() iter.Seq[netip.Addr]
- func (s *IPSet) Complement() *IPSet
- func (s *IPSet) Contains(a netip.Addr) bool
- func (s *IPSet) ContainsBatch(as []netip.Addr) []bool
- func (s *IPSet) ContainsPrefix(p netip.Prefix) bool
- func (s *IPSet) ContainsRange(r Range) bool
- func (s *IPSet) Difference(o *IPSet) *IPSet
- func (s *IPSet) Equal(o *IPSet) bool
- func (s *IPSet) Intersect(o *IPSet) *IPSet
- func (s *IPSet) IsEmpty() bool
- func (s *IPSet) MarshalText() ([]byte, error)
- func (s *IPSet) Overlaps(o *IPSet) bool
- func (s *IPSet) Prefixes() []netip.Prefix
- func (s *IPSet) Ranges() []Range
- func (s *IPSet) Size() *big.Int
- func (s *IPSet) String() string
- func (s *IPSet) SymmetricDifference(o *IPSet) *IPSet
- func (s *IPSet) Union(o *IPSet) *IPSet
- func (s *IPSet) UnmarshalText(b []byte) error
- type LookupResult
- type Overlap
- type PrefixInfo
- type PrefixNode
- type Range
- func (r Range) Adjacent(o Range) bool
- func (r Range) All() iter.Seq[netip.Addr]
- func (r Range) Contains(a netip.Addr) bool
- func (r Range) ContainsRange(o Range) bool
- func (r Range) From() netip.Addr
- func (r Range) Intersect(o Range) (Range, bool)
- func (r Range) IsValid() bool
- func (r Range) MarshalBinary() ([]byte, error)
- func (r Range) MarshalText() ([]byte, error)
- func (r Range) Merge(o Range) (Range, bool)
- func (r Range) Overlaps(o Range) bool
- func (r Range) Prefix() (netip.Prefix, bool)
- func (r Range) Prefixes() []netip.Prefix
- func (r Range) Size() *big.Int
- func (r Range) SplitRange(n uint64) []Range
- func (r Range) String() string
- func (r Range) Subtract(o Range) []Range
- func (r Range) To() netip.Addr
- func (r *Range) UnmarshalBinary(b []byte) error
- func (r *Range) UnmarshalText(b []byte) error
- func (r Range) Walk(ctx context.Context, fn func(netip.Addr) bool) error
- type Rule
- type SafeDialer
- type SpecialBlock
- type Strategy
- type SyncTable
- func (s *SyncTable[V]) Delete(p netip.Prefix) bool
- func (s *SyncTable[V]) Get(p netip.Prefix) (V, bool)
- func (s *SyncTable[V]) Insert(p netip.Prefix, v V) bool
- func (s *SyncTable[V]) Len() int
- func (s *SyncTable[V]) Lookup(a netip.Addr) (netip.Prefix, V, bool)
- func (s *SyncTable[V]) Snapshot() map[netip.Prefix]V
- type Table
- func (t *Table[V]) All() iter.Seq2[netip.Prefix, V]
- func (t *Table[V]) Contains(a netip.Addr) bool
- func (t *Table[V]) Delete(p netip.Prefix) bool
- func (t *Table[V]) Get(p netip.Prefix) (V, bool)
- func (t *Table[V]) Insert(p netip.Prefix, v V) bool
- func (t *Table[V]) Len() int
- func (t *Table[V]) Lookup(a netip.Addr) (netip.Prefix, V, bool)
- func (t *Table[V]) LookupBatch(as []netip.Addr) []LookupResult[V]
- func (t *Table[V]) LookupShortest(a netip.Addr) (netip.Prefix, V, bool)
- func (t *Table[V]) Matches(a netip.Addr) []V
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrInvalidAddr = errors.New("ipx: invalid IP address") ErrInvalidPrefix = errors.New("ipx: invalid CIDR prefix") ErrInvalidRange = errors.New("ipx: invalid IP range") ErrInvalidPort = errors.New("ipx: invalid port") ErrInvalidEndpoint = errors.New("ipx: invalid endpoint") ErrInvalidReverse = errors.New("ipx: invalid reverse DNS name") ErrHostBitsSet = errors.New("ipx: prefix has host bits set") ErrFamilyMismatch = errors.New("ipx: IPv4/IPv6 family mismatch") ErrOverflow = errors.New("ipx: address arithmetic overflow") ErrExhausted = errors.New("ipx: address pool exhausted") ErrNotInPool = errors.New("ipx: address outside pool") ErrInUse = errors.New("ipx: address already allocated or reserved") ErrBlockedAddr = errors.New("ipx: destination address blocked") )
Sentinel errors. Match with errors.Is.
var CloudMetadata = []netip.Prefix{ MustParsePrefix("169.254.169.254/32"), MustParsePrefix("169.254.170.2/32"), MustParsePrefix("100.100.100.200/32"), MustParsePrefix("fd00:ec2::254/128"), }
CloudMetadata lists well-known cloud instance-metadata endpoints.
var SpecialBlocks = []SpecialBlock{ {MustParsePrefix("0.0.0.0/8"), "This network", "RFC 791", CatThisNetwork, false}, {MustParsePrefix("10.0.0.0/8"), "Private-Use", "RFC 1918", CatPrivate, false}, {MustParsePrefix("100.64.0.0/10"), "Shared Address Space", "RFC 6598", CatShared, false}, {MustParsePrefix("127.0.0.0/8"), "Loopback", "RFC 1122", CatLoopback, false}, {MustParsePrefix("169.254.0.0/16"), "Link Local", "RFC 3927", CatLinkLocal, false}, {MustParsePrefix("172.16.0.0/12"), "Private-Use", "RFC 1918", CatPrivate, false}, {MustParsePrefix("192.0.0.0/24"), "IETF Protocol Assignments", "RFC 6890", CatProtocol, false}, {MustParsePrefix("192.0.2.0/24"), "Documentation (TEST-NET-1)", "RFC 5737", CatDocumentation, false}, {MustParsePrefix("192.88.99.0/24"), "6to4 Relay Anycast (deprecated)", "RFC 7526", CatTranslation, false}, {MustParsePrefix("192.168.0.0/16"), "Private-Use", "RFC 1918", CatPrivate, false}, {MustParsePrefix("198.18.0.0/15"), "Benchmarking", "RFC 2544", CatBenchmarking, false}, {MustParsePrefix("198.51.100.0/24"), "Documentation (TEST-NET-2)", "RFC 5737", CatDocumentation, false}, {MustParsePrefix("203.0.113.0/24"), "Documentation (TEST-NET-3)", "RFC 5737", CatDocumentation, false}, {MustParsePrefix("224.0.0.0/4"), "Multicast", "RFC 5771", CatMulticast, false}, {MustParsePrefix("240.0.0.0/4"), "Reserved", "RFC 1112", CatReserved, false}, {MustParsePrefix("255.255.255.255/32"), "Limited Broadcast", "RFC 919", CatBroadcast, false}, {MustParsePrefix("::/128"), "Unspecified", "RFC 4291", CatUnspecified, false}, {MustParsePrefix("::1/128"), "Loopback", "RFC 4291", CatLoopback, false}, {MustParsePrefix("::ffff:0:0/96"), "IPv4-mapped", "RFC 4291", CatIPv4Mapped, false}, {MustParsePrefix("64:ff9b::/96"), "IPv4-IPv6 Translation", "RFC 6052", CatTranslation, true}, {MustParsePrefix("64:ff9b:1::/48"), "Local-use IPv4/IPv6 Translation", "RFC 8215", CatTranslation, false}, {MustParsePrefix("100::/64"), "Discard-Only", "RFC 6666", CatDiscard, false}, {MustParsePrefix("2001::/23"), "IETF Protocol Assignments", "RFC 2928", CatProtocol, false}, {MustParsePrefix("2001:2::/48"), "Benchmarking", "RFC 5180", CatBenchmarking, false}, {MustParsePrefix("2001:db8::/32"), "Documentation", "RFC 3849", CatDocumentation, false}, {MustParsePrefix("2002::/16"), "6to4", "RFC 3056", CatTranslation, false}, {MustParsePrefix("3fff::/20"), "Documentation", "RFC 9637", CatDocumentation, false}, {MustParsePrefix("fc00::/7"), "Unique-Local", "RFC 4193", CatUniqueLocal, false}, {MustParsePrefix("fe80::/10"), "Link-Local Unicast", "RFC 4291", CatLinkLocal, false}, {MustParsePrefix("ff00::/8"), "Multicast", "RFC 4291", CatMulticast, false}, }
SpecialBlocks lists the IANA IPv4/IPv6 special-purpose address blocks. Treat it as read-only.
Functions ¶
func Aggregate ¶
Aggregate returns the minimal CIDR list covering all input prefixes: merges adjacent blocks, removes nested/duplicate ones and sorts.
Example ¶
package main
import (
"fmt"
"net/netip"
"github.com/bakhod1r/ipx"
)
func main() {
fmt.Println(ipx.Aggregate([]netip.Prefix{
netip.MustParsePrefix("10.0.0.0/25"),
netip.MustParsePrefix("10.0.0.128/25"),
netip.MustParsePrefix("10.0.1.0/24"),
}))
}
Output: [10.0.0.0/23]
func AggregateAddrs ¶
AggregateAddrs returns the minimal CIDR list covering the addresses.
func CommonAncestor ¶
CommonAncestor returns the longest prefix containing every input.
func CommonPrefix ¶
CommonPrefix returns the longest prefix containing both addresses.
func CompareAddr ¶
CompareAddr orders addresses: IPv4 before IPv6, then numerically.
func CompareEndpoint ¶
CompareEndpoint orders by address, then port.
func ComparePrefix ¶
ComparePrefix orders prefixes: IPv4 first, then by network address, then shorter prefix first. Suitable for slices.SortFunc.
func ContainsPrefix ¶
ContainsPrefix reports whether outer fully contains inner.
func DedupAddrs ¶
DedupAddrs normalizes (unmaps, drops zone), sorts and removes duplicates, returning a new slice.
func EmbeddedIPv4 ¶
EmbeddedIPv4 extracts an IPv4 address carried inside an IPv6 address via IPv4-mapped, IPv4-compatible, NAT64 (64:ff9b::/96) or 6to4 (2002::/16).
func ExpandIPv6 ¶
ExpandIPv6 returns the fully expanded form (2001:0db8:0000:…:0001). IPv4 addresses are returned in dotted form unchanged.
func FirstUsable ¶
FirstUsable returns the first assignable host address.
func FormatEndpoint ¶
FormatEndpoint formats a host and port, bracketing IPv6.
func IPv4ToMapped ¶
IPv4ToMapped returns ::ffff:a.b.c.d for an IPv4 address.
func IPv4ToUint32 ¶
IPv4ToUint32 converts an IPv4 (or IPv4-mapped) address to uint32.
func IntersectPrefix ¶
IntersectPrefix returns a ∩ b as a prefix (the longer one) if they overlap.
func IsBenchmarking ¶
func IsCloudMetadata ¶
IsCloudMetadata reports whether a is a known cloud metadata endpoint, including when embedded in IPv4-mapped/compatible/NAT64/6to4 form.
func IsDocumentation ¶
func IsGlobalUnicast ¶
func IsIPv4Compatible ¶
IsIPv4Compatible reports deprecated IPv4-compatible IPv6 (::a.b.c.d), excluding :: and ::1.
func IsInterfaceLocal ¶
func IsInternal ¶
IsInternal reports whether a must not be reached from a server fetching user-supplied URLs (SSRF guard). True for invalid, loopback, private, link-local, shared, reserved, multicast, unspecified, documentation, benchmarking and cloud metadata addresses — also when hidden inside IPv4-mapped, IPv4-compatible, NAT64 or 6to4 IPv6 forms.
Resolve hostnames first and check every resolved address, then connect to the checked address (not the hostname) to avoid DNS rebinding.
Example ¶
package main
import (
"fmt"
"net/netip"
"github.com/bakhod1r/ipx"
)
func main() {
for _, s := range []string{"8.8.8.8", "::ffff:169.254.169.254", "64:ff9b::7f00:1"} {
fmt.Println(s, ipx.IsInternal(netip.MustParseAddr(s)))
}
}
Output: 8.8.8.8 false ::ffff:169.254.169.254 true 64:ff9b::7f00:1 true
func IsLinkLocal ¶
IsLinkLocal covers unicast and multicast link-local scopes.
func IsLoopback ¶
func IsMulticast ¶
func IsPublic ¶
IsPublic reports whether a is a globally reachable unicast address: global unicast and not inside any special block marked non-global.
func IsReserved ¶
func IsUnspecified ¶
func IsValidAddr ¶
IsValidAddr reports whether s is a valid IP address.
func IsValidPrefix ¶
IsValidPrefix reports whether s is a valid CIDR prefix.
func LastUsable ¶
LastUsable returns the last assignable host address.
func MustParseAddr ¶
MustParseAddr is ParseAddr for trusted constants; it panics on error.
func MustParsePrefix ¶
MustParsePrefix is ParsePrefix for trusted constants; it panics on error.
func Normalize ¶
Normalize unmaps IPv4-mapped IPv6 addresses (::ffff:1.2.3.4 → 1.2.3.4) and drops the zone. Use it before any security comparison.
func NormalizeEndpoint ¶
NormalizeEndpoint unmaps the address.
func NormalizePrefix ¶
NormalizePrefix masks host bits and unmaps IPv4-mapped IPv6 prefixes (::ffff:10.0.0.0/104 → 10.0.0.0/8). An invalid prefix is returned unchanged.
func ParseAddr ¶
ParseAddr parses an IPv4 or IPv6 address. It is strict: leading zeros in IPv4 octets, surrounding whitespace and brackets are rejected.
func ParseAddrs ¶
ParseAddrs parses every string and returns all failures joined.
func ParseEndpoint ¶
ParseEndpoint parses "ip:port" or "[ipv6]:port" (zone allowed). The address is normalized (IPv4-mapped unmapped).
func ParseHost ¶
ParseHost extracts and normalizes the IP from any of the SplitHostPort forms. Hostnames are rejected.
func ParseNetwork ¶
ParseNetwork accepts either a CIDR or a bare address (treated as /32 or /128) and returns the masked prefix.
func ParsePrefix ¶
ParsePrefix parses a CIDR prefix. Host bits are allowed and preserved (192.168.1.10/24 is valid); use ParsePrefixStrict to reject them.
func ParsePrefixStrict ¶
ParsePrefixStrict parses a CIDR prefix and rejects host bits (192.168.1.10/24 fails, 192.168.1.0/24 succeeds).
func ParsePrefixes ¶
ParsePrefixes parses every string (CIDR or bare address) and returns all failures joined.
func ParseReverseName ¶
ParseReverseName parses a full PTR name (trailing dot optional, case-insensitive) back to an address.
func PlanSubnets ¶
PlanSubnets carves variable-size subnets (VLSM) out of parent. lengths are the requested prefix lengths; the result is in the same order. Largest blocks are placed first so alignment waste is minimal.
Example ¶
package main
import (
"fmt"
"net/netip"
"github.com/bakhod1r/ipx"
)
func main() {
plan, _ := ipx.PlanSubnets(netip.MustParsePrefix("10.0.0.0/24"), []int{26, 25, 27})
fmt.Println(plan)
}
Output: [10.0.0.128/26 10.0.0.0/25 10.0.0.192/27]
func PrefixFromMask ¶
PrefixFromMask builds a prefix from an address and a dotted netmask such as 255.255.255.0. Non-contiguous masks are rejected.
func PrefixLenForHosts ¶
PrefixLenForHosts returns the longest prefix length whose usable host count is at least hosts. ipv6 selects the family.
func RFC2317Zone ¶ added in v0.4.0
RFC2317Zone returns the classless child zone name for an IPv4 prefix longer than /24 (RFC 2317), e.g. 192.0.2.64/26 → "64-127.2.0.192.in-addr.arpa.". The hyphen form is used because "/" needs escaping in many DNS tools.
func ReverseName ¶
ReverseName returns the PTR owner name, with trailing dot: 8.8.8.8 → "8.8.8.8.in-addr.arpa.", 2001:db8::1 → "1.0.0.…ip6.arpa.".
Example ¶
package main
import (
"fmt"
"net/netip"
"github.com/bakhod1r/ipx"
)
func main() {
n, _ := ipx.ReverseName(netip.MustParseAddr("8.8.4.4"))
fmt.Println(n)
}
Output: 4.4.8.8.in-addr.arpa.
func ReverseZones ¶
ReverseZones returns the reverse zone names delegating p. The prefix is rounded to the next octet (IPv4) or nibble (IPv6) boundary, so 10.0.0.0/23 yields two /24 zones. RFC 2317 classless delegation is not generated.
func SortPrefixes ¶
SortPrefixes sorts in place with ComparePrefix.
func Split ¶
Split lazily yields every /newBits subnet of p in order. Splitting 2001:db8::/32 into /64 yields 2^32 values, so stop early when needed.
func SplitHostPort ¶
SplitHostPort splits "host:port", "[v6]:port", "v4", "v6" or "[v6]". Port is 0 when absent. Host is returned without brackets.
func SplitN ¶
SplitN splits p into /newBits subnets and returns at most limit of them (limit ≤ 0 means no limit — use carefully on IPv6).
Example ¶
package main
import (
"fmt"
"net/netip"
"github.com/bakhod1r/ipx"
)
func main() {
subs, _ := ipx.SplitN(netip.MustParsePrefix("192.168.1.0/24"), 26, 0)
fmt.Println(subs)
}
Output: [192.168.1.0/26 192.168.1.64/26 192.168.1.128/26 192.168.1.192/26]
func SubnetCount ¶
SubnetCount returns how many /newBits subnets fit in p.
func SubnetIndex ¶
SubnetIndex returns which /newBits subnet of p contains a (0-based).
func SubtractPrefixes ¶
SubtractPrefixes returns the minimal CIDR list for from \ remove.
Example ¶
package main
import (
"fmt"
"net/netip"
"github.com/bakhod1r/ipx"
)
func main() {
fmt.Println(ipx.SubtractPrefixes(
[]netip.Prefix{netip.MustParsePrefix("10.0.0.0/24")},
[]netip.Prefix{netip.MustParsePrefix("10.0.0.0/25")},
))
}
Output: [10.0.0.128/25]
func Uint32ToIPv4 ¶
Uint32ToIPv4 converts n to an IPv4 address.
Types ¶
type ACL ¶
type ACL struct {
// contains filtered or unexported fields
}
ACL is an allow/deny list. Safe for concurrent use.
Example ¶
package main
import (
"fmt"
"net/netip"
"github.com/bakhod1r/ipx"
)
func main() {
acl := ipx.NewACL(ipx.LongestPrefix, ipx.Deny)
acl.Add(ipx.Rule{Prefix: netip.MustParsePrefix("10.0.0.0/8"), Action: ipx.Allow})
acl.Add(ipx.Rule{Prefix: netip.MustParsePrefix("10.66.0.0/16"), Action: ipx.Deny})
fmt.Println(acl.Decide(netip.MustParseAddr("10.1.1.1")), acl.Decide(netip.MustParseAddr("10.66.1.1")))
}
Output: allow deny
func Allowlist ¶
Allowlist denies everything except the given networks. Invalid prefixes are skipped.
func (*ACL) RemoveGroup ¶
RemoveGroup deletes all rules in group and returns how many.
type AddrInfo ¶
type AddrInfo struct {
Addr netip.Addr
Version int
Public bool
Private bool
Loopback bool
Multicast bool
LinkLocal bool
Internal bool
Special string // IANA block name, empty if none
Reverse string
Hex string
Expanded string
EmbeddedV4 netip.Addr
}
AddrInfo is an immutable report about an address.
type Allocator ¶
type Allocator struct {
// contains filtered or unexported fields
}
Allocator hands out addresses from a pool. Safe for concurrent use.
The pool is a prefix (usable hosts only) or a Range, minus exclusions. State is in memory; persist Allocated() yourself if needed.
Example ¶
package main
import (
"fmt"
"net/netip"
"github.com/bakhod1r/ipx"
)
func main() {
al, _ := ipx.NewAllocator(netip.MustParsePrefix("10.0.0.0/29"))
al.Reserve(netip.MustParseAddr("10.0.0.1")) // gateway
a, _ := al.Allocate()
fmt.Println(a, al.Available())
}
Output: 10.0.0.2 4
func NewAllocator ¶
NewAllocator creates a pool of p's usable hosts (IPv4 network and broadcast excluded for prefixes shorter than /31).
func NewRangeAllocator ¶
NewRangeAllocator creates a pool over r.
func (*Allocator) Allocate ¶
Allocate returns the next free address after the last one handed out, wrapping around. Returns ErrExhausted when the pool is full.
func (*Allocator) AllocateN ¶
AllocateN allocates n addresses sequentially; on failure nothing is kept.
func (*Allocator) AllocatePrev ¶
AllocatePrev returns the highest free address, scanning downward.
func (*Allocator) AllocateRandom ¶
AllocateRandom picks a uniformly random start point and returns the first free address from there. Useful to avoid predictable addresses in IPv6.
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder accumulates items for an IPSet. Not safe for concurrent use.
func (*Builder) RemoveAddr ¶
Remove* calls apply after all adds when IPSet is called.
func (*Builder) RemovePrefix ¶
func (*Builder) RemoveRange ¶
type CNAME ¶ added in v0.4.0
type CNAME struct {
Name, Target string
}
CNAME is one RFC 2317 delegation record: Name → Target.
type Category ¶
type Category uint16
Category labels a special-purpose block.
const ( CatThisNetwork Category = 1 << iota CatPrivate // RFC 1918 CatLoopback CatLinkLocal CatDocumentation CatBenchmarking CatMulticast CatReserved CatBroadcast CatUniqueLocal // RFC 4193 fc00::/7 CatIPv4Mapped CatTranslation // NAT64, 6to4 CatProtocol // IETF protocol assignments CatDiscard CatUnspecified )
func Categories ¶
Categories returns the category bits of every special block containing a.
type IPSet ¶
type IPSet struct {
// contains filtered or unexported fields
}
IPSet is an immutable set of addresses stored as sorted, merged ranges. The zero value is the empty set. All operations return new sets, so an IPSet is safe for concurrent use.
Inputs are normalized: IPv4-mapped IPv6 is treated as IPv4, zones dropped.
Example ¶
package main
import (
"fmt"
"net/netip"
"github.com/bakhod1r/ipx"
)
func main() {
a := ipx.NewIPSet(netip.MustParsePrefix("10.0.0.0/24"))
b := ipx.NewIPSet(netip.MustParsePrefix("10.0.0.128/25"))
fmt.Println(a.Difference(b))
fmt.Println(a.Contains(netip.MustParseAddr("::ffff:10.0.0.1")))
}
Output: 10.0.0.0/25 true
func ParseIPSet ¶
ParseIPSet parses a comma/space separated list of addresses, CIDRs and "a-b" ranges.
func (*IPSet) Complement ¶
Complement returns every address (IPv4 and IPv6) not in s.
func (*IPSet) ContainsBatch ¶ added in v0.4.0
ContainsBatch reports membership for each address, in order.
func (*IPSet) ContainsPrefix ¶
ContainsPrefix reports whether all of p is in the set.
func (*IPSet) ContainsRange ¶
ContainsRange reports whether all of r is in the set.
func (*IPSet) MarshalText ¶
MarshalText encodes the set as its comma-separated prefix list.
func (*IPSet) SymmetricDifference ¶
SymmetricDifference returns (s \ o) ∪ (o \ s).
func (*IPSet) UnmarshalText ¶
UnmarshalText accepts anything ParseIPSet accepts.
type LookupResult ¶ added in v0.4.0
LookupResult is one answer of LookupBatch.
type Overlap ¶ added in v0.4.0
Overlap is a pair of overlapping prefixes; Outer contains Inner (they are equal for duplicates). CIDR blocks never partially overlap.
func FindOverlaps ¶ added in v0.4.0
FindOverlaps reports every overlapping pair in ps (duplicates included), sorted by Outer then Inner. Inputs are normalized; invalid ones skipped. O(n log n + k) for k reported pairs.
type PrefixInfo ¶
type PrefixInfo struct {
Prefix netip.Prefix
Version int
Bits int
Netmask netip.Addr
Hostmask netip.Addr
Network netip.Addr
Broadcast netip.Addr // invalid for IPv6
FirstUsable netip.Addr
LastUsable netip.Addr
Size *big.Int
UsableHosts *big.Int
}
PrefixInfo is an immutable report about a network.
func InspectPrefix ¶
func InspectPrefix(p netip.Prefix) (PrefixInfo, error)
InspectPrefix builds a PrefixInfo; host bits are masked.
func (PrefixInfo) String ¶
func (i PrefixInfo) String() string
type PrefixNode ¶ added in v0.4.0
type PrefixNode struct {
Prefix netip.Prefix
Children []*PrefixNode
}
PrefixNode is one network in a hierarchy built by BuildTree.
func BuildTree ¶ added in v0.4.0
func BuildTree(ps []netip.Prefix) []*PrefixNode
BuildTree arranges prefixes into a containment forest: each node's children are the largest prefixes it directly contains. Duplicates are merged; roots and children are in address order.
type Range ¶
type Range struct {
// contains filtered or unexported fields
}
Range is an inclusive span of addresses of one family. The zero Range is invalid. Build one with NewRange, RangeOf or ParseRange.
func MergeRanges ¶
MergeRanges sorts, merges overlapping/adjacent ranges and drops invalid ones, returning a new canonical slice.
func NewRange ¶
NewRange validates and returns [from, to]. Inputs are normalized (IPv4-mapped unmapped, zones dropped).
func ParseRange ¶
ParseRange accepts "a-b", a CIDR, or a single address.
func (Range) ContainsRange ¶
ContainsRange reports whether o lies fully inside r.
func (Range) MarshalBinary ¶
MarshalBinary encodes as from||to raw bytes (8 or 32 bytes).
func (Range) MarshalText ¶
MarshalText implements encoding.TextMarshaler.
func (Range) Prefixes ¶
Prefixes returns the minimal list of CIDRs exactly covering r.
Example ¶
package main
import (
"fmt"
"github.com/bakhod1r/ipx"
)
func main() {
r, _ := ipx.ParseRange("10.0.0.5-10.0.0.12")
fmt.Println(r.Prefixes())
}
Output: [10.0.0.5/32 10.0.0.6/31 10.0.0.8/30 10.0.0.12/32]
func (Range) SplitRange ¶
SplitRange cuts r into chunks of at most n addresses.
func (*Range) UnmarshalBinary ¶
UnmarshalBinary decodes MarshalBinary output.
func (*Range) UnmarshalText ¶
UnmarshalText implements encoding.TextUnmarshaler.
type Rule ¶
type Rule struct {
Prefix netip.Prefix
Action Action
Priority int // lower is evaluated first
Group string // free-form label, e.g. "office", "cloud-metadata"
}
Rule is one ACL entry.
type SafeDialer ¶
type SafeDialer struct {
// Dialer performs the connection; zero value uses net.Dialer defaults.
Dialer net.Dialer
// Lookup resolves host names; nil uses net.DefaultResolver.
Lookup func(ctx context.Context, host string) ([]netip.Addr, error)
// Allow, when set, overrides the default policy: an address is permitted
// iff Allow allows it. When nil, IsInternal addresses are blocked.
Allow *ACL
}
SafeDialer dials only addresses that pass an SSRF check. It resolves the host itself, rejects the whole connection if ANY resolved address is blocked (so a mixed public/internal answer cannot win a race), and then connects to the checked IP, never re-resolving the name. That closes the DNS-rebinding window between check and connect.
Plug it into http.Transport.DialContext for outbound fetches of user-supplied URLs. Redirects are covered because every hop dials anew.
Example ¶
package main
import (
"fmt"
"net/http"
"github.com/bakhod1r/ipx"
)
func main() {
d := &ipx.SafeDialer{}
client := &http.Client{Transport: &http.Transport{DialContext: d.DialContext}}
_, err := client.Get("http://169.254.169.254/latest/meta-data/")
fmt.Println(err != nil)
}
Output: true
func (*SafeDialer) DialContext ¶
DialContext has the signature of net.Dialer.DialContext.
type SpecialBlock ¶
type SpecialBlock struct {
Prefix netip.Prefix
Name string
RFC string
Category Category
// Global mirrors the registry's "Globally Reachable" column.
Global bool
}
SpecialBlock is one entry of the IANA special-purpose registries.
type SyncTable ¶
type SyncTable[V any] struct { // contains filtered or unexported fields }
SyncTable is a Table guarded by a RWMutex: many concurrent readers, one writer. The zero value is ready to use.
type Table ¶
type Table[V any] struct { // contains filtered or unexported fields }
Table is a binary prefix trie mapping prefixes to values, with longest-prefix-match lookup (a routing table). The zero value is ready to use. Table is not safe for concurrent mutation; use SyncTable for that.
Prefixes are normalized (masked, IPv4-mapped unmapped) on insert and addresses on lookup.
func (*Table[V]) All ¶
All yields every stored prefix and value: IPv4 first, then IPv6, each in address order with shorter prefixes before their children.
func (*Table[V]) Insert ¶
Insert stores v at p, replacing any existing value. Invalid p is ignored and reported as false.
func (*Table[V]) Lookup ¶
Lookup returns the longest stored prefix containing a and its value.
Example ¶
package main
import (
"fmt"
"net/netip"
"github.com/bakhod1r/ipx"
)
func main() {
var rt ipx.Table[string]
rt.Insert(netip.MustParsePrefix("10.0.0.0/8"), "corp")
rt.Insert(netip.MustParsePrefix("10.10.10.0/24"), "lab")
p, v, _ := rt.Lookup(netip.MustParseAddr("10.10.10.50"))
fmt.Println(p, v)
}
Output: 10.10.10.0/24 lab
func (*Table[V]) LookupBatch ¶ added in v0.4.0
func (t *Table[V]) LookupBatch(as []netip.Addr) []LookupResult[V]
LookupBatch runs Lookup for each address, in order.
func (*Table[V]) LookupShortest ¶
LookupShortest returns the shortest stored prefix containing a.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
ipx
command
Command ipx is a CLI for the ipx library.
|
Command ipx is a CLI for the ipx library. |
|
Package httpip resolves the real client IP of an HTTP request behind explicitly trusted proxies, and provides net/http middleware.
|
Package httpip resolves the real client IP of an HTTP request behind explicitly trusted proxies, and provides net/http middleware. |
|
integrations
|
|
|
echoipx
module
|
|
|
fiberipx
module
|
|
|
geoip
module
|
|
|
ginipx
module
|
|
|
Package k8s provides Kubernetes cluster-network helpers built on ipx: overlap validation for pod/service/node CIDRs, node podCIDR carving and well-known service IPs.
|
Package k8s provides Kubernetes cluster-network helpers built on ipx: overlap validation for pod/service/node CIDRs, node podCIDR carving and well-known service IPs. |