iprange

package module
v0.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 6, 2023 License: MIT Imports: 6 Imported by: 1

README

iprange

GoDoc

The package iprange parses IPv4/IPv6 addresses from strings in IP range format and handle interval mathematics between multiple IP ranges.

IP range formats

  • 172.18.0.1 / fd00::1
  • 172.18.0.0/24 / fd00::/64
  • 172.18.0.1-10 / fd00::1-a
  • 172.18.0.1-172.18.1.10 / fd00::1-fd00::1:a

Example

package main

import (
	"fmt"
	"log"

	"github.com/iiiceoo/iprange"
)

func main() {
	// Parse IP ranges.
	ranges, err := iprange.Parse(
		"172.18.0.1",
		"172.18.0.0/24",
		"172.18.0.1-10",
		"172.18.0.1-172.18.1.10",
	)
	if err != nil {
		log.Fatalf("failed to parse IP ranges: %v\n", err)
	}
	fmt.Printf("IP ranges: %s\n", ranges)

	// Merge IP ranges.
	merged := ranges.Merge()
	fmt.Printf("Merged IP ranges: %s\n", merged)

	// Interval mathematics of IP ranges.
	another, _ := iprange.Parse("172.18.0.0/24")
	diffSet := merged.Diff(another)
	fmt.Printf("Difference set between two IP ranges: %s\n", diffSet)

	// Iterate through IP ranges.
	fmt.Println("Scan the difference set:")
	iter := diffSet.Iterator()
	for {
		ip := iter.Next()
		if ip == nil {
			break
		}
		fmt.Println(ip)
	}
}

Documentation

Overview

Package iprange parses IPv4/IPv6 addresses from strings in IP range format and handle interval mathematics between multiple IP ranges.

The following IP range formats are supported:

172.18.0.1              fd00::1
172.18.0.0/24           fd00::/64
172.18.0.1-10           fd00::1-a
172.18.0.1-172.18.1.10  fd00::1-fd00::1:a

It takes a set of IP range strings, and returns a list of start-end IP address pairs, which can then be automatically extended and normalized, for instance:

ranges, err := iprange.Parse("172.18.0.1", "172.18.0.0/24")  // √
ranges, err = iprange.Parse("fd00::1", "fd00::/64")          // √
ranges, err = iprange.Parse("Invalid IP range string")       // ×
ranges, err = iprange.Parse("172.18.0.1", "fd00::/64")       // ×

When parsing an invalid IP range string, error errInvalidIPRangeFormat will be returned, and dual-stack IP ranges are not allowed because this approach is too complex and confusing. Use the following functions to assert the errors:

func IsInvalidIPRangeFormat(err error) bool
func IsDualStackIPRanges(err error) bool

Never never nerver use Go's append to combine two IPRanges (essentially, it is a Go's slice). Instead, its interval operation method should be used:

func (rr IPRanges) Union(rs IPRanges) IPRanges
func (rr IPRanges) Diff(rs IPRanges) IPRanges
func (rr IPRanges) Intersect(rs IPRanges) IPRanges

Similarly, do not attempt to calculate the intersection, union or difference of an IPv4 IPRanges and an IPv6 IPRanges:

res := v4Ranges.Diff(v6Ranges)  // Don't do this.

This behavior is not prohibited because the method literal does not return an error (for convenience), but the calculated result must be meaningless and incorrect.

The IPRanges can be converted into individual net.IP through its own iterator. Continuously call the method Next() to iterate through the IPRanges until nil is returned:

iter := ranges.Iterator()
for {
	ip := iter.Next()
	if ip == nil {
		break
	}
	// Do someting.
}

Finally, the inspiration for writing this package comes from

CNI plugins: https://github.com/containernetworking/plugins
malfunkt/iprange: https://github.com/malfunkt/iprange

both of which are great!

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsDualStackIPRanges

func IsDualStackIPRanges(err error) bool

IsDualStackIPRanges asserts whether the err is errDualStackIPRanges.

func IsInvalidIPRangeFormat

func IsInvalidIPRangeFormat(err error) bool

IsInvalidIPRangeFormat asserts whether the err is errInvalidIPRangeFormat.

Types

type IPRanges

type IPRanges []ipRange

IPRanges is a set of ipRange that uses the starting and ending IP addresses to represent any IP range of any size. The following IP range formats are valid:

172.18.0.1              fd00::1
172.18.0.0/24           fd00::/64
172.18.0.1-10           fd00::1-a
172.18.0.1-172.18.1.10  fd00::1-fd00::1:a

Never use Go's append to combine two IPRanges, but rather use its interval operation method Union, Diff or Intersect.

func Parse

func Parse(rs ...string) (IPRanges, error)

Parse parses a set of IP range format strings as IPRanges, the slice of ipRange, which records the starting and ending IP addresses.

The error errInvalidIPRangeFormat wiil be returned when one of IP range string is invalid. And dual-stack IP ranges are not allowed, the error errDualStackIPRanges occurs when parsing a set of IP range strings, where there are both IPv4 and IPv6 addresses.

Example
package main

import (
	"fmt"
	"log"

	"github.com/iiiceoo/iprange"
)

func main() {
	ranges, err := iprange.Parse("172.18.0.1", "172.18.0.0/24")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}
	fmt.Println(ranges)

	ranges, err = iprange.Parse("fd00::1-a", "fd00::1-fd00::1:a")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}
	fmt.Println(ranges)
}
Output:

[172.18.0.1 172.18.0.0-172.18.0.255]
[fd00::1-fd00::a fd00::1-fd00::1:a]

func (IPRanges) Contains

func (rr IPRanges) Contains(ip net.IP) bool

Contains reports whether IPRanges rr contain net.IP ip. If rr are IPv4 and ip is IPv6, then it is also considered not contained, and vice versa.

Example
package main

import (
	"fmt"
	"log"
	"net"

	"github.com/iiiceoo/iprange"
)

func main() {
	ranges, err := iprange.Parse("172.18.0.0/24")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}

	fmt.Println(ranges.Contains(net.ParseIP("172.18.0.1")))
	fmt.Println(ranges.Contains(net.ParseIP("172.19.0.1")))
	fmt.Println(ranges.Contains(net.ParseIP("fd00::1")))
}
Output:

true
false
false

func (IPRanges) Diff

func (rr IPRanges) Diff(rs IPRanges) IPRanges

Diff calculates the difference of IPRanges rr and rs. The result is always merged (ordered and deduplicated), for instance:

do:  [172.18.0.20-30, 172.18.0.1-25] - [172.18.0.5-25]
res: [172.18.0.1-4, 172.18.0.26-30]
Example
package main

import (
	"fmt"
	"log"

	"github.com/iiiceoo/iprange"
)

func main() {
	ranges1, err := iprange.Parse("172.18.0.20-30", "172.18.0.1-25")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}
	ranges2, err := iprange.Parse("172.18.0.5-25")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}

	fmt.Println(ranges1.Diff(ranges2))
}
Output:

[172.18.0.1-172.18.0.4 172.18.0.26-172.18.0.30]

func (IPRanges) Equal

func (rr1 IPRanges) Equal(rr2 IPRanges) bool

Equal reports whether IPRanges rr1 are equal to rr2.

Example
package main

import (
	"fmt"
	"log"

	"github.com/iiiceoo/iprange"
)

func main() {
	ranges1, err := iprange.Parse("172.18.0.0/24")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}
	ranges2, err := iprange.Parse("172.18.0.100-255", "172.18.0.0-200")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}

	fmt.Println(ranges1.Equal(ranges2))
	fmt.Println(ranges1.Equal(ranges1))
}
Output:

false
true

func (IPRanges) Intersect

func (rr IPRanges) Intersect(rs IPRanges) IPRanges

Intersect calculates the intersection of IPRanges rr and rs. The result is always merged (ordered and deduplicated), for instance:

do:  [172.18.0.20-30, 172.18.0.1-25] ∩ [172.18.0.5-25]
res: [172.18.0.5-25]
Example
package main

import (
	"fmt"
	"log"

	"github.com/iiiceoo/iprange"
)

func main() {
	ranges1, err := iprange.Parse("172.18.0.20-30", "172.18.0.1-25")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}
	ranges2, err := iprange.Parse("172.18.0.5-25")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}

	fmt.Println(ranges1.Intersect(ranges2))
}
Output:

[172.18.0.5-172.18.0.25]

func (IPRanges) Iterator

func (rr IPRanges) Iterator() *rangesIterator

Iterator generates a new iterator for IPRanges rr, which stores the merged rr (ordered and deduplicated) and always points the cursor to the first IP address of the entire IPRanges. Call Next to iterate through the IPRanges.

Example
package main

import (
	"fmt"
	"log"

	"github.com/iiiceoo/iprange"
)

func main() {
	ranges, err := iprange.Parse("172.18.0.1-3")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}

	iter := ranges.Iterator()
	for {
		ip := iter.Next()
		if ip == nil {
			break
		}
		fmt.Println(ip)
	}
}
Output:

172.18.0.1
172.18.0.2
172.18.0.3

func (IPRanges) Merge

func (rr IPRanges) Merge() IPRanges

Merge merges the duplicate parts of multiple ipRanges in rr and sort them by their respective starting xIP.

Example
package main

import (
	"fmt"
	"log"

	"github.com/iiiceoo/iprange"
)

func main() {
	ranges, err := iprange.Parse("172.18.1.1", "172.18.0.100-200", "172.18.0.1-150")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}

	fmt.Println(ranges)
	fmt.Println(ranges.Merge())
}
Output:

[172.18.1.1 172.18.0.100-172.18.0.200 172.18.0.1-172.18.0.150]
[172.18.0.1-172.18.0.200 172.18.1.1]

func (IPRanges) MergeEqual

func (rr1 IPRanges) MergeEqual(rr2 IPRanges) bool

MergeEqual reports whether IPRanges rr1 are equal to rr2, but both rr1 and rr2 are pre-merged, which means they are both ordered and deduplicated.

Example
package main

import (
	"fmt"
	"log"

	"github.com/iiiceoo/iprange"
)

func main() {
	ranges1, err := iprange.Parse("172.18.0.0/24")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}
	ranges2, err := iprange.Parse("172.18.0.100-255", "172.18.0.0-200")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}

	fmt.Println(ranges1.MergeEqual(ranges2))
}
Output:

true

func (IPRanges) Size added in v0.0.2

func (rr IPRanges) Size() *big.Int

Size calculates the total number of IP addresses that pertain to IPRanges rr.

Example
package main

import (
	"fmt"
	"log"

	"github.com/iiiceoo/iprange"
)

func main() {
	ranges, err := iprange.Parse("172.18.0.0/24")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}

	fmt.Println(ranges.Size())
	fmt.Println(iprange.IPRanges{}.Size())
}
Output:

256
0

func (IPRanges) Union

func (rr IPRanges) Union(rs IPRanges) IPRanges

Union calculates the union of IPRanges rr and rs. The result is always merged (ordered and deduplicated), for instance:

do:  [172.18.0.20-30, 172.18.0.1-25] U [172.18.0.5-25]
res: [172.18.0.1-30]
Example
package main

import (
	"fmt"
	"log"

	"github.com/iiiceoo/iprange"
)

func main() {
	ranges1, err := iprange.Parse("172.18.0.20-30", "172.18.0.1-25")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}
	ranges2, err := iprange.Parse("172.18.0.5-25")
	if err != nil {
		log.Fatalf("error parsing IP ranges: %v", err)
	}

	fmt.Println(ranges1.Union(ranges2))
}
Output:

[172.18.0.1-172.18.0.30]

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL