Documentation
¶
Overview ¶
Package inet represents IP-addresses and IP-Blocks as comparable types.
The IP addresses and blocks from this package can be used as keys in maps, freely copied and fast sorted without prior conversion from/to IPv4/IPv6. A Tree implementation for lookups with longest-prefix-match is included.
Some missing utility functions in the standard library for IP-addresses and IP-blocks are provided.
This is a package for system programming, all fields are public for easy and fast serialization without special treatment. Anyway, you should not direct modify the fields and bytes, unless you know what you are doing.
IP addresses are represented as fixed arrays of 21 bytes, this ensures natural sorting (IPv4 < IPv6).
type IP [21]byte IP[0] = version information (4 or 6) IP[1:5] = IPv4 address, if version == 4, else zero IP[5:21] = IPv6 address, if version == 6, else zero
Blocks are IP-networks or IP-ranges, e.g.
192.168.0.1/24 // CIDR, network ::1/128 // CIDR, network 10.0.0.3-10.0.17.134 // range 2001:db8::1-2001:db8::f6 // range
A Block is represented as a struct of three IP addresses:
type Block struct {
Base IP
Last IP
Mask IP // may be zero for begin-end ranges
}
Tree is an implementation of a CIDR/Block prefix tree for fast IP lookup with longest-prefix-match. It is NOT a standard patricia-trie, this isn't possible for general blocks not represented by bitmasks.
type Tree struct {
// Contains the root node the tree.
Root *Node
}
Node, recursive tree data structure. Items abstracted via Itemer interface
type Node struct {
Item *Itemer
Parent *Node
Childs []*Node
}
Itemer interface for Tree items, maybe with payload and not just ip Blocks. See relation between Compare and Contains at inet.Block.Compare()
type Itemer interface {
// Contains, defines the depth in the tree, parent child relationship.
Contains(Itemer) bool
// Compare, defines equality and sort order on same tree level, siblings relationship.
Compare(Itemer) int
}
The tree can be visualized as:
▼ ├─ 10.0.0.0/9 │ ├─ 10.0.0.0/11 │ │ ├─ 10.0.0.0/20 │ │ ├─ 10.0.16.0/20 │ │ └─ 10.0.32.0/20 │ └─ 10.32.0.0/11 │ ├─ 10.32.8.0/22 │ ├─ 10.32.12.0/22 │ └─ 10.32.16.0/22 ├─ 2001:db8:900::/48 │ ├─ 2001:db8:900::/49 │ │ ├─ 2001:db8:900::/52
Index ¶
- Variables
- func SortBlock(bs []Block)
- func SortIP(ips []IP)
- type Block
- func (a Block) BitLen() int
- func (a Block) BlockToCIDRList() []Block
- func (a Block) Compare(b Itemer) int
- func (a Block) Contains(b Itemer) bool
- func (a Block) FindFreeCIDR(bs []Block) []Block
- func (a Block) IsCIDR() bool
- func (a Block) IsDisjunctWith(b Block) bool
- func (a Block) IsValid() bool
- func (a Block) MarshalText() ([]byte, error)
- func (a Block) OverlapsWith(b Block) bool
- func (a Block) Size() string
- func (a Block) SplitCIDR(n int) []Block
- func (a Block) String() string
- func (a *Block) UnmarshalText(text []byte) error
- func (a Block) Version() int
- type IP
- func (ip IP) AddBytes(bs []byte) IP
- func (ip IP) AddUint64(i uint64) IP
- func (ip IP) Bytes() []byte
- func (a IP) Compare(b IP) int
- func (ip IP) Expand() string
- func (ip IP) IsValid() bool
- func (ip IP) MarshalText() ([]byte, error)
- func (ip IP) Reverse() string
- func (ip IP) String() string
- func (ip IP) SubBytes(bs []byte) IP
- func (ip IP) SubUint64(i uint64) IP
- func (ip IP) ToNetIP() net.IP
- func (ip *IP) UnmarshalText(text []byte) error
- func (ip IP) Version() int
- type Itemer
- type Node
- type Tree
- type WalkFunc
Examples ¶
- Aggregate
- Block.BlockToCIDRList
- Block.Compare
- Block.FindFreeCIDR (V4)
- Block.FindFreeCIDR (V6)
- Block.MarshalText
- Block.Size
- Block.SplitCIDR (V4)
- Block.SplitCIDR (V6)
- Block.UnmarshalText
- Block.Version
- IP.AddBytes
- IP.AddUint64
- IP.Bytes
- IP.Compare
- IP.Expand
- IP.MarshalText
- IP.Reverse
- IP.String
- IP.SubUint64
- IP.ToNetIP
- IP.UnmarshalText
- IP.Version
- Itemer (Interface)
- ParseBlock
- ParseIP
- SortBlock
- SortIP
- Tree.Lookup
- Tree.Lookup (Exists)
- Tree.Walk
Constants ¶
This section is empty.
Variables ¶
var ( // IPZero is the zero-value for type IP. // // IP is represented as an array, so we have no nil as zero value. // IPZero can be used for that. IPZero = IP{} // BlockZero is the zero-value for type Block. // // Block is represented as a struct, so we have no nil as zero value. // BlockZero can be used for that. BlockZero = Block{Base: IP{}, Last: IP{}, Mask: IP{}} // MaxCIDRSplit limits the input parameter for SplitCIDR() to 20 (max 2^20 CIDRs) for security. MaxCIDRSplit int = 20 )
Functions ¶
func SortBlock ¶
func SortBlock(bs []Block)
SortBlock sorts the given slice of Blocks in place, see Compare() for sort order. IPv4 Blocks are sorted before IPv6 Blocks. Outer sets are sorted before their subsets!
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
var buf []inet.Block
for _, s := range []string{
"2001:db8:dead:beef::/44",
"10.0.0.0/9",
"::/0",
"10.96.0.2-10.96.1.17",
"0.0.0.0/0",
"::-::ffff",
"2001:db8::/32",
} {
buf = append(buf, inet.MustBlock(s))
}
inet.SortBlock(buf)
fmt.Printf("%v\n", buf)
}
Output: [0.0.0.0/0 10.0.0.0/9 10.96.0.2-10.96.1.17 ::/0 ::/112 2001:db8::/32 2001:db8:dea0::/44]
func SortIP ¶
func SortIP(ips []IP)
SortIP sorts the given slice in place. IPv4 addresses are sorted 'naturally' before IPv6 addresses, no prior conversion or version split necessary.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
var buf []inet.IP
for _, s := range []string{
"2001:db8::",
"127.0.0.1",
"::1",
} {
buf = append(buf, inet.MustIP(s))
}
inet.SortIP(buf)
fmt.Printf("%v\n", buf)
}
Output: [127.0.0.1 ::1 2001:db8::]
Types ¶
type Block ¶
Block is an IP-network or IP-range, e.g.
192.168.0.1/24 // network, with CIDR mask ::1/128 // network, with CIDR mask 10.0.0.3-10.0.17.134 // range 2001:db8::1-2001:db8::f6 // range
This Block representation is comparable and can be used as key in maps and fast sorted without conversions to/from the different IP versions.
func Aggregate ¶
Aggregate, returns the minimal number of CIDRs spanning the range of input blocks.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
var bs []inet.Block
for _, s := range []string{
"10.0.0.0/32",
"10.0.0.1/32",
"10.0.0.4/30",
"10.0.0.7/31",
"fe80::/12",
"fe80:0000:0000:0000:fe2d:5eff:fef0:fc64/128",
"fe80::/10",
} {
bs = append(bs, inet.MustBlock(s))
}
packed := inet.Aggregate(bs)
fmt.Printf("%v\n", packed)
}
Output: [10.0.0.0/31 10.0.0.4/30 fe80::/10]
func MustBlock ¶
func MustBlock(i interface{}) Block
MustBlock is a helper that calls ParseBlock and returns just inet.Block or panics on errr. It is intended for use in variable initializations.
func ParseBlock ¶ added in v0.10.0
ParseBlock parses and returns the input as type Block. The input type may be:
string net.IPNet *net.IPNet net.IP *net.IP inet.IP
Example for valid input strings:
"2001:db8:dead:/38" "10.0.0.0/8" "2001:db8::1-2001:db8::ff00:35" "192.168.2.3-192.168.7.255"
If a begin-end range can be represented as a CIDR, ParseBlock() generates the netmask and returns the range as CIDR.
IP addresses in the form of net.IP or inet.IP as input are converted to /32 or /128 blocks Returns error and BlockZero on invalid input.
Example ¶
package main
import (
"fmt"
"net"
"github.com/gaissmai/go-inet/inet"
)
func main() {
for _, anyOf := range []interface{}{
"fe80::1-fe80::2", // block from string
"10.0.0.0-11.255.255.255", // block from string, as range but true CIDR, see output
net.IP{192, 168, 0, 0}, // IP from net.IP
inet.MustIP("2001:db8:900::1"), // IP from inet.IP
} {
a, _ := inet.ParseBlock(anyOf)
fmt.Printf("block: %v\n", a)
}
}
Output: block: fe80::1-fe80::2 block: 10.0.0.0/7 block: 192.168.0.0/32 block: 2001:db8:900::1/128
func (Block) BitLen ¶ added in v0.8.0
BitLen returns the minimum number of bits to represent the block.
func (Block) BlockToCIDRList ¶
BlockToCIDRList returns a list of CIDRs spanning the range of a.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
for _, s := range []string{
"10.128.0.0-10.128.2.7",
"2001:b8::3-2001:b8::f",
} {
a := inet.MustBlock(s)
fmt.Printf("%v -> %v\n", a, a.BlockToCIDRList())
}
}
Output: 10.128.0.0-10.128.2.7 -> [10.128.0.0/23 10.128.2.0/29] 2001:b8::3-2001:b8::f -> [2001:b8::3/128 2001:b8::4/126 2001:b8::8/125]
func (Block) Compare ¶
Compare returns an integer comparing two IP Blocks, implements the Itemer interface for Tree items.
0 if a == b, -1 if a is v4 and b is v6 +1 if a is v6 and b is v4 -1 if a.Base < b.Base +1 if a.Base > b.Base -1 if a.Base == b.Base and a is SuperSet of b +1 if a.Base == b.Base and a is Subset of b
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
a := inet.MustBlock("127.0.0.0/8")
b := inet.MustBlock("127.0.0.0/8")
fmt.Printf("Block{%v}.Compare(Block{%v}) = %d\n", a, b, a.Compare(b))
a = inet.MustBlock("0.0.0.0/0")
b = inet.MustBlock("::/0")
fmt.Printf("Block{%v}.Compare(Block{%v}) = %d\n", a, b, a.Compare(b))
a = inet.MustBlock("127.128.0.0/9")
b = inet.MustBlock("127.0.0.0/8")
fmt.Printf("Block{%v}.Compare(Block{%v}) = %d\n", a, b, a.Compare(b))
a = inet.MustBlock("fe80::/10")
b = inet.MustBlock("fe80::/12")
fmt.Printf("Block{%v}.Compare(Block{%v}) = %d\n", a, b, a.Compare(b))
}
Output: Block{127.0.0.0/8}.Compare(Block{127.0.0.0/8}) = 0 Block{0.0.0.0/0}.Compare(Block{::/0}) = -1 Block{127.128.0.0/9}.Compare(Block{127.0.0.0/8}) = 1 Block{fe80::/10}.Compare(Block{fe80::/12}) = -1
func (Block) Contains ¶
Contains reports whether Block a contains Block b. a and b may NOT coincide. Implements the Itemer interface for Tree items.
a |------------| |------------| |------------| b |-----------------| |-----------------| |-----------------|
func (Block) FindFreeCIDR ¶
FindFreeCIDR returns all free CIDR blocks (of max possible bitlen) within given CIDR, minus the inner CIDR blocks. Panics if inner blocks are no subset of (or not equal to) outer block.
Example (V4) ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
outer := inet.MustBlock("192.168.2.0/24")
inner := make([]inet.Block, 0)
inner = append(inner, inet.MustBlock("192.168.2.0/26"))
inner = append(inner, inet.MustBlock("192.168.2.240-192.168.2.249"))
free := outer.FindFreeCIDR(inner)
fmt.Printf("%v - %v\nfree: %v\n", outer, inner, free)
}
Output: 192.168.2.0/24 - [192.168.2.0/26 192.168.2.240-192.168.2.249] free: [192.168.2.64/26 192.168.2.128/26 192.168.2.192/27 192.168.2.224/28 192.168.2.250/31 192.168.2.252/30]
Example (V6) ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
outer := inet.MustBlock("2001:db8:de00::/40")
inner := make([]inet.Block, 0)
inner = append(inner, inet.MustBlock("2001:db8:dea0::/44"))
free := outer.FindFreeCIDR(inner)
fmt.Printf("%v - %v\nfree: %v\n", outer, inner, free)
}
Output: 2001:db8:de00::/40 - [2001:db8:dea0::/44] free: [2001:db8:de00::/41 2001:db8:de80::/43 2001:db8:deb0::/44 2001:db8:dec0::/42]
func (Block) IsDisjunctWith ¶
IsDisjunctWith reports whether the Blocks a and b are disjunct
a |----------| b |---| a |------| b |---|
func (Block) MarshalText ¶
MarshalText implements the encoding.TextMarshaler interface. The encoding is the same as returned by String.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
for _, s := range []string{
"127.0.0.0/8",
"fe80::/10",
"10.0.0.0-10.1.0.0",
"",
} {
a, _ := inet.ParseBlock(s)
bs, _ := a.MarshalText()
fmt.Printf("%-20v %-20q %v\n", string(bs), string(bs), bs)
}
}
Output: 127.0.0.0/8 "127.0.0.0/8" [49 50 55 46 48 46 48 46 48 47 56] fe80::/10 "fe80::/10" [102 101 56 48 58 58 47 49 48] 10.0.0.0-10.1.0.0 "10.0.0.0-10.1.0.0" [49 48 46 48 46 48 46 48 45 49 48 46 49 46 48 46 48] "" []
func (Block) OverlapsWith ¶
OverlapsWith reports whether the Blocks overlaps.
a |-------| b |------| a |------| b |-------| a |----| b |---------| a |---------| b |----|
func (Block) Size ¶
Size returns the number of ip addresses as string. Returns a string, since the amount of ip addresses can be greater than uint64.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
for _, s := range []string{
"0.0.0.0/12",
"::/0",
"10.0.0.0-10.0.0.43",
"2001:db8::1-2001:db8::ffec",
} {
a := inet.MustBlock(s)
fmt.Printf("%-30v size min. %d bits\n", a, a.BitLen())
if a.BitLen() < 21 {
fmt.Printf("%-30v len %s IPs\n", a, a.Size())
}
}
}
Output: 0.0.0.0/12 size min. 20 bits 0.0.0.0/12 len 1048576 IPs ::/0 size min. 128 bits 10.0.0.0-10.0.0.43 size min. 6 bits 10.0.0.0-10.0.0.43 len 44 IPs 2001:db8::1-2001:db8::ffec size min. 16 bits 2001:db8::1-2001:db8::ffec len 65516 IPs
func (Block) SplitCIDR ¶
SplitCIDR returns the next 2^n CIDRs, splitted from outer block. The number of CIDRs is limited to MaxCIDRSplit, panics if more CIDRs are requested. Returns nil at max mask length or if block is no CIDR.
Example (V4) ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
a := inet.MustBlock("127.0.0.1/8")
splits := a.SplitCIDR(2)
fmt.Println(a, splits)
}
Output: 127.0.0.0/8 [127.0.0.0/10 127.64.0.0/10 127.128.0.0/10 127.192.0.0/10]
Example (V6) ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
a := inet.MustBlock("2001:db8:dea0::/44")
splits := a.SplitCIDR(1)
fmt.Println(a, splits)
}
Output: 2001:db8:dea0::/44 [2001:db8:dea0::/45 2001:db8:dea8::/45]
func (*Block) UnmarshalText ¶
UnmarshalText implements the encoding.TextUnmarshaler interface. The block is expected in a form accepted by ParseBlock.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
var a = new(inet.Block)
for _, s := range []string{
"127.0.000.255/8", // base gets truncated by CIDR mask, see output
"10.000.000.000-10.1.0.0", // leading zeros are normalized, see output
"", // empty input string aka []byte(nil) returns zero-value (BlockZero) on UnmarshalText()
"fe80::", // invalid
} {
err := a.UnmarshalText([]byte(s))
if err != nil {
fmt.Println("ERROR:", err)
continue
}
fmt.Printf("%q\n", a)
}
}
Output: "127.0.0.0/8" "10.0.0.0-10.1.0.0" "" ERROR: invalid Block
func (Block) Version ¶
Version returns the IP version, 4 or 6, panics on invalid block.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
for _, s := range []string{
"10.0.0.1/8",
"fe80::/10",
"::-::1",
} {
a, _ := inet.ParseBlock(s)
fmt.Println(a.Version())
}
}
Output: 4 6 6
type IP ¶
type IP [21]byte
IP represents a single IPv4 or IPv6 address in a fixed array of 21 bytes.
IP[0] = version information (4 or 6) IP[1:5] = IPv4 address, if version == 4, else zero IP[5:21] = IPv6 address, if version == 6, else zero
This IP representation is comparable and can be used as key in maps and fast sorted by bytes.Compare() without conversions to/from the different IP versions.
func MustIP ¶
func MustIP(i interface{}) IP
MustIP is a helper that calls ParseIP and returns just inet.IP or panics on errr. It is intended for use in variable initializations.
func ParseIP ¶ added in v0.10.0
ParseIP parses and returns the input as type IP. The input type may be:
string net.IP *net.IP []byte
The hard part is done by net.ParseIP(). Returns IPZero and error on invalid input.
Example ¶
package main
import (
"fmt"
"net"
"github.com/gaissmai/go-inet/inet"
)
func main() {
for _, anyOf := range []interface{}{
"2001:db8::", // string
net.IP{10, 0, 0, 1}, // net.IP
[]byte{127, 0, 0, 1}, // []byte, IPv4
&net.IP{0: 0x20, 1: 0x01, 2: 0x0d, 3: 0xb9, 15: 0x01}, // *net.IP, IPv6
[]byte{0: 0xff, 1: 0xfe, 15: 0x01}, // []byte, IPv6
} {
ip, _ := inet.ParseIP(anyOf)
fmt.Printf("ip: %v\n", ip)
}
}
Output: ip: 2001:db8:: ip: 10.0.0.1 ip: 127.0.0.1 ip: 2001:db9::1 ip: fffe::1
func (IP) AddBytes ¶
AddBytes adds byte slice to ip, panics on overflow.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
for _, tt := range []struct {
ip string
add []byte
}{
{"0.0.0.0", []byte{255, 255, 255, 255}},
{"::", []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
} {
ip := inet.MustIP(tt.ip)
z := ip.AddBytes(tt.add)
fmt.Printf("%v + %#x = %v\n", tt.ip, tt.add, z)
}
}
Output: 0.0.0.0 + 0xffffffff = 255.255.255.255 :: + 0xffffffffffffffffffffffffffffffff = ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
func (IP) AddUint64 ¶
AddUint64 adds i to ip, panics on overflow.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
for _, ip := range []inet.IP{
inet.MustIP("127.0.0.1"),
inet.MustIP("2001:db8::"),
} {
z := ip.AddUint64(1)
fmt.Printf("%-10v + %d = %v\n", ip, 1, z)
}
}
Output: 127.0.0.1 + 1 = 127.0.0.2 2001:db8:: + 1 = 2001:db8::1
func (IP) Bytes ¶
Bytes returns the ip address in byte representation. Returns 4 bytes for IPv4 and 16 bytes for IPv6. Panics on invalid input.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
for _, ip := range []inet.IP{
inet.MustIP("192.168.2.1"),
inet.MustIP("fffe:0db8::"),
} {
buf := ip.Bytes()
fmt.Printf("%#v\n", buf)
}
}
Output: []byte{0xc0, 0xa8, 0x2, 0x1} []byte{0xff, 0xfe, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
func (IP) Compare ¶
Compare returns an integer comparing two IP addresses lexicographically. The result will be:
0 if a == b -1 if a < b +1 if a > b
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
a := inet.MustIP("127.0.0.1")
b := inet.MustIP("10.10.0.1")
fmt.Printf("IP{%v}.Compare(IP{%v}) = %d\n", a, b, a.Compare(b))
a = inet.MustIP("0.0.0.0")
b = inet.MustIP("::")
fmt.Printf("IP{%v}.Compare(IP{%v}) = %d\n", a, b, a.Compare(b))
a = inet.MustIP("fe80::1")
b = inet.MustIP("fe80::1")
fmt.Printf("IP{%v}.Compare(IP{%v}) = %d\n", a, b, a.Compare(b))
}
Output: IP{127.0.0.1}.Compare(IP{10.10.0.1}) = 1 IP{0.0.0.0}.Compare(IP{::}) = -1 IP{fe80::1}.Compare(IP{fe80::1}) = 0
func (IP) Expand ¶
Expand IP address into canonical form, useful for grep, aligned output and lexical sort.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
for _, ip := range []inet.IP{
inet.MustIP("192.168.2.1"),
inet.MustIP("fffe:db8::"),
} {
fmt.Printf("%q\n", ip.Expand())
}
}
Output: "192.168.002.001" "fffe:0db8:0000:0000:0000:0000:0000:0000"
func (IP) MarshalText ¶
MarshalText implements the encoding.TextMarshaler interface. The encoding is the same as returned by String.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
for _, ip := range []inet.IP{
inet.MustIP("127.0.0.1"),
inet.MustIP("fe80::1"),
inet.IPZero,
} {
bs, err := ip.MarshalText()
if err != nil {
fmt.Printf("%.78s ...\n", err)
continue
}
fmt.Printf("%-15v %-15q %v\n", string(bs), string(bs), bs)
}
}
Output: 127.0.0.1 "127.0.0.1" [49 50 55 46 48 46 48 46 49] fe80::1 "fe80::1" [102 101 56 48 58 58 49] "" []
func (IP) Reverse ¶
Reverse IP address, needed for PTR entries in DNS zone files.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
for _, ip := range []inet.IP{
inet.MustIP("192.168.2.1"),
inet.MustIP("fffe:db8::"),
} {
fmt.Printf("%q\n", ip.Reverse())
}
}
Output: "1.2.168.192" "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.e.f.f.f"
func (IP) String ¶
String implements the fmt.Stringer interface. Returns "" on IPZero, panics otherwise on invalid input.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
for _, ip := range []inet.IP{
inet.MustIP("127.0.0.1"),
inet.MustIP("fe80::1"),
inet.IPZero,
// {5, 10, 0, 0, 0}, inet.String() panics on invalid input.
} {
fmt.Printf("%#v\n", ip.String())
}
}
Output: "127.0.0.1" "fe80::1" ""
func (IP) SubUint64 ¶
SubUint64 subtracts i from ip, panics on underflow.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
for _, ip := range []inet.IP{
inet.MustIP("127.0.0.0"),
inet.MustIP("2001:db8::"),
} {
z := ip.SubUint64(1)
fmt.Printf("%-10v - %#x = %v\n", ip, 1, z)
}
}
Output: 127.0.0.0 - 0x1 = 126.255.255.255 2001:db8:: - 0x1 = 2001:db7:ffff:ffff:ffff:ffff:ffff:ffff
func (IP) ToNetIP ¶
ToNetIP converts to net.IP. Panics on invalid input.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
for _, ip := range []inet.IP{
inet.MustIP("192.168.2.1"),
inet.MustIP("fffe:db8::"),
// {5}, Panics on invalid input.
} {
fmt.Printf("%#v\n", ip.ToNetIP())
}
}
Output: net.IP{0xc0, 0xa8, 0x2, 0x1} net.IP{0xff, 0xfe, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
func (*IP) UnmarshalText ¶
UnmarshalText implements the encoding.TextUnmarshaler interface. The IP address is expected in a form accepted by ParseIP(string).
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
var ip = new(inet.IP)
for _, s := range []string{
"127.0.0.1",
"fe80::1",
"", // empty input string aka []byte(nil) returns zero-value (IPZero) on UnmarshalText()
"ge80::1",
} {
err := ip.UnmarshalText([]byte(s))
if err != nil {
fmt.Println(err)
continue
}
fmt.Printf("%-15q %03d\n", ip, ip)
}
}
Output: "127.0.0.1" &[004 127 000 000 001 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000] "fe80::1" &[006 000 000 000 000 254 128 000 000 000 000 000 000 000 000 000 000 000 000 000 001] "" &[000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000] invalid IP
type Itemer ¶ added in v0.2.0
type Itemer interface {
// Contains, defines the depth in the tree, parent child relationship.
Contains(Itemer) bool
// Compare, defines equality and sort order on same tree level, siblings relationship.
Compare(Itemer) int
}
Itemer interface for tree items, maybe with payload and not just ip Blocks. See relation between Compare and Contains at inet.Block.Compare()
Example (Interface) ¶
package main
import (
"fmt"
"os"
"strings"
"github.com/gaissmai/go-inet/inet"
)
// record implements inet.Itemer
type record struct {
cidr inet.Block
txt string
}
// Contains, part of the Itemer interface
func (a record) Contains(b inet.Itemer) bool {
c, ok := b.(record)
if !ok {
panic(fmt.Errorf("incompatible types: %T != %T", a, b))
}
return a.cidr.Contains(c.cidr) ||
(a.cidr.Compare(c.cidr) == 0 && strings.Compare(a.txt, c.txt) < 0)
}
// Compare, part of the Itemer interface
func (a record) Compare(b inet.Itemer) int {
c, ok := b.(record)
if !ok {
panic(fmt.Errorf("incompatible types: %T != %T", a, b))
}
cmp := a.cidr.Compare(c.cidr)
if cmp != 0 {
return cmp
}
return strings.Compare(a.txt, c.txt)
}
// String implements the Stringer interface
func (a record) String() string {
cidr := a.cidr.String()
spaceDots := 38 - len(cidr)
spacer := strings.Repeat(".", spaceDots)
return fmt.Sprintf("%s %s %s", cidr, spacer, a.txt)
}
func main() {
tr := inet.NewTree()
for _, r := range []record{
record{inet.MustBlock("0.0.0.0/8"), "lorem"},
record{inet.MustBlock("1.0.0.0/8"), "lorem"},
record{inet.MustBlock("::/64"), "lorem"},
record{inet.MustBlock("5.0.0.0/8"), "lorem"},
record{inet.MustBlock("0.0.0.0/0"), "lorem"},
record{inet.MustBlock("10.0.0.0-10.0.0.17"), "lorem"},
record{inet.MustBlock("::/0"), "same cidr"},
record{inet.MustBlock("::/0"), "same cidr, different text"},
record{inet.MustBlock("2001:7c0:900:1c2::/64"), "lorem"},
record{inet.MustBlock("2001:7c0:900:1c2::0/127"), "lorem"},
record{inet.MustBlock("2001:7c0:900:1c2::1/128"), "lorem"},
record{inet.MustBlock("0.0.0.0/10"), "lorem"},
// ...
} {
tr.Insert(r)
}
tr.Fprint(os.Stdout)
/*
▼
├─ 0.0.0.0/0 ............................. lorem
│ ├─ 0.0.0.0/8 ............................. lorem
│ │ └─ 0.0.0.0/10 ............................ lorem
│ ├─ 1.0.0.0/8 ............................. lorem
│ ├─ 5.0.0.0/8 ............................. lorem
│ └─ 10.0.0.0-10.0.0.17 .................... lorem
└─ ::/0 .................................. same cidr
└─ ::/0 .................................. same cidr, different text
├─ ::/64 ................................. lorem
└─ 2001:7c0:900:1c2::/64 ................. lorem
└─ 2001:7c0:900:1c2::/127 ................ lorem
└─ 2001:7c0:900:1c2::1/128 ............... lorem
*/
}
Output:
type Node ¶ added in v0.2.0
Node, recursive data structure. Items are abstracted via Itemer interface
type Tree ¶ added in v0.2.0
type Tree struct {
// Contains the root node of a tree.
Root *Node
}
Tree is an implementation of a CIDR/Block tree for fast IP lookup with longest-prefix-match. It is NOT a standard patricia-trie, this isn't possible for general blocks not represented by bitmasks.
func NewTree ¶ added in v0.2.0
func NewTree() *Tree
NewTree allocates a new tree and returns the pointer.
func (*Tree) Fprint ¶ added in v0.2.0
Fprint prints the ordered tree in ASCII graph to io.Writer. The items should implement the Stringer interface for readable output.
▼ ├─ ::/8................. "Reserved by IETF [RFC3513][RFC4291]" ├─ 100::/8.............. "Reserved by IETF [RFC3513][RFC4291]" ├─ 200::/7.............. "Reserved by IETF [RFC4048]" ├─ 400::/6.............. "Reserved by IETF [RFC3513][RFC4291]" ├─ 800::/5.............. "Reserved by IETF [RFC3513][RFC4291]" ├─ 1000::/4............. "Reserved by IETF [RFC3513][RFC4291]" ├─ 2000::/3............. "Global Unicast [RFC3513][RFC4291]" │ ├─ 2000::/4............. "Test" │ └─ 3000::/4............. "FREE" ├─ 4000::/3............. "Reserved by IETF [RFC3513][RFC4291]" ├─ 6000::/3............. "Reserved by IETF [RFC3513][RFC4291]"
func (*Tree) Insert ¶ added in v0.2.0
Insert one item into the tree. The position within the tree is defined by the Contains() and Compare() methods, part of the Itemer interface .
If you insert many values you should sort them first.
func (*Tree) InsertBulk ¶ added in v0.6.0
func (t *Tree) InsertBulk(i interface{})
InsertBulk takes a slice of []items, sorts the values and inserts it into the tree. If the input arg is no slice of items implementing the Itemer interface, the method will panic.
It is a convenience method. The user can also sort the slice itself and insert the elements in a loop with Insert()
func (*Tree) Lookup ¶ added in v0.2.0
Lookup item for longest prefix match in the tree. If not found, returns input argument and false.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
bs := make([]inet.Block, 0)
for _, s := range []string{
"0.0.0.0/8",
"1.0.0.0/8",
"5.0.0.0/8",
"0.0.0.0/0",
"::/64",
"::/0",
"0.0.0.0/10",
} {
bs = append(bs, inet.MustBlock(s))
}
bt := inet.NewTree()
bt.InsertBulk(bs)
q := inet.MustBlock(inet.MustIP("5.0.122.12"))
if match, ok := bt.Lookup(q); ok {
fmt.Printf("inet.Lookup(%v): LPM found at: %v\n", q, match)
}
}
Output: inet.Lookup(5.0.122.12/32): LPM found at: 5.0.0.0/8
Example (Exists) ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
bs := make([]inet.Block, 0)
for _, s := range []string{
"0.0.0.0/8",
"1.0.0.0/8",
"5.0.0.0/8",
"0.0.0.0/0",
"::/64",
"::/0",
"0.0.0.0/10",
} {
bs = append(bs, inet.MustBlock(s))
}
bt := inet.NewTree()
bt.InsertBulk(bs)
// look for exists, exact match, not just LPM
for _, s := range []string{
"5.0.0.0/8",
"5.0.1.2/32",
} {
q := inet.MustBlock(s)
match, ok := bt.Lookup(q)
if ok && match.Compare(q) == 0 {
fmt.Printf("%q exists in tree\n", q)
} else {
fmt.Printf("%q doesn't exists in tree\n", q)
}
}
}
Output: "5.0.0.0/8" exists in tree "5.0.1.2/32" doesn't exists in tree
func (*Tree) Remove ¶ added in v0.2.0
Remove one item from tree, relink parent/child relation at the gap. Returns true on success, false if not found.
func (*Tree) Walk ¶ added in v0.2.0
Walk the Tree starting at root, calling walkFn for each node. At every node the walkFn is called with the node and the current depth as arguments. The walk stops if the walkFn returns an error not nil. The error is propagated by Walk() to the caller.
Example ¶
package main
import (
"fmt"
"github.com/gaissmai/go-inet/inet"
)
func main() {
bs := make([]inet.Block, 0)
for _, s := range []string{
"0.0.0.0/8",
"1.0.0.0/8",
"5.0.0.0/8",
"0.0.0.0/0",
"10.0.0.0-10.0.0.17",
"::/64",
"::/0",
"2001:db8:900:1c2::/64",
"2001:db8:900:1c2::0/127",
"2001:db8:900:1c2::1/128",
"0.0.0.0/10",
} {
bs = append(bs, inet.MustBlock(s))
}
tr := inet.NewTree()
tr.InsertBulk(bs)
var maxDepth int
var maxWidth int
var walkFn inet.WalkFunc = func(n *inet.Node, depth int) error {
if depth > maxDepth {
maxDepth = depth
}
if l := len(n.Childs); l > maxWidth {
maxWidth = l
}
return nil
}
_ = tr.Walk(walkFn)
fmt.Printf("max depth: %v\n", maxDepth)
fmt.Printf("max width: %v\n", maxWidth)
}
Output: max depth: 3 max width: 4