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

Added new route type and refactored net.Prefix

parent 0d038158
No related branches found
No related tags found
No related merge requests found
...@@ -17,8 +17,8 @@ type Prefix struct { ...@@ -17,8 +17,8 @@ type Prefix struct {
} }
// NewPfx creates a new Prefix // NewPfx creates a new Prefix
func NewPfx(addr uint32, pfxlen uint8) *Prefix { func NewPfx(addr uint32, pfxlen uint8) Prefix {
return &Prefix{ return Prefix{
addr: addr, addr: addr,
pfxlen: pfxlen, pfxlen: pfxlen,
} }
...@@ -49,22 +49,22 @@ func StrToAddr(x string) (uint32, error) { ...@@ -49,22 +49,22 @@ func StrToAddr(x string) (uint32, error) {
} }
// Addr returns the address of the prefix // Addr returns the address of the prefix
func (pfx *Prefix) Addr() uint32 { func (pfx Prefix) Addr() uint32 {
return pfx.addr return pfx.addr
} }
// Pfxlen returns the length of the prefix // Pfxlen returns the length of the prefix
func (pfx *Prefix) Pfxlen() uint8 { func (pfx Prefix) Pfxlen() uint8 {
return pfx.pfxlen return pfx.pfxlen
} }
// String returns a string representation of pfx // String returns a string representation of pfx
func (pfx *Prefix) String() string { func (pfx Prefix) String() string {
return fmt.Sprintf("%s/%d", net.IP(convert.Uint32Byte(pfx.addr)), pfx.pfxlen) return fmt.Sprintf("%s/%d", net.IP(convert.Uint32Byte(pfx.addr)), pfx.pfxlen)
} }
// Contains checks if x is a subnet of or equal to pfx // Contains checks if x is a subnet of or equal to pfx
func (pfx *Prefix) Contains(x *Prefix) bool { func (pfx Prefix) Contains(x Prefix) bool {
if x.pfxlen <= pfx.pfxlen { if x.pfxlen <= pfx.pfxlen {
return false return false
} }
...@@ -74,12 +74,12 @@ func (pfx *Prefix) Contains(x *Prefix) bool { ...@@ -74,12 +74,12 @@ func (pfx *Prefix) Contains(x *Prefix) bool {
} }
// Equal checks if pfx and x are equal // Equal checks if pfx and x are equal
func (pfx *Prefix) Equal(x *Prefix) bool { func (pfx Prefix) Equal(x Prefix) bool {
return *pfx == *x return pfx == x
} }
// GetSupernet gets the next common supernet of pfx and x // GetSupernet gets the next common supernet of pfx and x
func (pfx *Prefix) GetSupernet(x *Prefix) *Prefix { func (pfx Prefix) GetSupernet(x Prefix) Prefix {
maxPfxLen := min(pfx.pfxlen, x.pfxlen) - 1 maxPfxLen := min(pfx.pfxlen, x.pfxlen) - 1
a := pfx.addr >> (32 - maxPfxLen) a := pfx.addr >> (32 - maxPfxLen)
b := x.addr >> (32 - maxPfxLen) b := x.addr >> (32 - maxPfxLen)
...@@ -90,7 +90,7 @@ func (pfx *Prefix) GetSupernet(x *Prefix) *Prefix { ...@@ -90,7 +90,7 @@ func (pfx *Prefix) GetSupernet(x *Prefix) *Prefix {
maxPfxLen-- maxPfxLen--
} }
return &Prefix{ return Prefix{
addr: a << (32 - maxPfxLen), addr: a << (32 - maxPfxLen),
pfxlen: maxPfxLen, pfxlen: maxPfxLen,
} }
......
...@@ -16,7 +16,7 @@ func TestNewPfx(t *testing.T) { ...@@ -16,7 +16,7 @@ func TestNewPfx(t *testing.T) {
func TestAddr(t *testing.T) { func TestAddr(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
pfx *Prefix pfx Prefix
expected uint32 expected uint32
}{ }{
{ {
...@@ -37,7 +37,7 @@ func TestAddr(t *testing.T) { ...@@ -37,7 +37,7 @@ func TestAddr(t *testing.T) {
func TestPfxlen(t *testing.T) { func TestPfxlen(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
pfx *Prefix pfx Prefix
expected uint8 expected uint8
}{ }{
{ {
...@@ -58,36 +58,36 @@ func TestPfxlen(t *testing.T) { ...@@ -58,36 +58,36 @@ func TestPfxlen(t *testing.T) {
func TestGetSupernet(t *testing.T) { func TestGetSupernet(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
a *Prefix a Prefix
b *Prefix b Prefix
expected *Prefix expected Prefix
}{ }{
{ {
name: "Test 1", name: "Test 1",
a: &Prefix{ a: Prefix{
addr: 167772160, // 10.0.0.0/8 addr: 167772160, // 10.0.0.0/8
pfxlen: 8, pfxlen: 8,
}, },
b: &Prefix{ b: Prefix{
addr: 191134464, // 11.100.123.0/24 addr: 191134464, // 11.100.123.0/24
pfxlen: 24, pfxlen: 24,
}, },
expected: &Prefix{ expected: Prefix{
addr: 167772160, // 10.0.0.0/7 addr: 167772160, // 10.0.0.0/7
pfxlen: 7, pfxlen: 7,
}, },
}, },
{ {
name: "Test 2", name: "Test 2",
a: &Prefix{ a: Prefix{
addr: 167772160, // 10.0.0.0/8 addr: 167772160, // 10.0.0.0/8
pfxlen: 8, pfxlen: 8,
}, },
b: &Prefix{ b: Prefix{
addr: 3232235520, // 192.168.0.0/24 addr: 3232235520, // 192.168.0.0/24
pfxlen: 24, pfxlen: 24,
}, },
expected: &Prefix{ expected: Prefix{
addr: 0, // 0.0.0.0/0 addr: 0, // 0.0.0.0/0
pfxlen: 0, pfxlen: 0,
}, },
...@@ -103,17 +103,17 @@ func TestGetSupernet(t *testing.T) { ...@@ -103,17 +103,17 @@ func TestGetSupernet(t *testing.T) {
func TestContains(t *testing.T) { func TestContains(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
a *Prefix a Prefix
b *Prefix b Prefix
expected bool expected bool
}{ }{
{ {
name: "Test 1", name: "Test 1",
a: &Prefix{ a: Prefix{
addr: 0, addr: 0,
pfxlen: 0, pfxlen: 0,
}, },
b: &Prefix{ b: Prefix{
addr: 100, addr: 100,
pfxlen: 24, pfxlen: 24,
}, },
...@@ -121,11 +121,11 @@ func TestContains(t *testing.T) { ...@@ -121,11 +121,11 @@ func TestContains(t *testing.T) {
}, },
{ {
name: "Test 2", name: "Test 2",
a: &Prefix{ a: Prefix{
addr: 100, addr: 100,
pfxlen: 24, pfxlen: 24,
}, },
b: &Prefix{ b: Prefix{
addr: 0, addr: 0,
pfxlen: 0, pfxlen: 0,
}, },
...@@ -133,11 +133,11 @@ func TestContains(t *testing.T) { ...@@ -133,11 +133,11 @@ func TestContains(t *testing.T) {
}, },
{ {
name: "Test 3", name: "Test 3",
a: &Prefix{ a: Prefix{
addr: 167772160, addr: 167772160,
pfxlen: 8, pfxlen: 8,
}, },
b: &Prefix{ b: Prefix{
addr: 167772160, addr: 167772160,
pfxlen: 9, pfxlen: 9,
}, },
...@@ -145,11 +145,11 @@ func TestContains(t *testing.T) { ...@@ -145,11 +145,11 @@ func TestContains(t *testing.T) {
}, },
{ {
name: "Test 4", name: "Test 4",
a: &Prefix{ a: Prefix{
addr: 167772160, addr: 167772160,
pfxlen: 8, pfxlen: 8,
}, },
b: &Prefix{ b: Prefix{
addr: 174391040, addr: 174391040,
pfxlen: 24, pfxlen: 24,
}, },
...@@ -157,11 +157,11 @@ func TestContains(t *testing.T) { ...@@ -157,11 +157,11 @@ func TestContains(t *testing.T) {
}, },
{ {
name: "Test 5", name: "Test 5",
a: &Prefix{ a: Prefix{
addr: 167772160, addr: 167772160,
pfxlen: 8, pfxlen: 8,
}, },
b: &Prefix{ b: Prefix{
addr: 184549377, addr: 184549377,
pfxlen: 24, pfxlen: 24,
}, },
...@@ -169,11 +169,11 @@ func TestContains(t *testing.T) { ...@@ -169,11 +169,11 @@ func TestContains(t *testing.T) {
}, },
{ {
name: "Test 6", name: "Test 6",
a: &Prefix{ a: Prefix{
addr: 167772160, addr: 167772160,
pfxlen: 8, pfxlen: 8,
}, },
b: &Prefix{ b: Prefix{
addr: 191134464, addr: 191134464,
pfxlen: 24, pfxlen: 24,
}, },
...@@ -227,8 +227,8 @@ func TestMin(t *testing.T) { ...@@ -227,8 +227,8 @@ func TestMin(t *testing.T) {
func TestEqual(t *testing.T) { func TestEqual(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
a *Prefix a Prefix
b *Prefix b Prefix
expected bool expected bool
}{ }{
{ {
...@@ -256,7 +256,7 @@ func TestEqual(t *testing.T) { ...@@ -256,7 +256,7 @@ func TestEqual(t *testing.T) {
func TestString(t *testing.T) { func TestString(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
pfx *Prefix pfx Prefix
expected string expected string
}{ }{
{ {
......
package route
// BGPPath represents a set of BGP path attributes
type BGPPath struct {
PathIdentifier uint32
NextHop uint32
LocalPref uint32
ASPath string
ASPathLen uint16
Origin uint8
MED uint32
EBGP bool
BGPIdentifier uint32
Source uint32
}
func (r *Route) bgpPathSelection() (best *Path, active []*Path) {
// TODO: Implement next hop lookup and compare IGP metrics
for _, p := range r.paths {
if p.Type != BGPPathType {
continue
}
if len(active) == 0 {
active = append(active, p)
continue
}
if active[0].BGPPath.ecmp(p.BGPPath) {
active = append(active, p)
if !r.bestPath.BGPPath.better(p.BGPPath) {
continue
}
best = p
continue
}
if !active[0].BGPPath.betterECMP(p.BGPPath) {
continue
}
active = []*Path{p}
}
return best, active
}
func (b *BGPPath) betterECMP(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) better(c *BGPPath) bool {
if b.betterECMP(c) {
return true
}
if c.BGPIdentifier < b.BGPIdentifier {
return true
}
if c.Source < b.Source {
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 route
import (
"log"
"sync"
)
// BGPPathManager is a component used to deduplicate BGP Path objects
type BGPPathManager struct {
paths map[BGPPath]*BGPPathCounter
mu sync.Mutex
}
// BGPPathCounter couples a counter to a particular path
type BGPPathCounter struct {
usageCount uint64
path *BGPPath
}
// NewBGPPathManager creates a new BGP Path Manager
func NewBGPPathManager() *BGPPathManager {
m := &BGPPathManager{}
return m
}
func (m *BGPPathManager) pathExists(p BGPPath) bool {
if _, ok := m.paths[p]; !ok {
return false
}
return true
}
// AddPath adds a path to the cache if it doesn't exist. If it exist a pointer to the cached object is returned.
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
}
// RemovePath notifies us that there is one user less for path p
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)
}
}
package route
type Path struct {
Type uint8
StaticPath *StaticPath
BGPPath *BGPPath
}
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
}
package route
import (
"sync"
"github.com/bio-routing/bio-rd/net"
)
// StaticPathType indicats a path is a static path
const StaticPathType = 1
// BGPPathType indicates a path is a BGP path
const BGPPathType = 2
// OSPFPathType indicates a path is an OSPF path
const OSPFPathType = 3
// ISISPathType indicates a path is an ISIS path
const ISISPathType = 4
// Route links a prefix to paths
type Route struct {
pfx net.Prefix
mu sync.Mutex
bestPath *Path
activePaths []*Path
paths []*Path
}
// NewRoute generates a new route
func NewRoute(pfx net.Prefix, p *Path) *Route {
r := &Route{
pfx: pfx,
}
if p == nil {
r.paths = make([]*Path, 0)
return r
}
r.paths = []*Path{p}
return r
}
// Prefix gets the prefix of route `r`
func (r *Route) Prefix() net.Prefix {
return r.pfx
}
// Addr gets a routes address
func (r *Route) Addr() uint32 {
return r.pfx.Addr()
}
// Pfxlen gets a routes prefix length
func (r *Route) Pfxlen() uint8 {
return r.pfx.Pfxlen()
}
// AddPath adds path p to route r
func (r *Route) AddPath(p *Path) {
if p == nil {
return
}
r.mu.Lock()
defer r.mu.Unlock()
r.paths = append(r.paths, p)
}
// RemovePath removes path `rm` from route `r`. Returns true if removed path was last one. False otherwise.
func (r *Route) RemovePath(p *Path) (final bool) {
if p == nil {
return false
}
r.mu.Lock()
defer r.mu.Unlock()
r.paths = removePath(r.paths, p)
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]
}
package route
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/bio-routing/bio-rd/net"
)
func TestNewRoute(t *testing.T) {
tests := []struct {
name string
pfx net.Prefix
path *Path
expected *Route
}{
{
name: "BGP Path",
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
path: &Path{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
expected: &Route{
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
paths: []*Path{
&Path{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
},
},
},
{
name: "Empty Path",
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
expected: &Route{
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
paths: []*Path{},
},
},
}
for _, test := range tests {
res := NewRoute(test.pfx, test.path)
assert.Equal(t, test.expected, res)
}
}
func TestPrefix(t *testing.T) {
tests := []struct {
name string
route *Route
expected net.Prefix
}{
{
name: "Prefix",
route: &Route{
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
},
expected: net.NewPfx(strAddr("10.0.0.0"), 8),
},
}
for _, test := range tests {
res := test.route.Prefix()
assert.Equal(t, test.expected, res)
}
}
func TestAddr(t *testing.T) {
tests := []struct {
name string
route *Route
expected uint32
}{
{
name: "Prefix",
route: &Route{
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
},
expected: 0xa000000,
},
}
for _, test := range tests {
res := test.route.Addr()
assert.Equal(t, test.expected, res)
}
}
func TestPfxlen(t *testing.T) {
tests := []struct {
name string
route *Route
expected uint8
}{
{
name: "Prefix",
route: &Route{
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
},
expected: 8,
},
}
for _, test := range tests {
res := test.route.Pfxlen()
assert.Equal(t, test.expected, res)
}
}
func TestAddPath(t *testing.T) {
tests := []struct {
name string
route *Route
newPath *Path
expected *Route
}{
{
name: "Regular BGP path",
route: NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &Path{
Type: BGPPathType,
BGPPath: &BGPPath{},
}),
newPath: &Path{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
expected: &Route{
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
paths: []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
},
},
},
{
name: "Nil path",
route: NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &Path{
Type: BGPPathType,
BGPPath: &BGPPath{},
}),
newPath: nil,
expected: &Route{
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
paths: []*Path{
{
Type: BGPPathType,
BGPPath: &BGPPath{},
},
},
},
},
}
for _, test := range tests {
test.route.AddPath(test.newPath)
assert.Equal(t, test.expected, test.route)
}
}
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 strAddr(s string) uint32 {
ret, _ := net.StrToAddr(s)
return ret
}
package route
// StaticPath represents a static path of a route
type StaticPath struct {
NextHop uint32
}
func (r *Route) staticPathSelection() (best *Path, active []*Path) {
if r.paths == nil {
return nil, nil
}
if len(r.paths) == 0 {
return nil, nil
}
for _, p := range r.paths {
if p.Type != StaticPathType {
continue
}
active = append(active, p)
best = p
}
return
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment