mirror of
https://github.com/smcquay/cidr/
synced 2024-12-04 13:49:35 -08:00
Just copy code
This commit is contained in:
parent
d4e4adff48
commit
08019f5593
79
cidr.go
Normal file
79
cidr.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Package cidr is a collection of assorted utilities for computing
|
||||||
|
// network and host addresses within network ranges.
|
||||||
|
//
|
||||||
|
// It expects a CIDR-type address structure where addresses are divided into
|
||||||
|
// some number of prefix bits representing the network and then the remaining
|
||||||
|
// suffix bits represent the host.
|
||||||
|
//
|
||||||
|
// For example, it can help to calculate addresses for sub-networks of a
|
||||||
|
// parent network, or to calculate host addresses within a particular prefix.
|
||||||
|
//
|
||||||
|
// At present this package is prioritizing simplicity of implementation and
|
||||||
|
// de-prioritizing speed and memory usage. Thus caution is advised before
|
||||||
|
// using this package in performance-critical applications or hot codepaths.
|
||||||
|
// Patches to improve the speed and memory usage may be accepted as long as
|
||||||
|
// they do not result in a significant increase in code complexity.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddressRange returns the first and last addresses in the given CIDR range.
|
||||||
|
func AddressRange(network *net.IPNet) (net.IP, net.IP) {
|
||||||
|
// the first IP is easy
|
||||||
|
firstIP := network.IP
|
||||||
|
|
||||||
|
// the last IP is the network address OR NOT the mask address
|
||||||
|
prefixLen, bits := network.Mask.Size()
|
||||||
|
if prefixLen == bits {
|
||||||
|
// Easy!
|
||||||
|
// But make sure that our two slices are distinct, since they
|
||||||
|
// would be in all other cases.
|
||||||
|
lastIP := make([]byte, len(firstIP))
|
||||||
|
copy(lastIP, firstIP)
|
||||||
|
return firstIP, lastIP
|
||||||
|
}
|
||||||
|
|
||||||
|
firstIPInt, bits := ipToInt(firstIP)
|
||||||
|
hostLen := uint(bits) - uint(prefixLen)
|
||||||
|
lastIPInt := big.NewInt(1)
|
||||||
|
lastIPInt.Lsh(lastIPInt, hostLen)
|
||||||
|
lastIPInt.Sub(lastIPInt, big.NewInt(1))
|
||||||
|
lastIPInt.Or(lastIPInt, firstIPInt)
|
||||||
|
|
||||||
|
return firstIP, intToIP(lastIPInt, bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressCount returns the number of distinct host addresses within the given
|
||||||
|
// CIDR range.
|
||||||
|
//
|
||||||
|
// Since the result is a uint64, this function returns meaningful information
|
||||||
|
// only for IPv4 ranges and IPv6 ranges with a prefix size of at least 65.
|
||||||
|
func AddressCount(network *net.IPNet) uint64 {
|
||||||
|
prefixLen, bits := network.Mask.Size()
|
||||||
|
return 1 << (uint64(bits) - uint64(prefixLen))
|
||||||
|
}
|
||||||
|
|
||||||
|
//NoOverlap takes a list subnets and supernet (CIDRBlock) and verifies
|
||||||
|
//none of the subnets overlap and all subnets are in the supernet
|
||||||
|
//it returns an error if any of those conditions are not satisfied
|
||||||
|
func NoOverlap(subnets []*net.IPNet) error {
|
||||||
|
firstLastIP := make([][]net.IP, len(subnets))
|
||||||
|
for i, s := range subnets {
|
||||||
|
first, last := AddressRange(s)
|
||||||
|
firstLastIP[i] = []net.IP{first, last}
|
||||||
|
}
|
||||||
|
for i, s := range subnets {
|
||||||
|
for j := i + 1; j < len(subnets); j++ {
|
||||||
|
first := firstLastIP[j][0]
|
||||||
|
last := firstLastIP[j][1]
|
||||||
|
if s.Contains(first) || s.Contains(last) {
|
||||||
|
return fmt.Errorf("%s overlaps with %s", subnets[j].String(), s.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
186
cidr_test.go
Normal file
186
cidr_test.go
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddressRange(t *testing.T) {
|
||||||
|
type Case struct {
|
||||||
|
Range string
|
||||||
|
First string
|
||||||
|
Last string
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := []Case{
|
||||||
|
Case{
|
||||||
|
Range: "192.168.0.0/16",
|
||||||
|
First: "192.168.0.0",
|
||||||
|
Last: "192.168.255.255",
|
||||||
|
},
|
||||||
|
Case{
|
||||||
|
Range: "192.168.0.0/17",
|
||||||
|
First: "192.168.0.0",
|
||||||
|
Last: "192.168.127.255",
|
||||||
|
},
|
||||||
|
Case{
|
||||||
|
Range: "fe80::/64",
|
||||||
|
First: "fe80::",
|
||||||
|
Last: "fe80::ffff:ffff:ffff:ffff",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range cases {
|
||||||
|
_, network, _ := net.ParseCIDR(testCase.Range)
|
||||||
|
firstIP, lastIP := AddressRange(network)
|
||||||
|
desc := fmt.Sprintf("AddressRange(%#v)", testCase.Range)
|
||||||
|
gotFirstIP := firstIP.String()
|
||||||
|
gotLastIP := lastIP.String()
|
||||||
|
if gotFirstIP != testCase.First {
|
||||||
|
t.Errorf("%s first is %s; want %s", desc, gotFirstIP, testCase.First)
|
||||||
|
}
|
||||||
|
if gotLastIP != testCase.Last {
|
||||||
|
t.Errorf("%s last is %s; want %s", desc, gotLastIP, testCase.Last)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddressCount(t *testing.T) {
|
||||||
|
type Case struct {
|
||||||
|
Range string
|
||||||
|
Count uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := []Case{
|
||||||
|
Case{
|
||||||
|
Range: "192.168.0.0/16",
|
||||||
|
Count: 65536,
|
||||||
|
},
|
||||||
|
Case{
|
||||||
|
Range: "192.168.0.0/17",
|
||||||
|
Count: 32768,
|
||||||
|
},
|
||||||
|
Case{
|
||||||
|
Range: "192.168.0.0/32",
|
||||||
|
Count: 1,
|
||||||
|
},
|
||||||
|
Case{
|
||||||
|
Range: "192.168.0.0/31",
|
||||||
|
Count: 2,
|
||||||
|
},
|
||||||
|
Case{
|
||||||
|
Range: "0.0.0.0/0",
|
||||||
|
Count: 4294967296,
|
||||||
|
},
|
||||||
|
Case{
|
||||||
|
Range: "0.0.0.0/1",
|
||||||
|
Count: 2147483648,
|
||||||
|
},
|
||||||
|
Case{
|
||||||
|
Range: "::/65",
|
||||||
|
Count: 9223372036854775808,
|
||||||
|
},
|
||||||
|
Case{
|
||||||
|
Range: "::/128",
|
||||||
|
Count: 1,
|
||||||
|
},
|
||||||
|
Case{
|
||||||
|
Range: "::/127",
|
||||||
|
Count: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range cases {
|
||||||
|
_, network, _ := net.ParseCIDR(testCase.Range)
|
||||||
|
gotCount := AddressCount(network)
|
||||||
|
desc := fmt.Sprintf("AddressCount(%#v)", testCase.Range)
|
||||||
|
if gotCount != testCase.Count {
|
||||||
|
t.Errorf("%s = %d; want %d", desc, gotCount, testCase.Count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyNetowrk(t *testing.T) {
|
||||||
|
|
||||||
|
type testVerifyNetwork struct {
|
||||||
|
CIDRBlock string
|
||||||
|
CIDRList []string
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []*testVerifyNetwork{
|
||||||
|
&testVerifyNetwork{
|
||||||
|
CIDRList: []string{
|
||||||
|
"192.168.8.0/24",
|
||||||
|
"192.168.9.0/24",
|
||||||
|
"192.168.10.0/24",
|
||||||
|
"192.168.11.0/25",
|
||||||
|
"192.168.11.128/25",
|
||||||
|
"192.168.12.0/25",
|
||||||
|
"192.168.12.128/26",
|
||||||
|
"192.168.12.192/26",
|
||||||
|
"192.168.13.0/26",
|
||||||
|
"192.168.13.64/27",
|
||||||
|
"192.168.13.96/27",
|
||||||
|
"192.168.13.128/27",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
failCases := []*testVerifyNetwork{
|
||||||
|
&testVerifyNetwork{
|
||||||
|
CIDRList: []string{
|
||||||
|
"192.168.8.0/24",
|
||||||
|
"192.168.9.0/24",
|
||||||
|
"192.168.10.0/24",
|
||||||
|
"192.168.11.0/25",
|
||||||
|
"192.168.11.128/25",
|
||||||
|
"192.168.12.0/25",
|
||||||
|
"192.168.12.64/26",
|
||||||
|
"192.168.12.128/26",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&testVerifyNetwork{
|
||||||
|
CIDRList: []string{
|
||||||
|
"192.168.7.0/24",
|
||||||
|
"192.168.9.0/24",
|
||||||
|
"192.168.10.0/24",
|
||||||
|
"192.168.11.0/25",
|
||||||
|
"192.168.11.128/25",
|
||||||
|
"192.168.12.0/25",
|
||||||
|
"192.168.12.64/26",
|
||||||
|
"192.168.12.128/26",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
subnets := make([]*net.IPNet, len(tc.CIDRList))
|
||||||
|
for i, s := range tc.CIDRList {
|
||||||
|
_, n, err := net.ParseCIDR(s)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Bad test data %s\n", s)
|
||||||
|
}
|
||||||
|
subnets[i] = n
|
||||||
|
}
|
||||||
|
test := NoOverlap(subnets)
|
||||||
|
if test != nil {
|
||||||
|
t.Errorf("Failed test with %v\n", test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, tc := range failCases {
|
||||||
|
subnets := make([]*net.IPNet, len(tc.CIDRList))
|
||||||
|
for i, s := range tc.CIDRList {
|
||||||
|
_, n, err := net.ParseCIDR(s)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Bad test data %s\n", s)
|
||||||
|
}
|
||||||
|
subnets[i] = n
|
||||||
|
}
|
||||||
|
test := NoOverlap(subnets)
|
||||||
|
if test == nil {
|
||||||
|
t.Errorf("Test should have failed with CIDR %s\n", tc.CIDRBlock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
main.go
8
main.go
@ -4,8 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/apparentlymart/go-cidr/cidr"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = "sn <cidr>"
|
const usage = "sn <cidr>"
|
||||||
@ -24,11 +22,11 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
nets = append(nets, net)
|
nets = append(nets, net)
|
||||||
a, b := cidr.AddressRange(net)
|
a, b := AddressRange(net)
|
||||||
l := cidr.AddressCount(net)
|
l := AddressCount(net)
|
||||||
fmt.Printf("%-16s %-16s %10d\n", a, b, l)
|
fmt.Printf("%-16s %-16s %10d\n", a, b, l)
|
||||||
}
|
}
|
||||||
if err := cidr.NoOverlap(nets); err != nil {
|
if err := NoOverlap(nets); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "overlaping networks: %v\n", err)
|
fmt.Fprintf(os.Stderr, "overlaping networks: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
30
wrangling.go
Normal file
30
wrangling.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ipToInt(ip net.IP) (*big.Int, int) {
|
||||||
|
val := &big.Int{}
|
||||||
|
val.SetBytes([]byte(ip))
|
||||||
|
if len(ip) == net.IPv4len {
|
||||||
|
return val, 32
|
||||||
|
} else if len(ip) == net.IPv6len {
|
||||||
|
return val, 128
|
||||||
|
} else {
|
||||||
|
panic(fmt.Errorf("Unsupported address length %d", len(ip)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func intToIP(ipInt *big.Int, bits int) net.IP {
|
||||||
|
ipBytes := ipInt.Bytes()
|
||||||
|
ret := make([]byte, bits/8)
|
||||||
|
// Pack our IP bytes into the end of the return array,
|
||||||
|
// since big.Int.Bytes() removes front zero padding.
|
||||||
|
for i := 1; i <= len(ipBytes); i++ {
|
||||||
|
ret[len(ret)-i] = ipBytes[len(ipBytes)-i]
|
||||||
|
}
|
||||||
|
return net.IP(ret)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user