Skip to content

Commit

Permalink
https://github.com/yl2chen/cidranger/pull/43
Browse files Browse the repository at this point in the history
  • Loading branch information
monoidic committed Aug 28, 2022
1 parent b1b677f commit 17d6a3f
Show file tree
Hide file tree
Showing 11 changed files with 442 additions and 26 deletions.
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 netip.Prefix) ([]RangerEntry, erro
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 netip.Prefix) ([]RangerEntry, error) {
entries, err := b.getEntriesByVersion(network.Addr())
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 netip.Addr) (map[netip.Prefix]Range
}
return nil, ErrInvalidNetworkInput
}

// Just to complete interface
func (p *bruteRanger) Adjacent(network netip.Prefix) (RangerEntry, error) {
return nil, nil
}
28 changes: 28 additions & 0 deletions brute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,31 @@ 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 := netip.MustParsePrefix(insert)
err := ranger.Insert(NewBasicRangerEntry(network))
assert.NoError(t, err)
}
var expectedEntries []string
expectedEntries = append(expectedEntries, tc.networks...)
sort.Strings(expectedEntries)
snet := netip.MustParsePrefix(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)
})
}
}
33 changes: 17 additions & 16 deletions cidranger.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,37 @@ inclusion tests against it.
To create a new instance of the path-compressed trie:
ranger := NewPCTrieRanger()
ranger := NewPCTrieRanger()
To insert or remove an entry (any object that satisfies the RangerEntry
interface):
_, network, _ := net.ParseCIDR("192.168.0.0/24")
ranger.Insert(NewBasicRangerEntry(*network))
ranger.Remove(network)
_, network, _ := net.ParseCIDR("192.168.0.0/24")
ranger.Insert(NewBasicRangerEntry(*network))
ranger.Remove(network)
If you desire for any value to be attached to the entry, simply
create custom struct that satisfies the RangerEntry interface:
type RangerEntry interface {
Network() net.IPNet
}
type RangerEntry interface {
Network() net.IPNet
}
To test whether an IP is contained in the constructed networks ranger:
// returns bool, error
containsBool, err := ranger.Contains(net.ParseIP("192.168.0.1"))
// returns bool, error
containsBool, err := ranger.Contains(net.ParseIP("192.168.0.1"))
To get a list of CIDR blocks in constructed ranger that contains IP:
// returns []RangerEntry, error
entries, err := ranger.ContainingNetworks(net.ParseIP("192.168.0.1"))
// returns []RangerEntry, error
entries, err := ranger.ContainingNetworks(net.ParseIP("192.168.0.1"))
To get a list of all IPv4/IPv6 rangers respectively:
// returns []RangerEntry, error
entries, err := ranger.CoveredNetworks(*AllIPv4)
entries, err := ranger.CoveredNetworks(*AllIPv6)
// returns []RangerEntry, error
entries, err := ranger.CoveredNetworks(*AllIPv4)
entries, err := ranger.CoveredNetworks(*AllIPv6)
*/
package cidranger

Expand Down Expand Up @@ -78,7 +77,7 @@ func (b *basicRangerEntry) Network() netip.Prefix {
// itself.
func NewBasicRangerEntry(ipNet netip.Prefix) RangerEntry {
return &basicRangerEntry{
ipNet: ipNet, //.Masked(),
ipNet: ipNet.Masked(),
}
}

Expand All @@ -89,6 +88,8 @@ type Ranger interface {
Contains(ip netip.Addr) (bool, error)
ContainingNetworks(ip netip.Addr) ([]RangerEntry, error)
CoveredNetworks(network netip.Prefix) ([]RangerEntry, error)
CoveringNetworks(network netip.Prefix) ([]RangerEntry, error)
Adjacent(network netip.Prefix) (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"

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

/*
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 @@ -121,6 +129,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
8 changes: 6 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ module github.com/monoidic/cidranger

go 1.18

require github.com/stretchr/testify v1.6.1
require (
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.7.0
)

require (
github.com/davecgh/go-spew v1.1.0 // indirect
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-20220715151400-c0bba94af5f8 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
11 changes: 8 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
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.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
Expand Down
16 changes: 15 additions & 1 deletion net/ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,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 @@ -219,7 +233,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/netip"
"testing"
Expand Down Expand Up @@ -73,6 +74,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(netip.MustParseAddr("192.168.0.0")),
8,
// 192.168.1.0
NetworkNumber{0xc0_a8_01_00},
"Flip bit 8",
nil,
},
{
NewNetworkNumber(netip.MustParseAddr("128.0.0.1")),
31,
// 0.0.0.1
NetworkNumber{0x00_00_00_01},
"Flip bit 31",
nil,
},
{
NewNetworkNumber(netip.MustParseAddr("128.0.0.1")),
32,
// 0.0.0.1
NetworkNumber{0x00_00_00_01},
"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
Loading

0 comments on commit 17d6a3f

Please sign in to comment.