Skip to content

Commit

Permalink
IP to binary string conversion, subnet functions
Browse files Browse the repository at this point in the history
 - add a new function to convert IP addresses to dot-separated
   binary strings
 - first pass at subnet functions
 - tests and docs for everything
  • Loading branch information
Chad Robinson committed May 24, 2019
1 parent e810439 commit 0fb1627
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 34 deletions.
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ An enhancement of `net.IPNet` providing features such as:
- Get the network, broadcast, first and last usable addresses
- Increment or decrement an address within the boundaries of a netblock
- Enumerate all or part of a netblock to `[]net.IP`
- Allocate subnets
- Find free space between allocated subnets
- Expand subnets if space allows
- Allocate subnets and supernets

## Installing

Expand Down Expand Up @@ -81,6 +79,7 @@ func main() {
sort.Sort(iplib.ByIP(iplist)) // []net.IP{ipa, ipc, ipb}

fmt.Println(iplib.IP4ToUint32(ipa)) // 3232235777
fmt.Println(iplib.IPToBinaryString(ipa)) // 11000000.10101000.00000001.00000001
ipd := iplib.Uint32ToIP4(iplib.IP4ToUint32(ipa)+20) // ipd is 192.168.1.21
fmt.Println(iplib.IP4ToARPA(ipa)) // 1.1.168.192.in-addr.arpa
}
Expand Down Expand Up @@ -116,6 +115,7 @@ func main() {

fmt.Println(iplib.ExpandIP6(ipa)) // "2001:0db8:0000:0000:0000:0000:0000:0001"
fmt.Println(iplib.IPToBigint(ipa)) // 42540766411282592856903984951653826561
fmt.Println(iplib.IPToBinaryString(ipa)) // 00100000.00000001.00001101.10111000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000001

iplist := []net.IP{ ipb, ipc, ipa }
sort.Sort(iplib.ByIP(iplist)) // []net.IP{ipa, ipc, ipb}
Expand Down Expand Up @@ -188,4 +188,25 @@ func main() {
}
```

TODO: add subnetting functions
`iplib.IPNet` objects can be used to generate subnets and supernets:

```go
package main

import (
"fmt"

"github.com/c-robinson/iplib"
)

func main() {
_, ipna, _ := iplib.ParseCIDR("192.168.4.0/22")
fmt.Println(ipna.Subnet(24)) // []iplib.Net{ 192.168.4.0/24, 192.168.5.0/24,
// 192.168.6.0/24, 192.168.7.0/24 }
ipnb, err := ipna.Supernet(21) // 192.168.0.0/21

ipnc := ipna.PreviousNet(21) // 192.168.0.0/21

ipnd := ipna.NextNet(21) // 192.168.8.0/21
}
```
23 changes: 18 additions & 5 deletions iplib.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const (
var (
ErrAddressAtEndOfRange = errors.New("proposed operation would cause address to exit block")
ErrAddressOutOfRange = errors.New("the given IP address is not a part of this netblock")
ErrBadMaskLength = errors.New("illegal mask length provided")
ErrBroadcastAddress = errors.New("address is the broadcast address of this netblock (and not considered usable)")
ErrNetworkAddress = errors.New("address is the network address of this netblock (and not considered usable)")
ErrNoValidRange = errors.New("no netblock can be found between the supplied values")
Expand Down Expand Up @@ -323,6 +324,18 @@ func IncrementIP6By(ip net.IP, count *big.Int) net.IP {
return BigintToIP6(z)
}

// IPToBinaryString returns the given net.IP as a binary string
func IPToBinaryString(ip net.IP) string {
var sa []string
if len(ip) > 4 && EffectiveVersion(ip) == 4 {
ip = ForceIP4(ip)
}
for _, b := range ip {
sa = append(sa, fmt.Sprintf("%08b", b))
}
return strings.Join(sa, ".")
}

// IPToHexString returns the given net.IP as a hexadecimal string. This is the
// default stringer format for v6 net.IP
func IPToHexString(ip net.IP) string {
Expand Down Expand Up @@ -370,7 +383,7 @@ func IP6ToARPA(ip net.IP) string {
h = make([]byte, hex.EncodedLen(len(ip)))
hex.Encode(h, []byte(ip))

for i := len(h)-1; i >= 0 ; i-- {
for i := len(h) - 1; i >= 0; i-- {
s = s + string(h[i]) + "."
}
return s + domain
Expand All @@ -389,7 +402,7 @@ func IPToBigint(ip net.IP) *big.Int {
// benchmarks doing so, as well as iterating over the entire v4 address space.
func NextIP(ip net.IP) net.IP {
var ipn []byte
if Version(ip) == 4 {
if EffectiveVersion(ip) == 4 {
ipn = make([]byte, 4)
copy(ipn, ip)
} else {
Expand All @@ -406,13 +419,13 @@ func NextIP(ip net.IP) net.IP {
return ip // if we're already at the end of range, don't wrap
}

// PrevIP returns a net.IP decremented by one from the input address. This
// PreviousIP returns a net.IP decremented by one from the input address. This
// function is roughly as fast for v4 as DecrementIP4By(1) but is consistently
// 4x faster on v6 than DecrementIP6By(1). The bundled tests provide
// benchmarks doing so, as well as iterating over the entire v4 address space.
func PrevIP(ip net.IP) net.IP {
func PreviousIP(ip net.IP) net.IP {
var ipn []byte
if Version(ip) == 4 {
if EffectiveVersion(ip) == 4 {
ipn = make([]byte, 4)
copy(ipn, ip.To4())
} else {
Expand Down
24 changes: 24 additions & 0 deletions iplib_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,30 @@ func BenchmarkIncrementIPBy_v6(b *testing.B) {
}
}

func BenchmarkNet_Subnet_v4(b *testing.B) {
_, n, _ := ParseCIDR("192.168.0.0/24")
b.StartTimer()
for i := 0; i < b.N; i++ {
_, _ = n.Subnet(25)
}
}

func BenchmarkNet_Subnet_v6(b *testing.B) {
_, n, _ := ParseCIDR("2001:db8::/98")
b.StartTimer()
for i := 0; i < b.N; i++ {
_, _ = n.Subnet(99)
}
}

func BenchmarkNewNetBetween_v4(b *testing.B) {
ipa := net.IP{10, 0, 0, 0}
ipb := net.IP{10, 0, 0, 255}
for i := 0; i < b.N; i++ {
_, _, _ = NewNetBetween(ipa, ipb)
}
}

// Sorry for abusing the benchmark suite here, i just think it's kind of neat
// to see how quickly one can allocate the entire v4 space in a Go application
func BenchmarkNextIP_EntireV4Space(b *testing.B) {
Expand Down
Loading

0 comments on commit 0fb1627

Please sign in to comment.