Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions brute.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,24 @@ func (b *bruteRanger) CoveredNetworks(network net.IPNet) ([]RangerEntry, error)
return results, nil
}

// Covering returns the list of RangerEntry(s) the given ipnet
// is covered. It's like ContainingNetworks() for ipnet.
func (b *bruteRanger) CoveringNetworks(network net.IPNet) ([]RangerEntry, error) {
entries, err := b.getEntriesByVersion(network.IP)
if err != nil {
return nil, err
}
var results []RangerEntry
testNetwork := rnet.NewNetwork(network)
for _, entry := range entries {
entryNetwork := rnet.NewNetwork(entry.Network())
if entryNetwork.Covers(testNetwork) {
results = append(results, entry)
}
}
return results, nil
}

// Len returns number of networks in ranger.
func (b *bruteRanger) Len() int {
return len(b.ipV4Entries) + len(b.ipV6Entries)
Expand All @@ -122,3 +140,8 @@ func (b *bruteRanger) getEntriesByVersion(ip net.IP) (map[string]RangerEntry, er
}
return nil, ErrInvalidNetworkInput
}

// Just to complete interface
func (p *bruteRanger) Adjacent(network net.IPNet) (RangerEntry, error) {
return nil, nil
}
30 changes: 30 additions & 0 deletions brute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,33 @@ func TestCoveredNetworks(t *testing.T) {
})
}
}

func TestCoveringNetworks(t *testing.T) {
for _, tc := range coveringNetworkTests {
t.Run(tc.name, func(t *testing.T) {
ranger := newBruteRanger()
for _, insert := range tc.inserts {
_, network, _ := net.ParseCIDR(insert)
err := ranger.Insert(NewBasicRangerEntry(*network))
assert.NoError(t, err)
}
var expectedEntries []string
for _, network := range tc.networks {
expectedEntries = append(expectedEntries, network)
}
sort.Strings(expectedEntries)
_, snet, _ := net.ParseCIDR(tc.search)
networks, err := ranger.CoveringNetworks(*snet)
assert.NoError(t, err)

var results []string
for _, result := range networks {
net := result.Network()
results = append(results, net.String())
}
sort.Strings(results)

assert.Equal(t, expectedEntries, results)
})
}
}
2 changes: 2 additions & 0 deletions cidranger.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ type Ranger interface {
Contains(ip net.IP) (bool, error)
ContainingNetworks(ip net.IP) ([]RangerEntry, error)
CoveredNetworks(network net.IPNet) ([]RangerEntry, error)
CoveringNetworks(network net.IPNet) ([]RangerEntry, error)
Adjacent(network net.IPNet) (RangerEntry, error)
Len() int
}

Expand Down
36 changes: 35 additions & 1 deletion cidranger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"testing"
"time"

rnet "github.com/Ramzeth/cidranger/net"
"github.com/stretchr/testify/assert"
rnet "github.com/yl2chen/cidranger/net"
)

/*
Expand All @@ -30,6 +30,10 @@ func TestCoveredNetworksAgainstBaseIPv4(t *testing.T) {
testCoversNetworksAgainstBase(t, 100000, randomIPNetGenFactory(ipV4AWSRangesIPNets))
}

func TestCoveringNetworksAgainstBaseIPv4(t *testing.T) {
testCoveringNetworksAgainstBase(t, 100000, randomIPNetGenFactory(ipV4AWSRangesIPNets))
}

// IPv6 spans an extremely large address space (2^128), randomly generated IPs
// will often fall outside of the test ranges (AWS public CIDR blocks), so it
// it more meaningful for testing to run from a curated list of IPv6 IPs.
Expand All @@ -45,6 +49,10 @@ func TestCoveredNetworksAgainstBaseIPv6(t *testing.T) {
testCoversNetworksAgainstBase(t, 100000, randomIPNetGenFactory(ipV6AWSRangesIPNets))
}

func TestCoveringNetworksAgainstBaseIPv6(t *testing.T) {
testCoveringNetworksAgainstBase(t, 100000, randomIPNetGenFactory(ipV6AWSRangesIPNets))
}

func testContainsAgainstBase(t *testing.T, iterations int, ipGen ipGenerator) {
if testing.Short() {
t.Skip("Skipping memory test in `-short` mode")
Expand Down Expand Up @@ -120,6 +128,32 @@ func testCoversNetworksAgainstBase(t *testing.T, iterations int, netGen networkG
}
}

func testCoveringNetworksAgainstBase(t *testing.T, iterations int, netGen networkGenerator) {
if testing.Short() {
t.Skip("Skipping memory test in `-short` mode")
}
rangers := []Ranger{NewPCTrieRanger()}
baseRanger := newBruteRanger()
for _, ranger := range rangers {
configureRangerWithAWSRanges(t, ranger)
}
configureRangerWithAWSRanges(t, baseRanger)

for i := 0; i < iterations; i++ {
network := netGen()
expected, err := baseRanger.CoveringNetworks(network.IPNet)
assert.NoError(t, err)
for _, ranger := range rangers {
actual, err := ranger.CoveringNetworks(network.IPNet)
assert.NoError(t, err)
assert.Equal(t, len(expected), len(actual))
for _, network := range actual {
assert.Contains(t, expected, network)
}
}
}
}

/*
******************************************************************
Benchmarks.
Expand Down
14 changes: 11 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
module github.com/yl2chen/cidranger
module github.com/Ramzeth/cidranger

go 1.13
go 1.17

require (
github.com/stretchr/testify v1.6.1
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.4.0
github.com/yl2chen/cidranger v1.0.2
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect
gopkg.in/yaml.v2 v2.2.2 // indirect
)
14 changes: 9 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU=
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
18 changes: 17 additions & 1 deletion net/ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ func (n NetworkNumber) ToIP() net.IP {
}
if len(ip) == net.IPv4len {
ip = net.IPv4(ip[0], ip[1], ip[2], ip[3])
// Convert to 4-byte value, because parseCIDR returns 4-byte IPv4 address - https://github.com/golang/go/issues/41214
ip = ip.To4()
}
return ip
}
Expand Down Expand Up @@ -144,6 +146,20 @@ func (n NetworkNumber) Bit(position uint) (uint32, error) {
return (n[idx] >> rShift) & 1, nil
}

// FlipNthBit reverses the bit value at position. Position numbering is LSB 0.
func (n *NetworkNumber) FlipNthBit(position uint) error {
if int(position) > len(*n)*BitsPerUint32-1 {
return ErrInvalidBitPosition
}
idx := len(*n) - 1 - int(position/BitsPerUint32)
bitUintPosition := position % 32
XORMask := 1 << bitUintPosition
//byteNum := net.IPv6len - (position / 8) - 1
// getByteIndexOfBit(bitNum)
(*n)[idx] ^= uint32(XORMask)
return nil
}

// LeastCommonBitPosition returns the smallest position of the preceding common
// bits of the 2 network numbers, and returns an error ErrNoGreatestCommonBit
// if the two network number diverges from the first bit.
Expand Down Expand Up @@ -209,7 +225,7 @@ func (n Network) Contains(nn NetworkNumber) bool {
return true
}

// Contains returns true if Network covers o, false otherwise
// Covers returns true if Network covers o, false otherwise
func (n Network) Covers(o Network) bool {
if len(n.Number) != len(o.Number) {
return false
Expand Down
47 changes: 47 additions & 0 deletions net/ip_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net

import (
"errors"
"math"
"net"
"testing"
Expand Down Expand Up @@ -75,6 +76,52 @@ func TestNetworkNumberBit(t *testing.T) {
}
}

func TestNetworkNumber_FlipNthBit(t *testing.T) {
cases := []struct {
initial NetworkNumber
position uint
expected NetworkNumber
name string
expectedErr error
}{
{
NewNetworkNumber(net.ParseIP("192.168.0.0")),
8,
// 192.168.1.0
NetworkNumber{0b11000000101010000000000100000000},
"Flip bit 8",
nil,
},
{
NewNetworkNumber(net.ParseIP("128.0.0.1")),
31,
// 0.0.0.1
NetworkNumber{0b00000000000000000000000000000001},
"Flip bit 31",
nil,
},
{
NewNetworkNumber(net.ParseIP("128.0.0.1")),
32,
// 0.0.0.1
NetworkNumber{0b00000000000000000000000000000001},
"error in position",
errors.New("bit position not valid"),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
actual := tc.initial
err := actual.FlipNthBit(tc.position)
assert.Equal(t, tc.expectedErr, err)
if err == nil {
assert.Equal(t, tc.expected, actual)

}
})
}
}

func TestNetworkNumberBitError(t *testing.T) {
cases := []struct {
ip NetworkNumber
Expand Down
78 changes: 77 additions & 1 deletion trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"net"
"strings"

rnet "github.com/yl2chen/cidranger/net"
rnet "github.com/Ramzeth/cidranger/net"
)

// prefixTrie is a path-compressed (PC) trie implementation of the
Expand Down Expand Up @@ -124,6 +124,13 @@ func (p *prefixTrie) CoveredNetworks(network net.IPNet) ([]RangerEntry, error) {
return p.coveredNetworks(net)
}

// Covering returns the list of RangerEntry(s) the given ipnet
// is covered by. It's like ContainingNetworks() for ipnet.
func (p *prefixTrie) CoveringNetworks(network net.IPNet) ([]RangerEntry, error) {
net := rnet.NewNetwork(network)
return p.coveringNetworks(net)
Comment on lines +130 to +131
Copy link
Owner

@yl2chen yl2chen Oct 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! btw is new implementation needed, could we not just do the following?

Suggested change
net := rnet.NewNetwork(network)
return p.coveringNetworks(net)
return p.ContainingNetworks(network.IP.Mask(network.IPMask))

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about this method. But i've discover Covers function, and decided to use it.

}

// Len returns number of networks in ranger.
func (p *prefixTrie) Len() int {
return p.size
Expand All @@ -145,6 +152,43 @@ func (p *prefixTrie) String() string {
p.targetBitPosition(), p.hasEntry(), strings.Join(children, ""))
}

// Returns adjacent entries to givent entry, identified by given network. Returns nil if adjacent entry not exists.
// Adjacent networks are networks with only different lower bit in network address, e.g. 192.168.0.0/24 and 192.168.1.0/24
// That networks can be mergeg, e.g 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23
func (p *prefixTrie) Adjacent(network net.IPNet) (RangerEntry, error) {
adjacentNumber := rnet.NewNetworkNumber(network.IP)
ones, size := network.Mask.Size()
if ones == 0 {
// It's a full network, e.g. 0.0.0.0/0, there is no adjacents
return nil, nil
}
position := size - ones
err := adjacentNumber.FlipNthBit(uint(position))
if err != nil {
return nil, err
}
adjacentNet := rnet.NewNetwork(net.IPNet{adjacentNumber.ToIP(), network.Mask})
return p.adjacent(adjacentNet)
}

func (p *prefixTrie) adjacent(network rnet.Network) (RangerEntry, error) {
if p.hasEntry() && p.network.Equal(network) {
return p.entry, nil
}
if p.targetBitPosition() < 0 {
return nil, nil
}
bit, err := p.targetBitFromIP(network.Number)
if err != nil {
return nil, err
}
child := p.children[bit]
if child != nil {
return child.adjacent(network)
}
return nil, nil
}

func (p *prefixTrie) contains(number rnet.NetworkNumber) (bool, error) {
if !p.network.Contains(number) {
return false, nil
Expand Down Expand Up @@ -217,6 +261,38 @@ func (p *prefixTrie) coveredNetworks(network rnet.Network) ([]RangerEntry, error
return results, nil
}

func (p *prefixTrie) coveringNetworks(network rnet.Network) ([]RangerEntry, error) {
var results []RangerEntry
if !p.network.Covers(network) {
return results, nil
}
if p.hasEntry() {
results = []RangerEntry{p.entry}
}
if p.targetBitPosition() < 0 {
return results, nil
}
bit, err := p.targetBitFromIP(network.Number)
if err != nil {
return nil, err
}
child := p.children[bit]
if child != nil {
ranges, err := child.coveringNetworks(network)
if err != nil {
return nil, err
}
if len(ranges) > 0 {
if len(results) > 0 {
results = append(results, ranges...)
} else {
results = ranges
}
}
}
return results, nil
}

func (p *prefixTrie) insert(network rnet.Network, entry RangerEntry) (bool, error) {
if p.network.Equal(network) {
sizeIncreased := p.entry == nil
Expand Down
Loading