Skip to content
Snippets Groups Projects
Commit b0cee44e authored by Oliver Herms's avatar Oliver Herms
Browse files

Added bestpath() hooks and added tests

parent 21327b96
No related branches found
No related tags found
No related merge requests found
......@@ -23,11 +23,12 @@ type Route struct {
}
func NewRoute(pfx *net.Prefix, paths []*Path) *Route {
return &Route{
pfx: pfx,
activePaths: make([]*Path, 0),
paths: paths,
r := &Route{
pfx: pfx,
paths: paths,
}
return r
}
func (r *Route) Pfxlen() uint8 {
......@@ -95,7 +96,7 @@ func (r *Route) AddPaths(paths []*Path) {
}
func (r *Route) bestPaths() {
var best []*Path
best := []*Path{}
protocol := getBestProtocol(r.paths)
switch protocol {
......
......@@ -26,8 +26,7 @@ func TestNewRoute(t *testing.T) {
},
},
expected: &Route{
pfx: net.NewPfx(158798889, 24),
activePaths: make([]*Path, 0),
pfx: net.NewPfx(158798889, 24),
paths: []*Path{
{
Type: 2,
......
......@@ -4,11 +4,13 @@ import (
"github.com/bio-routing/bio-rd/net"
)
// RT represents a routing table
type RT struct {
root *node
nodes uint64
}
// node is a node in the compressed trie that is used to implement a routing table
type node struct {
skip uint8
dummy bool
......@@ -32,31 +34,32 @@ func newNode(route *Route, skip uint8, dummy bool) *node {
}
// LPM performs a longest prefix match for pfx on lpm
func (lpm *RT) LPM(pfx *net.Prefix) (res []*Route) {
if lpm.root == nil {
func (rt *RT) LPM(pfx *net.Prefix) (res []*Route) {
if rt.root == nil {
return nil
}
lpm.root.lpm(pfx, &res)
rt.root.lpm(pfx, &res)
return res
}
// RemovePath removes a path from the trie
func (lpm *RT) RemovePath(route *Route) {
lpm.root.removePath(route)
func (rt *RT) RemovePath(route *Route) {
rt.root.removePath(route)
}
func (lpm *RT) RemovePfx(pfx *net.Prefix) {
lpm.root.removePfx(pfx)
// RemovePfx removes a prefix from the rt including all it's paths
func (rt *RT) RemovePfx(pfx *net.Prefix) {
rt.root.removePfx(pfx)
}
// Get get's prefix pfx from the LPM
func (lpm *RT) Get(pfx *net.Prefix, moreSpecifics bool) (res []*Route) {
if lpm.root == nil {
func (rt *RT) Get(pfx *net.Prefix, moreSpecifics bool) (res []*Route) {
if rt.root == nil {
return nil
}
node := lpm.root.get(pfx)
node := rt.root.get(pfx)
if moreSpecifics {
return node.dumpPfxs(res)
}
......@@ -71,13 +74,14 @@ func (lpm *RT) Get(pfx *net.Prefix, moreSpecifics bool) (res []*Route) {
}
// Insert inserts a route into the LPM
func (lpm *RT) Insert(route *Route) {
if lpm.root == nil {
lpm.root = newNode(route, route.Pfxlen(), false)
func (rt *RT) Insert(route *Route) {
if rt.root == nil {
route.bestPaths()
rt.root = newNode(route, route.Pfxlen(), false)
return
}
lpm.root = lpm.root.insert(route)
rt.root = rt.root.insert(route)
}
func (n *node) removePath(route *Route) {
......@@ -204,6 +208,7 @@ func (n *node) insert(route *Route) *node {
// is pfx NOT a subnet of this node?
if !n.route.Prefix().Contains(route.Prefix()) {
route.bestPaths()
if route.Prefix().Contains(n.route.Prefix()) {
return n.insertBefore(route, n.route.Pfxlen()-n.skip-1)
}
......@@ -221,6 +226,7 @@ func (n *node) insert(route *Route) *node {
func (n *node) insertLow(route *Route, parentPfxLen uint8) *node {
if n.l == nil {
route.bestPaths()
n.l = newNode(route, route.Pfxlen()-parentPfxLen-1, false)
return n
}
......@@ -230,6 +236,7 @@ func (n *node) insertLow(route *Route, parentPfxLen uint8) *node {
func (n *node) insertHigh(route *Route, parentPfxLen uint8) *node {
if n.h == nil {
route.bestPaths()
n.h = newNode(route, route.Pfxlen()-parentPfxLen-1, false)
return n
}
......@@ -288,9 +295,10 @@ func (n *node) insertBefore(route *Route, parentPfxLen uint8) *node {
return new
}
func (lpm *RT) Dump() []*Route {
// Dump dumps all routes in table rt into a slice
func (rt *RT) Dump() []*Route {
res := make([]*Route, 0)
return lpm.root.dump(res)
return rt.root.dump(res)
}
func (n *node) dump(res []*Route) []*Route {
......
......@@ -52,18 +52,24 @@ func TestRemovePath(t *testing.T) {
}),
},
expected: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 9), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
{
pfx: net.NewPfx(strAddr("10.0.0.0"), 9),
paths: []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
},
}),
NewRoute(net.NewPfx(strAddr("10.128.0.0"), 9), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
{
pfx: net.NewPfx(strAddr("10.128.0.0"), 9),
paths: []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
},
}),
},
},
},
{
......@@ -107,14 +113,25 @@ func TestRemovePath(t *testing.T) {
}),
},
expected: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 2000,
{
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
paths: []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 2000,
},
},
},
}),
activePaths: []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 2000,
},
},
},
},
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 9), []*Path{
{
Type: BGPPathType,
......@@ -132,16 +149,16 @@ func TestRemovePath(t *testing.T) {
}
for _, test := range tests {
lpm := New()
rt := New()
for _, route := range test.routes {
lpm.Insert(route)
rt.Insert(route)
}
for _, route := range test.remove {
lpm.RemovePath(route)
rt.RemovePath(route)
}
res := lpm.Dump()
res := rt.Dump()
assert.Equal(t, test.expected, res)
}
}
......@@ -219,6 +236,371 @@ func TestRemovePfx(t *testing.T) {
}
}
func TestGetBitUint32(t *testing.T) {
tests := []struct {
name string
input uint32
offset uint8
expected bool
}{
{
name: "test 1",
input: 167772160, // 10.0.0.0
offset: 8,
expected: false,
},
{
name: "test 2",
input: 184549376, // 11.0.0.0
offset: 8,
expected: true,
},
}
for _, test := range tests {
b := getBitUint32(test.input, test.offset)
if b != test.expected {
t.Errorf("%s: Unexpected failure: Bit %d of %d is %v. Expected %v", test.name, test.offset, test.input, b, test.expected)
}
}
}
func TestLPM(t *testing.T) {
tests := []struct {
name string
routes []*Route
needle *net.Prefix
expected []*Route
}{
{
name: "LPM for non-existent route",
routes: []*Route{},
needle: net.NewPfx(strAddr("10.0.0.0"), 32),
expected: nil,
},
{
name: "Positive LPM test",
routes: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
},
needle: net.NewPfx(167772160, 32), // 10.0.0.0/32
expected: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
},
},
/*{
name: "Exact match",
routes: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
},
needle: net.NewPfx(strAddr("10.0.0.0"), 10),
expected: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
},
},*/
}
for _, test := range tests {
rt := New()
for _, route := range test.routes {
rt.Insert(route)
}
assert.Equal(t, test.expected, rt.LPM(test.needle))
}
}
func TestGet(t *testing.T) {
tests := []struct {
name string
moreSpecifics bool
routes []*Route
needle *net.Prefix
expected []*Route
}{
{
name: "Test 1: Search pfx and dump route + more specifics",
moreSpecifics: true,
routes: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
},
needle: net.NewPfx(strAddr("10.0.0.0"), 8),
expected: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
},
},
{
name: "Test 2: Search pfx and don't dump more specifics",
routes: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
},
needle: net.NewPfx(strAddr("10.0.0.0"), 8),
expected: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
},
},
{
name: "Test 3: Empty table",
routes: []*Route{},
needle: net.NewPfx(strAddr("10.0.0.0"), 32),
expected: nil,
},
{
name: "Test 4: Get Dummy",
routes: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
},
needle: net.NewPfx(strAddr("10.0.0.0"), 7),
expected: nil,
},
{
name: "Test 5",
routes: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
},
needle: net.NewPfx(strAddr("11.100.123.0"), 24),
expected: []*Route{
NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
},
},
{
name: "Test 4: Get nonexistent #1",
routes: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
},
needle: net.NewPfx(strAddr("10.0.0.0"), 10),
expected: nil,
},
{
name: "Test 4: Get nonexistent #2",
routes: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
},
needle: net.NewPfx(strAddr("10.0.0.0"), 10),
expected: nil,
},
}
for _, test := range tests {
rt := New()
for _, route := range test.routes {
rt.Insert(route)
}
p := rt.Get(test.needle, test.moreSpecifics)
if p == nil {
if test.expected != nil {
t.Errorf("Unexpected nil result for test %q", test.name)
}
continue
}
assert.Equal(t, test.expected, p)
}
}
func TestInsert(t *testing.T) {
tests := []struct {
name string
routes []*Route
expected *node
}{
{
name: "Insert first node",
routes: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
},
expected: &node{
route: &Route{
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
activePaths: []*Path{},
},
skip: 8,
},
},
{
name: "Insert duplicate node",
routes: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
},
expected: &node{
route: &Route{
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
activePaths: []*Path{},
},
skip: 8,
},
},
/*{
name: "Insert triangle",
prefixes: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0
net.NewPfx(167772160, 9), // 10.0.0.0
net.NewPfx(176160768, 9), // 10.128.0.0
},
expected: &node{
route: &Route{
pfx: net.NewPfx(167772160, 8), // 10.0.0.0/8
},
skip: 8,
l: &node{
route: &Route{
pfx: net.NewPfx(167772160, 9), // 10.0.0.0
},
},
h: &node{
route: &Route{
pfx: net.NewPfx(176160768, 9), // 10.128.0.0
},
},
},
},
{
name: "Insert disjunct prefixes",
prefixes: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0
net.NewPfx(191134464, 24), // 11.100.123.0/24
},
expected: &node{
route: &Route{
pfx: net.NewPfx(167772160, 7), // 10.0.0.0/7
},
skip: 7,
dummy: true,
l: &node{
route: &Route{
pfx: net.NewPfx(167772160, 8), // 10.0.0.0/8
},
},
h: &node{
route: &Route{
pfx: net.NewPfx(191134464, 24), // 10.0.0.0/8
},
skip: 16,
},
},
},
{
name: "Insert disjunct prefixes plus one child low",
prefixes: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0
net.NewPfx(191134464, 24), // 11.100.123.0/24
net.NewPfx(167772160, 12), // 10.0.0.0
net.NewPfx(167772160, 10), // 10.0.0.0
},
expected: &node{
route: &Route{
pfx: net.NewPfx(167772160, 7), // 10.0.0.0/7
},
skip: 7,
dummy: true,
l: &node{
route: &Route{
pfx: net.NewPfx(167772160, 8), // 10.0.0.0/8
},
l: &node{
skip: 1,
route: &Route{
pfx: net.NewPfx(167772160, 10), // 10.0.0.0/10
},
l: &node{
skip: 1,
route: &Route{
pfx: net.NewPfx(167772160, 12), // 10.0.0.0
},
},
},
},
h: &node{
route: &Route{
pfx: net.NewPfx(191134464, 24), // 10.0.0.0/8
},
skip: 16,
},
},
},
{
name: "Insert disjunct prefixes plus one child high",
prefixes: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0
net.NewPfx(191134464, 24), // 11.100.123.0/24
net.NewPfx(167772160, 12), // 10.0.0.0
net.NewPfx(167772160, 10), // 10.0.0.0
net.NewPfx(191134592, 25), // 11.100.123.128/25
},
expected: &node{
route: &Route{
pfx: net.NewPfx(167772160, 7), // 10.0.0.0/7
},
skip: 7,
dummy: true,
l: &node{
route: &Route{
pfx: net.NewPfx(167772160, 8), // 10.0.0.0/8
},
l: &node{
skip: 1,
route: &Route{
pfx: net.NewPfx(167772160, 10), // 10.0.0.0/10
},
l: &node{
skip: 1,
route: &Route{
pfx: net.NewPfx(167772160, 12), // 10.0.0.0
},
},
},
},
h: &node{
route: &Route{
pfx: net.NewPfx(191134464, 24), //11.100.123.0/24
},
skip: 16,
h: &node{
route: &Route{
pfx: net.NewPfx(191134592, 25), //11.100.123.128/25
},
},
},
},
},*/
}
for _, test := range tests {
rt := New()
for _, route := range test.routes {
rt.Insert(route)
}
assert.Equal(t, test.expected, rt.root)
}
}
func strAddr(s string) uint32 {
ret, _ := net.StrToAddr(s)
return ret
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment