Skip to content
forked from c-robinson/iplib

A library for working with IP addresses and networks in Go

License

Notifications You must be signed in to change notification settings

adamehirsch/iplib

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

IPLib

Documentation CircleCI Go Report Card Coverage Status

I really enjoy Python's ipaddress library and Ruby's ipaddr, I think you can write a lot of neat software if some of the little problems around manipulating IP addresses and netblocks are taken care of for you, so I set out to write something like them for my language of choice, Go. This is what I've come up with.

IPLib is a hopefully useful, aspirationally full-featured library built around and on top of the address primitives found in the net package, it seeks to make them more accessible and easier to manipulate.

It includes:

net.IP tools

Some simple tools for performing common tasks against IP objects:

  • Compare two addresses
  • Get the delta between two addresses
  • Sort
  • Decrement or increment addresses
  • Print v4 as a hexadecimal string
  • Print v6 in fully expanded form
  • Convert between net.IP, integer and hexadecimal
  • Get the version of a v4 address or force a IPv4-mapped IPv6address to be a v4 address
iplib.IPNet

An enhancement of net.IPNet providing features such as:

  • Retrieve the wildcard mask
  • 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

Installing

go get -u github.com/c-robinson/iplib

Using IPLib

There are a series of functions for working with v4 or v6 net.IP objects:

package main

import (
	"fmt"
	"net"
	"sort"
	
	"github.com/c-robinson/iplib"
)


func main() {
	ipa := net.ParseIP("192.168.1.1")
	ipb := iplib.IncrementIPBy(ipa, 15)      // ipb is 192.168.1.16
	ipc := iplib.NextIP(ipa)                 // ipc is 192.168.1.2

	fmt.Println(iplib.CompareIPs(ipa, ipb))  // -1
    
	fmt.Println(iplib.DeltaIP(ipa, ipb))     // 15
    
	fmt.Println(iplib.IPToHexString(ipc))    // "c0a80102"

	iplist := []net.IP{ ipb, ipc, ipa }
	sort.Sort(iplib.ByIP(iplist))            // []net.IP{ipa, ipc, ipb}

	fmt.Println(iplib.IP4ToUint32(ipa))      // 3232235777
	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
}

Addresses that require or return a count default to using uint32, which is sufficient for working with the entire IPv4 space. As a rule these functions are just lowest-common wrappers around IPv4- or IPv6-specific functions. The IPv6-specific variants use big.Int so they can access the entire v6 space:

package main

import (
	"fmt"
	"math/big"
	"net"
	"sort"
	
	"github.com/c-robinson/iplib"
)


func main() {
	ipa := net.ParseIP("2001:db8::1")
	ipb := iplib.IncrementIPBy(ipa, 15)      // ipb is 2001:db8::16
	ipc := iplib.NextIP(ipa)                 // ipc is 2001:db8::2

	fmt.Println(iplib.CompareIPs(ipa, ipb))  // -1
    
	fmt.Println(iplib.DeltaIP6(ipa, ipb))     // 15
    
	fmt.Println(iplib.ExpandIP6(ipa))        // "2001:0db8:0000:0000:0000:0000:0000:0001"
	fmt.Println(iplib.IPToBigint(ipa))       // 42540766411282592856903984951653826561 
    
	iplist := []net.IP{ ipb, ipc, ipa }
	sort.Sort(iplib.ByIP(iplist))            // []net.IP{ipa, ipc, ipb}

	m := big.NewInt(int64(iplib.MaxIPv4))    // e.g. 4,294,967,295
	ipd := iplib.IncrementIP6By(ipa, m)      // ipd is 2001:db8::1:0:0

	fmt.Println(iplib.DeltaIP6(ipb, ipd))    // 4294967274
	fmt.Println(iplib.IP6ToARPA(ipa))        // 1.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.1.0.0.2.ip6.arpa
}

To work with networks simply create an iplib.IPNet object:

package main

import (
	"fmt"
	"net"
	"sort"
	
	"github.com/c-robinson/iplib"
)

func main() {
	// this calls net.ParseCIDR() under the hood, but returns an iplib.Net object
	_, ipna, err := iplib.ParseCIDR("192.168.1.0/22")
	if err != nil {
		// this will be an error from the net package 
	}
	
	// NewNet() wants a net.IP and is waaaaaaaaaaaaaaaaay faster
	ipb := net.ParseIP("192.168.2.0")
	ipnb := iplib.NewNet(ipb, 22)
    
	// ...works for IPv6 too
	ipc := net.ParseIP("2001:db8::1")
	ipnc := iplib.NewNet(ipc, 64)

	fmt.Println(ipna.Count())                  // 1022 -- good enough for ipv4, but...
    
	fmt.Println(ipnc.Count())                  // 4294967295 -- ...sigh
	fmt.Println(ipnc.Count6())                 // 18446744073709551616 -- yay Count6() !

	fmt.Println(iplib.CompareNets(ipna, ipnb)) // -1

	ipnlist := []iplib.Net{ipnb, ipna, ipnc}
	sort.Sort(iplib.ByNet(ipnlist))            // []iplib.Net{ ipna, ipnb, ipnc } 
    
	elist := ipna.Enumerate(0, 0)
	fmt.Println(len(elist))                    // 1022
    
	fmt.Println(ipna.ContainsNet(ipnb))        // true
    
	fmt.Println(ipna.NetworkAddress())         // 192.168.1.0
	fmt.Println(ipna.FirstAddress())           // 192.168.1.1
	fmt.Println(ipna.LastAddress())            // 192.168.3.254
	fmt.Println(ipna.BroadcastAddress())       // 192.168.3.255
    
	fmt.Println(ipnc.NetworkAddress())         // 2001:db8::1 -- meaningless in IPv6
	fmt.Println(ipnc.FirstAddress())           // 2001:db8::1
	fmt.Println(ipnc.LastAddress())            // 2001:db8::ffff:ffff:ffff:ffff
	fmt.Println(ipnc.BroadcastAddress())       // 2001:db8::ffff:ffff:ffff:ffff
    
	ipa1 := net.ParseIP("2001:db8::2")
	ipa1, err = ipna.PreviousIP(ipa1)         //  net.IP{2001:db8::1}, nil
	ipa1, err = ipna.PreviousIP(ipa1)         //  net.IP{}, ErrAddressAtEndOfRange
}

TODO: add subnetting functions

About

A library for working with IP addresses and networks in Go

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Go 100.0%