Skip to content
Snippets Groups Projects
Unverified Commit b5c8847d authored by takt's avatar takt Committed by GitHub
Browse files

Merge pull request #3 from bio-routing/route

Added new route type and refactored net.Prefix
parents 0d038158 14001aad
Branches
Tags
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)
best = 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}
best = 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) lookup(p BGPPath) *BGPPath {
pathCounter, ok := m.paths[p]
if !ok {
return nil
}
return pathCounter.path
}
// 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()
q := m.lookup(p)
if q == nil {
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.lookup(p) == nil {
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()
}
// Paths returns a copy of the list of paths associated with route r
func (r *Route) Paths() []*Path {
if r.paths == nil {
return nil
}
ret := make([]*Path, len(r.paths))
copy(ret, r.paths)
return ret
}
// 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) int {
if p == nil {
return len(r.paths)
}
r.mu.Lock()
defer r.mu.Unlock()
r.paths = removePath(r.paths, p)
return len(r.paths)
}
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