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

Adding routing table

parent 1219d029
No related branches found
No related tags found
No related merge requests found
rt/bgp.go 0 → 100644
package rt
import (
"sync"
log "github.com/sirupsen/logrus"
)
type BGPPath struct {
PathIdentifier uint32
NextHop uint32
LocalPref uint32
ASPath string
ASPathLen uint16
Origin uint8
MED uint32
EBGP bool
Source uint32
}
type BGPPathManager struct {
paths map[BGPPath]*BGPPathCounter
mu sync.Mutex
}
type BGPPathCounter struct {
usageCount uint64
path *BGPPath
}
func NewBGPPathManager() *BGPPathManager {
m := &BGPPathManager{}
return m
}
func (m *BGPPathManager) pathExists(p BGPPath) bool {
if _, ok := m.paths[p]; !ok {
return false
}
return true
}
func (m *BGPPathManager) AddPath(p BGPPath) *BGPPath {
m.mu.Lock()
defer m.mu.Unlock()
if !m.pathExists(p) {
m.paths[p] = &BGPPathCounter{
path: &p,
}
}
m.paths[p].usageCount++
return m.paths[p].path
}
func (m *BGPPathManager) RemovePath(p BGPPath) {
m.mu.Lock()
defer m.mu.Unlock()
if !m.pathExists(p) {
log.Fatalf("Tried to remove non-existent BGPPath: %v", p)
return
}
m.paths[p].usageCount--
if m.paths[p].usageCount == 0 {
delete(m.paths, p)
}
}
func (r *Route) bgpPathSelection() (res []*Path) {
// TODO: Implement next hop lookup and compare IGP metrics
if len(r.paths) == 1 {
copy(res, r.paths)
return res
}
for _, p := range r.paths {
if p.Type != BGPPathType {
continue
}
if len(res) == 0 {
res = append(res, p)
continue
}
if res[0].BGPPath.ecmp(p.BGPPath) {
res = append(res, p)
continue
}
if !res[0].BGPPath.better(p.BGPPath) {
continue
}
res = []*Path{p}
}
return res
}
func (b *BGPPath) better(c *BGPPath) bool {
if c.LocalPref < b.LocalPref {
return false
}
if c.LocalPref > b.LocalPref {
return true
}
if c.ASPathLen > b.ASPathLen {
return false
}
if c.ASPathLen < b.ASPathLen {
return true
}
if c.Origin > b.Origin {
return false
}
if c.Origin < b.Origin {
return true
}
if c.MED > b.MED {
return false
}
if c.MED < b.MED {
return true
}
return false
}
func (b *BGPPath) ecmp(c *BGPPath) bool {
return b.LocalPref == c.LocalPref && b.ASPathLen == c.ASPathLen && b.Origin == c.Origin && b.MED == c.MED
}
package rt
import (
"testing"
"github.com/taktv6/tbgp/net"
"github.com/stretchr/testify/assert"
)
func TestNew(t *testing.T) {
l := New()
if l == nil {
t.Errorf("New() returned nil")
}
}
func TestRemove(t *testing.T) {
tests := []struct {
name string
routes []*Route
remove []*net.Prefix
expected []*Route
}{
{
name: "Test 1",
routes: []*Route{
NewRoute(net.NewPfx(167772160, 8)), // 10.0.0.0
NewRoute(net.NewPfx(167772160, 9)), // 10.0.0.0
NewRoute(net.NewPfx(176160768, 9)), // 10.128.0.0
},
remove: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0
},
expected: []*Route{
NewRoute(net.NewPfx(167772160, 9)), // 10.0.0.0
NewRoute(net.NewPfx(176160768, 9)), // 10.128.0.0
},
},
{
name: "Test 2",
routes: []*Route{
NewRoute(net.NewPfx(167772160, 8)), // 10.0.0.0/8
NewRoute(net.NewPfx(191134464, 24)), // 11.100.123.0/24
NewRoute(net.NewPfx(167772160, 12)), // 10.0.0.0/12
NewRoute(net.NewPfx(167772160, 10)), // 10.0.0.0/10
},
remove: []*net.Prefix{
net.NewPfx(167772160, 7), // 10.0.0.0/7
},
expected: []*Route{
NewRoute(net.NewPfx(167772160, 8)), // 10.0.0.0/8
NewRoute(net.NewPfx(167772160, 10)), // 10.0.0.0/10
NewRoute(net.NewPfx(167772160, 12)), // 10.0.0.0/12
NewRoute(net.NewPfx(191134464, 24)), // 11.100.123.0/24
},
},
{
name: "Test 3",
remove: []*net.Prefix{
NewRoute(net.NewPfx(167772160, 7)), // 10.0.0.0/7
},
expected: []*Route{},
},
{
name: "Test 4",
prefixes: []*net.Prefix{
NewRoute(net.NewPfx(167772160, 8)), // 10.0.0.0
NewRoute(net.NewPfx(191134464, 24)), // 11.100.123.0/24
NewRoute(net.NewPfx(167772160, 12)), // 10.0.0.0
NewRoute(net.NewPfx(167772160, 10)), // 10.0.0.0
NewRoute(net.NewPfx(191134592, 25)), // 11.100.123.128/25
},
remove: []*net.Prefix{
NewRoute(net.NewPfx(191134464, 24)), // 11.100.123.0/24
},
expected: []*Route{
NewRoute(net.NewPfx(167772160, 8)), // 10.0.0.0
NewRoute(net.NewPfx(167772160, 10)), // 10.0.0.0
NewRoute(net.NewPfx(167772160, 12)), // 10.0.0.0
NewRoute(net.NewPfx(191134592, 25)), // 11.100.123.128/25
},
},
{
name: "Test 5",
prefixes: []*net.Prefix{
NewRoute(net.NewPfx(167772160, 8)), // 10.0.0.0
NewRoute(net.NewPfx(191134464, 24)), // 11.100.123.0/24
NewRoute(net.NewPfx(167772160, 12)), // 10.0.0.0
NewRoute(net.NewPfx(167772160, 10)), // 10.0.0.0
NewRoute(net.NewPfx(191134592, 25)), // 11.100.123.128/25
},
remove: []*net.Prefix{
NewRoute(net.NewPfx(167772160, 12)), // 10.0.0.0/12
},
expected: []*Route{
NewRoute(net.NewPfx(167772160, 8)), // 10.0.0.0
NewRoute(net.NewPfx(167772160, 10)), // 10.0.0.0
NewRoute(net.NewPfx(191134464, 24)), // 11.100.123.0/24
NewRoute(net.NewPfx(191134592, 25)), // 11.100.123.128/25
},
},
}
for _, test := range tests {
lpm := New()
for _, route := range test.routes {
lpm.Insert(route)
}
for _, pfx := range test.remove {
lpm.Remove(pfx)
}
res := lpm.Dump()
assert.Equal(t, test.expected, res)
}
}
func TestInsert(t *testing.T) {
tests := []struct {
name string
prefixes []*net.Prefix
expected *node
}{
{
name: "Insert first node",
prefixes: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0/8
},
expected: &node{
route: &Route{
pfx: net.NewPfx(167772160, 8)}, // 10.0.0.0/8
skip: 8,
},
},
{
name: "Insert double node",
prefixes: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0/8
net.NewPfx(167772160, 8), // 10.0.0.0/8
net.NewPfx(167772160, 8), // 10.0.0.0/8
net.NewPfx(167772160, 8), // 10.0.0.0/8
},
expected: &node{
route: &Route{
pfx: net.NewPfx(167772160, 8), // 10.0.0.0/8
},
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 {
l := New()
for _, pfx := range test.prefixes {
l.Insert(&Route{pfx: pfx})
}
assert.Equal(t, test.expected, l.root)
}
}
func TestLPM(t *testing.T) {
tests := []struct {
name string
prefixes []*net.Prefix
needle *net.Prefix
expected []*net.Prefix
}{
{
name: "Test 1",
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
},
needle: net.NewPfx(167772160, 32), // 10.0.0.0/32
expected: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0
net.NewPfx(167772160, 10), // 10.0.0.0
net.NewPfx(167772160, 12), // 10.0.0.0
},
},
{
name: "Test 2",
prefixes: []*net.Prefix{},
needle: net.NewPfx(167772160, 32), // 10.0.0.0/32
expected: nil,
},
{
name: "Test 3",
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
},
needle: net.NewPfx(167772160, 10), // 10.0.0.0/10
expected: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0
net.NewPfx(167772160, 10), // 10.0.0.0
},
},
}
for _, test := range tests {
lpm := New()
for _, pfx := range test.prefixes {
lpm.Insert(&Route{pfx: pfx})
}
assert.Equal(t, test.expected, lpm.LPM(test.needle))
}
}
func TestGet(t *testing.T) {
tests := []struct {
name string
moreSpecifics bool
prefixes []*net.Prefix
needle *net.Prefix
expected []*net.Prefix
}{
{
name: "Test 1: Search pfx and dump more specifics",
moreSpecifics: true,
prefixes: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0/8
net.NewPfx(191134464, 24), // 11.100.123.0/24
net.NewPfx(167772160, 12), // 10.0.0.0/12
net.NewPfx(167772160, 10), // 10.0.0.0/10
},
needle: net.NewPfx(167772160, 8), // 10.0.0.0/8
expected: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0/8
net.NewPfx(167772160, 10), // 10.0.0.0
net.NewPfx(167772160, 12), // 10.0.0.0
},
},
{
name: "Test 2: Search pfx and don't dump more specifics",
prefixes: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0/8
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
},
needle: net.NewPfx(167772160, 8), // 10.0.0.0/8
expected: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0/8
},
},
{
name: "Test 3",
prefixes: []*net.Prefix{},
needle: net.NewPfx(167772160, 32), // 10.0.0.0/32
expected: nil,
},
{
name: "Test 4: Get Dummy",
prefixes: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0
net.NewPfx(191134464, 24), // 11.100.123.0/24
},
needle: net.NewPfx(167772160, 7), // 10.0.0.0/7
expected: nil,
},
{
name: "Test 5",
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
},
needle: net.NewPfx(191134464, 24), // 10.0.0.0/8
expected: []*net.Prefix{
net.NewPfx(191134464, 24), // 11.100.123.0/24
},
},
{
name: "Test 4: Get nonexistent #1",
prefixes: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0
net.NewPfx(191134464, 24), // 11.100.123.0/24
},
needle: net.NewPfx(167772160, 10), // 10.0.0.0/10
expected: nil,
},
{
name: "Test 4: Get nonexistent #2",
prefixes: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0/8
net.NewPfx(167772160, 12), // 10.0.0.0/12
},
needle: net.NewPfx(167772160, 10), // 10.0.0.0/10
expected: nil,
},
}
for _, test := range tests {
lpm := New()
for _, pfx := range test.prefixes {
lpm.Insert(&Route{pfx: pfx})
}
p := lpm.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 TestNewSuperNode(t *testing.T) {
tests := []struct {
name string
a *net.Prefix
b *net.Prefix
expected *node
}{
{
name: "Test 1",
a: net.NewPfx(167772160, 8), // 10.0.0.0/8
b: 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), //11.100.123.0/24
},
skip: 16,
},
},
},
}
for _, test := range tests {
n := newNode(&Route{pfx: test.a}, test.a.Pfxlen(), false)
n = n.newSuperNode(&Route{pfx: test.b})
assert.Equal(t, test.expected, n)
}
}
func TestDumpPfxs(t *testing.T) {
tests := []struct {
name string
prefixes []*net.Prefix
expected []*net.Prefix
}{
{
name: "Test 1: Empty node",
expected: nil,
},
{
name: "Test 2: ",
prefixes: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0/8
net.NewPfx(191134464, 24), // 11.100.123.0/24
net.NewPfx(167772160, 12), // 10.0.0.0/12
net.NewPfx(167772160, 10), // 10.0.0.0/10
},
expected: []*net.Prefix{
net.NewPfx(167772160, 8), // 10.0.0.0/8
net.NewPfx(167772160, 10), // 10.0.0.0/10
net.NewPfx(167772160, 12), // 10.0.0.0/12
net.NewPfx(191134464, 24), // 11.100.123.0/24
},
},
}
for _, test := range tests {
lpm := New()
for _, pfx := range test.prefixes {
lpm.Insert(&Route{pfx: pfx})
}
res := make([]*Route, 0)
r := lpm.root.dumpPfxs(res)
assert.Equal(t, test.expected, r)
}
}
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 TestInsertChildren(t *testing.T) {
tests := []struct {
name string
base *net.Prefix
old *net.Prefix
new *net.Prefix
expected *node
}{
{
name: "Test 1",
base: net.NewPfx(167772160, 8), //10.0.0.0/8
old: net.NewPfx(167772160, 9), //10.0.0.0/9
new: net.NewPfx(176160768, 9), //10.128.0.0/9
expected: &node{
route: &Route{
pfx: net.NewPfx(167772160, 8),
},
skip: 8,
dummy: true,
l: &node{
route: &Route{
pfx: net.NewPfx(167772160, 9),
},
},
h: &node{
route: &Route{
pfx: net.NewPfx(176160768, 9),
},
},
},
},
{
name: "Test 2",
base: net.NewPfx(167772160, 8), //10.0.0.0/8
old: net.NewPfx(176160768, 9), //10.128.0.0/9
new: net.NewPfx(167772160, 9), //10.0.0.0/9
expected: &node{
route: &Route{
pfx: net.NewPfx(167772160, 8),
},
skip: 8,
dummy: true,
l: &node{
route: &Route{
pfx: net.NewPfx(167772160, 9),
},
},
h: &node{
route: &Route{
pfx: net.NewPfx(176160768, 9),
},
},
},
},
}
for _, test := range tests {
n := newNode(&Route{pfx: test.base}, test.base.Pfxlen(), true)
old := newNode(&Route{pfx: test.old}, test.old.Pfxlen(), false)
n.insertChildren(old, &Route{pfx: test.new})
assert.Equal(t, test.expected, n)
}
}
func TestInsertBefore(t *testing.T) {
tests := []struct {
name string
a *net.Prefix
b *net.Prefix
expected *node
}{
{
name: "Test 1",
a: net.NewPfx(167772160, 10), // 10.0.0.0
b: net.NewPfx(167772160, 8), // 10.0.0.0
expected: &node{
route: &Route{
pfx: net.NewPfx(167772160, 8), // 10.0.0.0,
},
l: &node{
route: &Route{
pfx: net.NewPfx(167772160, 10), // 10.0.0.0
},
skip: 1,
},
skip: 8,
},
},
{
name: "Test 2",
a: net.NewPfx(184549376, 8), // 11.0.0.0/8
b: net.NewPfx(167772160, 7), // 10.0.0.0/7
expected: &node{
route: &Route{
pfx: net.NewPfx(167772160, 7), // 10.0.0.0,
},
h: &node{
route: &Route{
pfx: net.NewPfx(184549376, 8), // 10.0.0.0
},
skip: 0,
},
skip: 7,
},
},
}
for _, test := range tests {
n := newNode(&Route{pfx: test.a}, test.a.Pfxlen(), false)
n = n.insertBefore(&Route{pfx: test.b}, test.b.Pfxlen())
assert.Equal(t, test.expected, n)
}
}
package rt
import (
net "github.com/bio-routing/bio-rd/net"
)
// Path Types
const StaticPathType = 1
const BGPPathType = 2
const OSPFPathType = 3
const ISISPathType = 4
type Path struct {
Type uint8
StaticPath *StaticPath
BGPPath *BGPPath
}
type Route struct {
pfx *net.Prefix
activePaths []*Path
paths []*Path
}
func NewRoute(pfx *net.Prefix, paths []*Path) *Route {
return &Route{
pfx: pfx,
activePaths: make([]*Path, 0),
paths: paths,
}
}
func (r *Route) Pfxlen() uint8 {
return r.pfx.Pfxlen()
}
func (r *Route) Prefix() *net.Prefix {
return r.pfx
}
func (r *Route) Remove(rm *Route) (final bool) {
for _, del := range rm.paths {
r.paths = removePath(r.paths, del)
}
return len(r.paths) == 0
}
func removePath(paths []*Path, remove *Path) []*Path {
i := -1
for j := range paths {
if paths[j].Equal(remove) {
i = j
break
}
}
if i < 0 {
return paths
}
copy(paths[i:], paths[i+1:])
return paths[:len(paths)-1]
}
func (p *Path) Equal(q *Path) bool {
if p == nil || q == nil {
return false
}
if p.Type != q.Type {
return false
}
switch p.Type {
case BGPPathType:
if *p.BGPPath != *q.BGPPath {
return false
}
}
return true
}
func (r *Route) AddPath(p *Path) {
r.paths = append(r.paths, p)
r.bestPaths()
}
func (r *Route) AddPaths(paths []*Path) {
for _, p := range paths {
r.paths = append(r.paths, p)
}
r.bestPaths()
}
func (r *Route) bestPaths() {
var best []*Path
protocol := getBestProtocol(r.paths)
switch protocol {
case StaticPathType:
best = r.staticPathSelection()
case BGPPathType:
best = r.bgpPathSelection()
}
r.activePaths = best
}
func getBestProtocol(paths []*Path) uint8 {
best := uint8(0)
for _, p := range paths {
if best == 0 {
best = p.Type
continue
}
if p.Type > best {
best = p.Type
}
}
return best
}
package rt
import (
"testing"
net "github.com/bio-routing/bio-rd/net"
"github.com/stretchr/testify/assert"
)
func TestNewRoute(t *testing.T) {
tests := []struct {
name string
pfx *net.Prefix
paths []*Path
expected *Route
}{
{
name: "Test #1",
pfx: net.NewPfx(158798889, 24),
paths: []*Path{
{
Type: 2,
StaticPath: &StaticPath{
NextHop: 56963289,
},
},
},
expected: &Route{
pfx: net.NewPfx(158798889, 24),
activePaths: make([]*Path, 0),
paths: []*Path{
{
Type: 2,
StaticPath: &StaticPath{
NextHop: 56963289,
},
},
},
},
},
}
for _, test := range tests {
res := NewRoute(test.pfx, test.paths)
assert.Equal(t, test.expected, res)
}
}
func TestPfxlen(t *testing.T) {
tests := []struct {
name string
pfx *net.Prefix
expected uint8
}{
{
name: "Test #1",
pfx: net.NewPfx(158798889, 24),
expected: 24,
},
}
for _, test := range tests {
r := NewRoute(test.pfx, nil)
res := r.Pfxlen()
assert.Equal(t, test.expected, res)
}
}
func TestPrefix(t *testing.T) {
tests := []struct {
name string
pfx *net.Prefix
expected *net.Prefix
}{
{
name: "Test #1",
pfx: net.NewPfx(158798889, 24),
expected: net.NewPfx(158798889, 24),
},
}
for _, test := range tests {
r := NewRoute(test.pfx, nil)
res := r.Prefix()
assert.Equal(t, test.expected, res)
}
}
func TestRouteRemovePath(t *testing.T) {
tests := []struct {
name string
paths []*Path
remove *Path
expected []*Path
}{
{
name: "Remove middle",
paths: []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 100,
},
},
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 200,
},
},
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 300,
},
},
},
remove: &Path{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 200,
},
},
expected: []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 100,
},
},
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 300,
},
},
},
},
{
name: "Remove non-existent",
paths: []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 10,
},
},
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 20,
},
},
},
remove: &Path{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 50,
},
},
expected: []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 10,
},
},
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 20,
},
},
},
},
}
for _, test := range tests {
res := removePath(test.paths, test.remove)
assert.Equal(t, test.expected, res)
}
}
func TestEqual(t *testing.T) {
tests := []struct {
name string
pathA *Path
pathB *Path
expected bool
}{
{
name: "Unequal types",
pathA: &Path{
Type: 1,
},
pathB: &Path{
Type: 2,
},
expected: false,
},
{
name: "Unequal attributes",
pathA: &Path{
Type: 2,
BGPPath: &BGPPath{
LocalPref: 100,
},
},
pathB: &Path{
Type: 2,
BGPPath: &BGPPath{
LocalPref: 200,
},
},
expected: false,
},
{
name: "Equal",
pathA: &Path{
Type: 2,
BGPPath: &BGPPath{
LocalPref: 100,
},
},
pathB: &Path{
Type: 2,
BGPPath: &BGPPath{
LocalPref: 100,
},
},
expected: true,
},
}
for _, test := range tests {
res := test.pathA.Equal(test.pathB)
assert.Equal(t, test.expected, res)
}
}
func TestAddPath(t *testing.T) {
tests := []struct {
name string
route *Route
new *Path
expected *Route
}{
{
name: "Add a new best path",
route: &Route{
paths: []*Path{
{
Type: 2,
BGPPath: &BGPPath{
LocalPref: 100,
},
},
},
},
new: &Path{
Type: 2,
BGPPath: &BGPPath{
LocalPref: 200,
},
},
expected: &Route{
activePaths: []*Path{
{
Type: 2,
BGPPath: &BGPPath{
LocalPref: 200,
},
},
},
paths: []*Path{
{
Type: 2,
BGPPath: &BGPPath{
LocalPref: 100,
},
},
{
Type: 2,
BGPPath: &BGPPath{
LocalPref: 200,
},
},
},
},
},
}
for _, test := range tests {
test.route.AddPath(test.new)
assert.Equal(t, test.expected, test.route)
}
}
func TestAddPaths(t *testing.T) {
tests := []struct {
name string
route *Route
new []*Path
expected *Route
}{
{
name: "Add 2 new paths including a new best path",
route: &Route{
paths: []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 100,
},
},
},
},
new: []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 200,
},
},
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 50,
},
},
},
expected: &Route{
activePaths: []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 200,
},
},
},
paths: []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 100,
},
},
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 200,
},
},
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 50,
},
},
},
},
},
}
for _, test := range tests {
test.route.AddPaths(test.new)
assert.Equal(t, test.expected, test.route)
}
}
package rt
import (
"github.com/bio-routing/bio-rd/net"
)
type LPM struct {
root *node
nodes uint64
}
type node struct {
skip uint8
dummy bool
route *Route
l *node
h *node
}
// New creates a new empty LPM
func New() *LPM {
return &LPM{}
}
func newNode(route *Route, skip uint8, dummy bool) *node {
n := &node{
route: route,
skip: skip,
dummy: dummy,
}
return n
}
// LPM performs a longest prefix match for pfx on lpm
func (lpm *LPM) LPM(pfx *net.Prefix) (res []*Route) {
if lpm.root == nil {
return nil
}
lpm.root.lpm(pfx, &res)
return res
}
// RemovePath removes a path from the trie
func (lpm *LPM) RemovePath(route *Route) {
lpm.root.removePath(route)
}
func (lpm *LPM) RemovePfx(pfx *net.Prefix) {
lpm.root.removePfx(pfx)
}
// Get get's prefix pfx from the LPM
func (lpm *LPM) Get(pfx *net.Prefix, moreSpecifics bool) (res []*Route) {
if lpm.root == nil {
return nil
}
node := lpm.root.get(pfx)
if moreSpecifics {
return node.dumpPfxs(res)
}
if node == nil {
return nil
}
return []*Route{
node.route,
}
}
// Insert inserts a route into the LPM
func (lpm *LPM) Insert(route *Route) {
if lpm.root == nil {
lpm.root = newNode(route, route.Pfxlen(), false)
return
}
lpm.root = lpm.root.insert(route)
}
func (n *node) removePath(route *Route) {
if n == nil {
return
}
if *n.route.Prefix() == *route.Prefix() {
if n.dummy {
return
}
if n.route.Remove(route) {
// FIXME: Can this node actually be removed from the trie entirely?
n.dummy = true
}
return
}
b := getBitUint32(route.Prefix().Addr(), n.route.Pfxlen()+1)
if !b {
n.l.removePath(route)
return
}
n.h.removePath(route)
return
}
func (n *node) removePfx(pfx *net.Prefix) {
if n == nil {
return
}
if *n.route.Prefix() == *pfx {
if n.dummy {
return
}
n.dummy = true
return
}
b := getBitUint32(pfx.Addr(), n.route.Pfxlen()+1)
if !b {
n.l.removePfx(pfx)
return
}
n.h.removePfx(pfx)
return
}
func (n *node) lpm(needle *net.Prefix, res *[]*Route) {
if n == nil {
return
}
if *n.route.Prefix() == *needle && !n.dummy {
*res = append(*res, n.route)
return
}
if !n.route.Prefix().Contains(needle) {
return
}
if !n.dummy {
*res = append(*res, n.route)
}
n.l.lpm(needle, res)
n.h.lpm(needle, res)
}
func (n *node) dumpPfxs(res []*Route) []*Route {
if n == nil {
return nil
}
if !n.dummy {
res = append(res, n.route)
}
if n.l != nil {
res = n.l.dumpPfxs(res)
}
if n.h != nil {
res = n.h.dumpPfxs(res)
}
return res
}
func (n *node) get(pfx *net.Prefix) *node {
if n == nil {
return nil
}
if *n.route.Prefix() == *pfx {
if n.dummy {
return nil
}
return n
}
if n.route.Pfxlen() > pfx.Pfxlen() {
return nil
}
b := getBitUint32(pfx.Addr(), n.route.Pfxlen()+1)
if !b {
return n.l.get(pfx)
}
return n.h.get(pfx)
}
func (n *node) insert(route *Route) *node {
if *n.route.Prefix() == *route.Prefix() {
n.route.AddPaths(route.paths)
n.dummy = false
return n
}
// is pfx NOT a subnet of this node?
if !n.route.Prefix().Contains(route.Prefix()) {
if route.Prefix().Contains(n.route.Prefix()) {
return n.insertBefore(route, n.route.Pfxlen()-n.skip-1)
}
return n.newSuperNode(route)
}
// pfx is a subnet of this node
b := getBitUint32(route.Prefix().Addr(), n.route.Pfxlen()+1)
if !b {
return n.insertLow(route, n.route.Prefix().Pfxlen())
}
return n.insertHigh(route, n.route.Pfxlen())
}
func (n *node) insertLow(route *Route, parentPfxLen uint8) *node {
if n.l == nil {
n.l = newNode(route, route.Pfxlen()-parentPfxLen-1, false)
return n
}
n.l = n.l.insert(route)
return n
}
func (n *node) insertHigh(route *Route, parentPfxLen uint8) *node {
if n.h == nil {
n.h = newNode(route, route.Pfxlen()-parentPfxLen-1, false)
return n
}
n.h = n.h.insert(route)
return n
}
func (n *node) newSuperNode(route *Route) *node {
superNet := route.Prefix().GetSupernet(n.route.Prefix())
pfxLenDiff := n.route.Pfxlen() - superNet.Pfxlen()
skip := n.skip - pfxLenDiff
pseudoNode := newNode(NewRoute(superNet, nil), skip, true)
pseudoNode.insertChildren(n, route)
return pseudoNode
}
func (n *node) insertChildren(old *node, new *Route) {
// Place the old node
b := getBitUint32(old.route.Prefix().Addr(), n.route.Pfxlen()+1)
if !b {
n.l = old
n.l.skip = old.route.Pfxlen() - n.route.Pfxlen() - 1
} else {
n.h = old
n.h.skip = old.route.Pfxlen() - n.route.Pfxlen() - 1
}
// Place the new Prefix
newNode := newNode(new, new.Pfxlen()-n.route.Pfxlen()-1, false)
b = getBitUint32(new.Prefix().Addr(), n.route.Pfxlen()+1)
if !b {
n.l = newNode
} else {
n.h = newNode
}
}
func (n *node) insertBefore(route *Route, parentPfxLen uint8) *node {
tmp := n
pfxLenDiff := n.route.Pfxlen() - route.Pfxlen()
skip := n.skip - pfxLenDiff
new := newNode(route, skip, false)
b := getBitUint32(route.Prefix().Addr(), parentPfxLen)
if !b {
new.l = tmp
new.l.skip = tmp.route.Pfxlen() - route.Pfxlen() - 1
} else {
new.h = tmp
new.h.skip = tmp.route.Pfxlen() - route.Pfxlen() - 1
}
return new
}
func (lpm *LPM) Dump() []*Route {
res := make([]*Route, 0)
return lpm.root.dump(res)
}
func (n *node) dump(res []*Route) []*Route {
if n == nil {
return res
}
if !n.dummy {
res = append(res, n.route)
}
res = n.l.dump(res)
res = n.h.dump(res)
return res
}
func getBitUint32(x uint32, pos uint8) bool {
return ((x) & (1 << (32 - pos))) != 0
}
package rt
import (
"testing"
net "github.com/bio-routing/bio-rd/net"
"github.com/stretchr/testify/assert"
)
func TestNew(t *testing.T) {
l := New()
if l == nil {
t.Errorf("New() returned nil")
}
}
func TestRemovePath(t *testing.T) {
tests := []struct {
name string
routes []*Route
remove []*Route
expected []*Route
}{
{
name: "Remove a path that is the only one for a prefix",
routes: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
}),
NewRoute(net.NewPfx(167772160, 9), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
}), // 10.0.0.0/9
NewRoute(net.NewPfx(176160768, 9), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
}), // 10.128.0.0/9
},
remove: []*Route{
NewRoute(net.NewPfx(167772160, 8), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
}), // 10.0.0.0
},
expected: []*Route{
NewRoute(net.NewPfx(167772160, 9), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
}), // 10.0.0.0
NewRoute(net.NewPfx(176160768, 9), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
}), // 10.128.0.0
},
},
{
name: "Remove a path that is one of two for a prefix",
routes: []*Route{
NewRoute(net.NewPfx(167772160, 8), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 1000,
},
},
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 2000,
},
},
}), // 10.0.0.0/8
NewRoute(net.NewPfx(167772160, 9), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
}), // 10.0.0.0/9
NewRoute(net.NewPfx(176160768, 9), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
}), // 10.128.0.0/9
},
remove: []*Route{
NewRoute(net.NewPfx(167772160, 8), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 1000,
},
},
}), // 10.0.0.0
},
expected: []*Route{
NewRoute(net.NewPfx(167772160, 8), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{
LocalPref: 2000,
},
},
}), // 10.0.0.0/8
NewRoute(net.NewPfx(167772160, 9), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
}), // 10.0.0.0/9
NewRoute(net.NewPfx(176160768, 9), []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
}), // 10.128.0.0/9
},
},
}
for _, test := range tests {
lpm := New()
for _, route := range test.routes {
lpm.Insert(route)
}
for _, route := range test.remove {
lpm.RemovePath(route)
}
res := lpm.Dump()
assert.Equal(t, test.expected, res)
}
}
func strAddr(s string) uint32 {
ret, _ := net.StrToAddr(s)
return ret
}
package rt
type StaticPath struct {
NextHop uint32
}
func (r *Route) staticPathSelection() (res []*Path) {
if len(r.paths) == 1 {
copy(res, r.paths)
return res
}
for _, p := range r.paths {
if p.Type != StaticPathType {
continue
}
res = append(res, p)
}
return
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment