Skip to content

Commit

Permalink
chore: added examples and update readme file
Browse files Browse the repository at this point in the history
  • Loading branch information
Khalid-Nowaf committed Jul 2, 2024
1 parent 76b2c51 commit 63c9ace
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 58 deletions.
150 changes: 112 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,63 @@

# Supernet (NOT STABLE, UNDER DEVELOPMENT)

`supernet` is CIDR Store that can handle conflict resolution. It utilizes trie data structures to efficiently handle IPv4 and IPv6 addresses, ensuring fast conflict detection and resolution upon CIDR insertions. Designed to optimize memory usage, `supernet` can be used for different network management applications.
`Supernet` is a conflict-free CIDR Database (network store).

## Features
- **In-Place Conflict Resolution**: Automatically resolves conflicts during CIDR insertions, based on the CIDR priorities.
- **Generic Metadata**: Supports genetic metadata for CIDRs, making it easy to add custom data for CIDRs.
- **Small Memory Footprint**: designed to minimize memory usage while maintaining fast access and modification speeds.
- **IP Lookups**: Once Supernet loads all CIDRs, it's ready to lookup IP and return the associated CIDR and its Metadata.
- **Fixable CLI**: Shipped with simple and configurable CLI that can resolve conflicts in files (JSON, CSV and TSV).


- **Dynamic Conflict Resolution**: Automatically resolves conflicts during CIDR insertions, based on the CIDR priorities.
- **Generic Metadata**: Supports genetic metadata for CIDRs, making it adaptable for various applications.
- **Optimized Memory Footprint**: designed to minimize memory usage while maintaining fast access and modification speeds.
- **Fast Lookups and Conflict Detection**: Uses trie data structures to ensure rapid conflict resolution and CIDR lookups, crucial for high-performance network environments.

## Installation

To install `supernet`, ensure you have Go installed on your machine (version 1.13 or later is recommended). Install the package by executing:
To install `Supernet`, ensure you have Go installed on your machine (version 1.13 or later is recommended). Install the package by executing:

```sh
go get github.com/khalid_nowaf/supernet
go get github.com/khalid-nowaf/supernet
```

## Installation

To install `supernet`, you need to have Go installed on your machine (version 1.13 or later recommended). Install the package by running:

```sh
go get github.com/khalid_nowaf/supernet
go get github.com/khalid-nowaf/supernet
```

## Usage
Below are some examples of how you can use the supernet package to manage network CIDRs:

## CLI
```shell
go run cmd/supernet/main.go resolve

Resolve CIDR conflicts

Arguments:
<files> ... Input file containing CIDRs in CSV or JSON format

Flags:
-h, --help Show context-sensitive help.
--log Print the details about the inserted CIDR and the conflicts if any

--cidr-key="cidr" Key/Colum of the CIDRs in the file
--priority-keys=,... Keys/Columns to be used as CIDRs priorities
--fill-empty-priority Replace empty/null priority with zero value
--flip-rank-priority Make low value priority mean higher priority
--report Report only conflicted CIDRs
--output-format="csv" Output file format
--drop-keys=,... Keys/Columns to be dropped
--split-ip-versions Split the results in to separate files based on the CIDR IP version
```
### Initializing a Supernet
```go
package main

import (
"github.com/khalid_nowaf/supernet"
"github.com/khalid-nowaf/supernet"
)

func main() {
Expand All @@ -43,48 +66,99 @@ func main() {
}
```
### Inserting a CIDR
### Simple Inserting CIDRs and Lookup an IP
```go
package main

import (
"net"
"github.com/khalid_nowaf/supernet"
"fmt"
"net"

"github.com/khalid-nowaf/supernet/pkg/supernet"
)

func main() {
sn := supernet.NewSupernet()
_, ipnet, _ := net.ParseCIDR("192.168.100.14/24")
metadata := supernet.NewDefaultMetadata()
sn.InsertCidr(ipnet, metadata)
type Network struct {
cidr string
name string
priorities []uint8
}

```
### Searching for a CIDR
```go
import (
"fmt"
"github.com/khalid_nowaf/supernet"
)

func main() {
sn := supernet.NewSupernet()
result, err := sn.LookupIP("192.168.100.14")
if err != nil {
fmt.Println("Error:", err)
return
}
if result != nil {
fmt.Printf("Found CIDR: %s\n", result)
} else {
fmt.Println("No matching CIDR found.")
}

// create random Cidrs
networks := []*Network{
{cidr: "123.123.123.0/24", name: "maybe my home network", priorities: []uint8{0, 0, 1}},
{cidr: "123.123.123.0/23", name: "could be my home network", priorities: []uint8{0, 0, 2}},
{cidr: "123.123.123.0/30", name: "it is my home network", priorities: []uint8{0, 0, 3}},
}

super := supernet.NewSupernet()
for _, network := range networks {
if _, ipnet, err := net.ParseCIDR(network.cidr); err == nil {

metadata := supernet.NewMetadata(ipnet)
attributes is used to store any additional data about the network
metadata.Attributes["name"] = network.name
optional, it will be used to resolve conflict
if no priority add. the size of the network e.g /32, will be used as priority
so it is grunted smaller network will be not be over taken by larger network
metadata.Priority = network.priorities
result has information about the conflict and how it solve it
insertResult := super.InsertCidr(ipnet, metadata)
fmt.Println(insertResult.String()) see what happened
}
}
// get all networks (it will return conflict free networks)
nodes := super.AllCIDRS(false)

for _, node := range nodes {
fmt.Printf("CIDR: %s, name: %s\n", supernet.NodeToCidr(node), node.Metadata().Attributes["name"])
}

// if you want to to lookup a an IP
ipnet, node, _ := super.LookupIP("123.123.123.16")
if ipnet != nil {
fmt.Printf("found: CIDR: %s, name: %s\n", ipnet.String(), node.Metadata().Attributes["name"])
}

}
```
**Output**
```shell
## Insertion Results
Action Taken: Insert New CIDR, Added CIDRs: [123.123.123.0/24], Removed CIDRs: []

Detect Super CIDR conflict |New CIDR 123.123.122.0/23 conflicted with [123.123.123.0/24 ]
Action Taken: Remove Existing CIDR, Added CIDRs: [], Removed CIDRs: [123.123.123.0/24]
Action Taken: Insert New CIDR, Added CIDRs: [123.123.122.0/23], Removed CIDRs: []

Detect Sub CIDR conflict |New CIDR 123.123.123.0/30 conflicted with [123.123.122.0/23 ]
Action Taken: Insert New CIDR, Added CIDRs: [123.123.123.0/30], Removed CIDRs: []
Action Taken: Split Existing CIDR, Added CIDRs: [123.123.123.4/30 123.123.123.8/29 123.123.123.16/28 123.123.123.32/27 123.123.123.64/26 123.123.123.128/25 123.123.122.0/24], Removed CIDRs: []
Action Taken: Remove Existing CIDR, Added CIDRs: [], Removed CIDRs: [123.123.122.0/23]

## Internal CIDRs State
CIDR: 123.123.122.0/24, name: could be my home network
CIDR: 123.123.123.0/30, name: it is my home network
CIDR: 123.123.123.4/30, name: could be my home network
CIDR: 123.123.123.8/29, name: could be my home network
CIDR: 123.123.123.16/28, name: could be my home network
CIDR: 123.123.123.32/27, name: could be my home network
CIDR: 123.123.123.64/26, name: could be my home network
CIDR: 123.123.123.128/25, name: could be my home network

## IP Lookup
found: CIDR: 123.123.123.16/28, name: could be my home network
```
### Running Tests
To run tests for the supernet package, use the Go tool:
```sh
go test github.com/khalid_nowaf/supernet
go test github.com/khalid-nowaf/supernet
```
### Contributing
Expand Down
77 changes: 77 additions & 0 deletions examples/simple.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package main

import (
"fmt"
"net"

"github.com/khalid-nowaf/supernet/pkg/supernet"
)

type Network struct {
cidr string
name string
priorities []uint8
}

func main() {

// create random Cidrs
networks := []*Network{
{cidr: "123.123.123.0/24", name: "maybe my home network", priorities: []uint8{0, 0, 1}},
{cidr: "123.123.123.0/23", name: "could be my home network", priorities: []uint8{0, 0, 2}},
{cidr: "123.123.123.0/30", name: "it is my home network", priorities: []uint8{0, 0, 3}},
}

super := supernet.NewSupernet()
for _, network := range networks {
if _, ipnet, err := net.ParseCIDR(network.cidr); err == nil {
//
metadata := supernet.NewMetadata(ipnet)
// attributes is used to store any additional data about the network
metadata.Attributes["name"] = network.name
// optional, it will be used to resolve conflict
// if no priority add. the size of the network e.g /32, will be used as priority
// so it is grunted smaller network will be not be over taken by larger network
metadata.Priority = network.priorities
// result has information about the conflict and how it solve it
insertResult := super.InsertCidr(ipnet, metadata)
fmt.Println(insertResult.String()) // see what happened
}
}
// get all networks (it will return conflict free networks)
nodes := super.AllCIDRS(false)

for _, node := range nodes {
fmt.Printf("CIDR: %s, name: %s\n", supernet.NodeToCidr(node), node.Metadata().Attributes["name"])
}

// if you want to to lookup a an IP
ipnet, node, _ := super.LookupIP("123.123.123.16")
if ipnet != nil {
fmt.Printf("found: CIDR: %s, name: %s\n", ipnet.String(), node.Metadata().Attributes["name"])
}

}

// OUTPUT:
// Action Taken: Insert New CIDR, Added CIDRs: [123.123.123.0/24], Removed CIDRs: []

// Detect Super CIDR conflict |New CIDR 123.123.122.0/23 conflicted with [123.123.123.0/24 ]
// Action Taken: Remove Existing CIDR, Added CIDRs: [], Removed CIDRs: [123.123.123.0/24]
// Action Taken: Insert New CIDR, Added CIDRs: [123.123.122.0/23], Removed CIDRs: []

// Detect Sub CIDR conflict |New CIDR 123.123.123.0/30 conflicted with [123.123.122.0/23 ]
// Action Taken: Insert New CIDR, Added CIDRs: [123.123.123.0/30], Removed CIDRs: []
// Action Taken: Split Existing CIDR, Added CIDRs: [123.123.123.4/30 123.123.123.8/29 123.123.123.16/28 123.123.123.32/27 123.123.123.64/26 123.123.123.128/25 123.123.122.0/24], Removed CIDRs: []
// Action Taken: Remove Existing CIDR, Added CIDRs: [], Removed CIDRs: [123.123.122.0/23]

// CIDR: 123.123.122.0/24, name: could be my home network
// CIDR: 123.123.123.0/30, name: it is my home network
// CIDR: 123.123.123.4/30, name: could be my home network
// CIDR: 123.123.123.8/29, name: could be my home network
// CIDR: 123.123.123.16/28, name: could be my home network
// CIDR: 123.123.123.32/27, name: could be my home network
// CIDR: 123.123.123.64/26, name: could be my home network
// CIDR: 123.123.123.128/25, name: could be my home network

// found: CIDR: 123.123.123.16/28, name: could be my home network
9 changes: 5 additions & 4 deletions pkg/supernet/supernet.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func NewMetadata(ipnet *net.IPNet) *Metadata {
return &Metadata{
originCIDR: ipnet,
IsV6: isV6,
Attributes: map[string]string{},
}
}

Expand Down Expand Up @@ -80,7 +81,7 @@ func (super *Supernet) InsertCidr(ipnet *net.IPNet, metadata *Metadata) *Inserti
}

// LookupIP searches for the closest matching CIDR for a given IP address within the supernet.
func (super *Supernet) LookupIP(ip string) (*net.IPNet, error) {
func (super *Supernet) LookupIP(ip string) (*net.IPNet, *CidrTrie, error) {
// Determine if the IP is IPv4 or IPv6 based on the presence of a colon.
isV6 := strings.Contains(ip, ":")
mask := 32
Expand All @@ -94,7 +95,7 @@ func (super *Supernet) LookupIP(ip string) (*net.IPNet, error) {
// Parse the IP address with a full netmask to form a valid CIDR for bit conversion.
_, parsedIP, err := net.ParseCIDR(fmt.Sprintf("%s/%d", ip, mask))
if err != nil {
return nil, err
return nil, nil, err
}

ipBits, _ := CidrToBits(parsedIP)
Expand All @@ -103,9 +104,9 @@ func (super *Supernet) LookupIP(ip string) (*net.IPNet, error) {
for i, bit := range ipBits {
if supernet == nil {
// Return nil if no matching CIDR is found in the trie.
return nil, nil
return nil, nil, nil
} else if supernet.IsLeaf() {
return BitsToCidr(ipBits[:i], isV6), nil
return BitsToCidr(ipBits[:i], isV6), supernet, nil
} else {
supernet = supernet.Child(bit)
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/supernet/supernet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ func TestLookIPv4(t *testing.T) {
root.InsertCidr(sub, &Metadata{Priority: []uint8{1}, Attributes: makeCidrAtrr(sub.String())})
root.InsertCidr(super, &Metadata{Priority: []uint8{0}, Attributes: makeCidrAtrr(super.String())})

cidr, err := root.LookupIP("192.168.25.154")
cidr, _, err := root.LookupIP("192.168.25.154")

assert.NoError(t, err)
assert.NotNil(t, cidr)
Expand All @@ -290,19 +290,19 @@ func TestLookIPv6(t *testing.T) {
root.InsertCidr(sub, &Metadata{Priority: []uint8{1}, Attributes: makeCidrAtrr(sub.String())})
root.InsertCidr(super, &Metadata{Priority: []uint8{0}, Attributes: makeCidrAtrr(super.String())})

cidr, err := root.LookupIP("2001:0db8:abcd:12:1234::")
cidr, _, err := root.LookupIP("2001:0db8:abcd:12:1234::")

assert.NoError(t, err)
assert.NotNil(t, cidr)
assert.Equal(t, "2001:db8:abcd:12:1234::/80", cidr.String())

cidr, err = root.LookupIP("2001:db8:abcd:12:1234::abcd")
cidr, _, err = root.LookupIP("2001:db8:abcd:12:1234::abcd")

assert.NoError(t, err)
assert.NotNil(t, cidr)
assert.Equal(t, "2001:db8:abcd:12:1234::/80", cidr.String())

cidr, err = root.LookupIP("2001:db8:abcd:12:0000::1")
cidr, _, err = root.LookupIP("2001:db8:abcd:12:0000::1")

assert.NoError(t, err)
assert.NotNil(t, cidr)
Expand Down
12 changes: 0 additions & 12 deletions pkg/supernet/uitls.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,6 @@ func BitsToCidr(bits []int, ipV6 bool) *net.IPNet {

// NodeToCidr converts a given trie node into a CIDR (Classless Inter-Domain Routing) string representation.
// This function uses the node's path to generate the CIDR string.
//
// Parameters:
// - t: Pointer to a trie.BinaryTrie node of type Metadata. It must contain valid metadata and a path.
// - isV6: A boolean indicating whether the IP version is IPv6. True means IPv6, false means IPv4.
//
// Returns:
// - A string representing the CIDR notation of the node's IP address.
//
// Panics:
// - If the node's metadata is nil, indicating that it is a path node without associated CIDR data,
// this function will panic with a specific error message.
//
// Example:
//
// Given a trie node representing an IP address with metadata, this function will output the address in CIDR format,
Expand Down

0 comments on commit 63c9ace

Please sign in to comment.