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
No related branches found
No related tags found
No related merge requests found
......@@ -17,8 +17,8 @@ type Prefix struct {
}
// NewPfx creates a new Prefix
func NewPfx(addr uint32, pfxlen uint8) *Prefix {
return &Prefix{
func NewPfx(addr uint32, pfxlen uint8) Prefix {
return Prefix{
addr: addr,
pfxlen: pfxlen,
}
......@@ -49,22 +49,22 @@ func StrToAddr(x string) (uint32, error) {
}
// Addr returns the address of the prefix
func (pfx *Prefix) Addr() uint32 {
func (pfx Prefix) Addr() uint32 {
return pfx.addr
}
// Pfxlen returns the length of the prefix
func (pfx *Prefix) Pfxlen() uint8 {
func (pfx Prefix) Pfxlen() uint8 {
return pfx.pfxlen
}
// 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)
}
// 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 {
return false
}
......@@ -74,12 +74,12 @@ func (pfx *Prefix) Contains(x *Prefix) bool {
}
// Equal checks if pfx and x are equal
func (pfx *Prefix) Equal(x *Prefix) bool {
return *pfx == *x
func (pfx Prefix) Equal(x Prefix) bool {
return pfx == 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
a := pfx.addr >> (32 - maxPfxLen)
b := x.addr >> (32 - maxPfxLen)
......@@ -90,7 +90,7 @@ func (pfx *Prefix) GetSupernet(x *Prefix) *Prefix {
maxPfxLen--
}
return &Prefix{
return Prefix{
addr: a << (32 - maxPfxLen),
pfxlen: maxPfxLen,
}
......
......@@ -16,7 +16,7 @@ func TestNewPfx(t *testing.T) {
func TestAddr(t *testing.T) {
tests := []struct {
name string
pfx *Prefix
pfx Prefix
expected uint32
}{
{
......@@ -37,7 +37,7 @@ func TestAddr(t *testing.T) {
func TestPfxlen(t *testing.T) {
tests := []struct {
name string
pfx *Prefix
pfx Prefix
expected uint8
}{
{
......@@ -58,36 +58,36 @@ func TestPfxlen(t *testing.T) {
func TestGetSupernet(t *testing.T) {
tests := []struct {
name string
a *Prefix
b *Prefix
expected *Prefix
a Prefix
b Prefix
expected Prefix
}{
{
name: "Test 1",
a: &Prefix{
a: Prefix{
addr: 167772160, // 10.0.0.0/8
pfxlen: 8,
},
b: &Prefix{
b: Prefix{
addr: 191134464, // 11.100.123.0/24
pfxlen: 24,
},
expected: &Prefix{
expected: Prefix{
addr: 167772160, // 10.0.0.0/7
pfxlen: 7,
},
},
{
name: "Test 2",
a: &Prefix{
a: Prefix{
addr: 167772160, // 10.0.0.0/8
pfxlen: 8,
},
b: &Prefix{
b: Prefix{
addr: 3232235520, // 192.168.0.0/24
pfxlen: 24,
},
expected: &Prefix{
expected: Prefix{
addr: 0, // 0.0.0.0/0
pfxlen: 0,
},
......@@ -103,17 +103,17 @@ func TestGetSupernet(t *testing.T) {
func TestContains(t *testing.T) {
tests := []struct {
name string
a *Prefix
b *Prefix
a Prefix
b Prefix
expected bool
}{
{
name: "Test 1",
a: &Prefix{
a: Prefix{
addr: 0,
pfxlen: 0,
},
b: &Prefix{
b: Prefix{
addr: 100,
pfxlen: 24,
},
......@@ -121,11 +121,11 @@ func TestContains(t *testing.T) {
},
{
name: "Test 2",
a: &Prefix{
a: Prefix{
addr: 100,
pfxlen: 24,
},
b: &Prefix{
b: Prefix{
addr: 0,
pfxlen: 0,
},
......@@ -133,11 +133,11 @@ func TestContains(t *testing.T) {
},
{
name: "Test 3",
a: &Prefix{
a: Prefix{
addr: 167772160,
pfxlen: 8,
},
b: &Prefix{
b: Prefix{
addr: 167772160,
pfxlen: 9,
},
......@@ -145,11 +145,11 @@ func TestContains(t *testing.T) {
},
{
name: "Test 4",
a: &Prefix{
a: Prefix{
addr: 167772160,
pfxlen: 8,
},
b: &Prefix{
b: Prefix{
addr: 174391040,
pfxlen: 24,
},
......@@ -157,11 +157,11 @@ func TestContains(t *testing.T) {
},
{
name: "Test 5",
a: &Prefix{
a: Prefix{
addr: 167772160,
pfxlen: 8,
},
b: &Prefix{
b: Prefix{
addr: 184549377,
pfxlen: 24,
},
......@@ -169,11 +169,11 @@ func TestContains(t *testing.T) {
},
{
name: "Test 6",
a: &Prefix{
a: Prefix{
addr: 167772160,
pfxlen: 8,
},
b: &Prefix{
b: Prefix{
addr: 191134464,
pfxlen: 24,
},
......@@ -227,8 +227,8 @@ func TestMin(t *testing.T) {
func TestEqual(t *testing.T) {
tests := []struct {
name string
a *Prefix
b *Prefix
a Prefix
b Prefix
expected bool
}{
{
......@@ -256,7 +256,7 @@ func TestEqual(t *testing.T) {
func TestString(t *testing.T) {
tests := []struct {
name string
pfx *Prefix
pfx Prefix
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