Skip to content
Snippets Groups Projects
Unverified Commit 4f0e870b authored by Daniel Czerwonk's avatar Daniel Czerwonk Committed by GitHub
Browse files

Merge pull request #5 from bio-routing/adjRIBIn

Implementing Adj rib in
parents d6026457 f1a52422
Branches
Tags
No related merge requests found
...@@ -80,7 +80,7 @@ func (r *Route) AddPath(p *Path) { ...@@ -80,7 +80,7 @@ func (r *Route) AddPath(p *Path) {
r.paths = append(r.paths, p) r.paths = append(r.paths, p)
} }
// RemovePath removes path `rm` from route `r`. Returns true if removed path was last one. False otherwise. // RemovePath removes path `p` from route `r`. Returns length of path list after removing path `p`
func (r *Route) RemovePath(p *Path) int { func (r *Route) RemovePath(p *Path) int {
if p == nil { if p == nil {
return len(r.paths) return len(r.paths)
......
package adjRIBIn
import (
"sync"
"github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/route"
"github.com/bio-routing/bio-rd/routingtable"
)
// AdjRIBIn represents an Adjacency RIB In as described in RFC4271
type AdjRIBIn struct {
rt *routingtable.RoutingTable
routingtable.ClientManager
mu sync.RWMutex
}
// NewAdjRIBIn creates a new Adjacency RIB In
func NewAdjRIBIn() *AdjRIBIn {
a := &AdjRIBIn{
rt: routingtable.NewRoutingTable(),
}
a.ClientManager = routingtable.NewClientManager(a)
return a
}
// UpdateNewClient sends current state to a new client
func (a *AdjRIBIn) UpdateNewClient(client routingtable.RouteTableClient) error {
a.mu.RLock()
defer a.mu.RUnlock()
routes := a.rt.Dump()
for _, route := range routes {
paths := route.Paths()
for _, path := range paths {
client.AddPath(route.Prefix(), path)
}
}
return nil
}
// AddPath replaces the path for prefix `pfx`. If the prefix doesn't exist it is added.
func (a *AdjRIBIn) AddPath(pfx net.Prefix, p *route.Path) error {
a.mu.Lock()
defer a.mu.Unlock()
oldPaths := a.rt.ReplacePath(pfx, p)
a.removePathsFromClients(pfx, oldPaths)
for _, client := range a.ClientManager.Clients() {
client.AddPath(pfx, p)
}
return nil
}
// RemovePath removes the path for prefix `pfx`
func (a *AdjRIBIn) RemovePath(pfx net.Prefix, p *route.Path) bool {
a.mu.Lock()
defer a.mu.Unlock()
r := a.rt.Get(pfx)
if r == nil {
return false
}
oldPaths := r.Paths()
for _, path := range oldPaths {
a.rt.RemovePath(pfx, path)
}
a.removePathsFromClients(pfx, oldPaths)
return true
}
func (a *AdjRIBIn) removePathsFromClients(pfx net.Prefix, paths []*route.Path) {
for _, path := range paths {
for _, client := range a.ClientManager.Clients() {
client.RemovePath(pfx, path)
}
}
}
package adjRIBIn
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/route"
"github.com/bio-routing/bio-rd/routingtable"
)
type RTMockClient struct {
removePathParams struct {
pfx net.Prefix
path *route.Path
}
}
func NewRTMockClient() *RTMockClient {
return &RTMockClient{}
}
func (m *RTMockClient) AddPath(pfx net.Prefix, p *route.Path) error {
return nil
}
func (m *RTMockClient) UpdateNewClient(client routingtable.RouteTableClient) error {
return fmt.Errorf("Not implemented")
}
// RemovePath removes the path for prefix `pfx`
func (m *RTMockClient) RemovePath(pfx net.Prefix, p *route.Path) bool {
m.removePathParams.pfx = pfx
m.removePathParams.path = p
return true
}
func TestAddPath(t *testing.T) {
tests := []struct {
name string
routes []*route.Route
removePfx net.Prefix
removePath *route.Path
expected []*route.Route
}{
{
name: "Add route",
routes: []*route.Route{
route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{
LocalPref: 100,
},
}),
},
removePfx: net.NewPfx(0, 0),
removePath: nil,
expected: []*route.Route{
route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{
LocalPref: 100,
},
}),
},
},
{
name: "Overwrite routes",
routes: []*route.Route{
route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{
LocalPref: 100,
},
}),
route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{
LocalPref: 200,
},
}),
},
removePfx: net.NewPfx(strAddr("10.0.0.0"), 8),
removePath: &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{
LocalPref: 100,
},
},
expected: []*route.Route{
route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{
LocalPref: 200,
},
}),
},
},
}
for _, test := range tests {
adjRIBIn := NewAdjRIBIn()
mc := NewRTMockClient()
adjRIBIn.ClientManager.Register(mc)
for _, route := range test.routes {
adjRIBIn.AddPath(route.Prefix(), route.Paths()[0])
}
if mc.removePathParams.pfx != test.removePfx {
t.Errorf("Test %q failed: Call to RemovePath did not propagate prefix properly: Got: %s Want: %s", test.name, mc.removePathParams.pfx.String(), test.removePfx.String())
}
assert.Equal(t, test.removePath, mc.removePathParams.path)
assert.Equal(t, test.expected, adjRIBIn.rt.Dump())
}
}
func TestRemovePath(t *testing.T) {
tests := []struct {
name string
routes []*route.Route
removePfx net.Prefix
removePath *route.Path
expected []*route.Route
wantPropagation bool
}{
{
name: "Remove an existing route",
routes: []*route.Route{
route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{},
}),
route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 9), &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{},
}),
route.NewRoute(net.NewPfx(strAddr("10.128.0.0"), 9), &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{},
}),
},
removePfx: net.NewPfx(strAddr("10.0.0.0"), 8),
removePath: &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{},
},
expected: []*route.Route{
route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 9), &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{},
}),
route.NewRoute(net.NewPfx(strAddr("10.128.0.0"), 9), &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{},
}),
},
wantPropagation: true,
},
{
name: "Remove non existing route",
routes: []*route.Route{
route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 9), &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{},
}),
route.NewRoute(net.NewPfx(strAddr("10.128.0.0"), 9), &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{},
}),
},
removePfx: net.NewPfx(strAddr("10.0.0.0"), 8),
removePath: &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{},
},
expected: []*route.Route{
route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 9), &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{},
}),
route.NewRoute(net.NewPfx(strAddr("10.128.0.0"), 9), &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{},
}),
},
wantPropagation: false,
},
}
for _, test := range tests {
adjRIBIn := NewAdjRIBIn()
for _, route := range test.routes {
adjRIBIn.AddPath(route.Prefix(), route.Paths()[0])
}
mc := NewRTMockClient()
adjRIBIn.ClientManager.Register(mc)
adjRIBIn.RemovePath(test.removePfx, test.removePath)
if test.wantPropagation {
if mc.removePathParams.pfx != test.removePfx {
t.Errorf("Test %q failed: Call to RemovePath did not propagate prefix properly: Got: %s Want: %s", test.name, mc.removePathParams.pfx.String(), test.removePfx.String())
}
assert.Equal(t, test.removePath, mc.removePathParams.path)
} else {
if mc.removePathParams.pfx != net.NewPfx(0, 0) {
t.Errorf("Test %q failed: Call to RemovePath propagated unexpectedly", test.name)
}
}
assert.Equal(t, test.expected, adjRIBIn.rt.Dump())
}
}
func strAddr(s string) uint32 {
ret, _ := net.StrToAddr(s)
return ret
}
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
// RouteTableClient is the interface that every type of RIB must implement // RouteTableClient is the interface that every type of RIB must implement
type RouteTableClient interface { type RouteTableClient interface {
AddPath(*net.Prefix, *route.Path) error AddPath(net.Prefix, *route.Path) error
RemovePath(*net.Prefix, *route.Path) error RemovePath(net.Prefix, *route.Path) bool
UpdateNewClient(RouteTableClient) error
} }
package routingtable package routingtable
// ClientManager manages clients of routing tables (observer pattern)
type ClientManager struct { type ClientManager struct {
clients map[RouteTableClient]struct{} // Ensures a client registers at most once clients map[RouteTableClient]struct{} // Ensures a client registers at most once
routingTable *RoutingTable master RouteTableClient
} }
// NewClientManager creates and initializes a new client manager
func NewClientManager(master RouteTableClient) ClientManager {
return ClientManager{
clients: make(map[RouteTableClient]struct{}, 0),
master: master,
}
}
// Register registers a client for updates
func (c *ClientManager) Register(client RouteTableClient) { func (c *ClientManager) Register(client RouteTableClient) {
if c.clients == nil { if c.clients == nil {
c.clients = make(map[RouteTableClient]struct{}, 0) c.clients = make(map[RouteTableClient]struct{}, 0)
} }
c.clients[client] = struct{}{} c.clients[client] = struct{}{}
//c.routingTable.updateNewClient(client) c.master.UpdateNewClient(client)
} }
// Unregister unregisters a client
func (c *ClientManager) Unregister(client RouteTableClient) { func (c *ClientManager) Unregister(client RouteTableClient) {
if _, ok := c.clients[client]; !ok { if _, ok := c.clients[client]; !ok {
return return
} }
delete(c.clients, client) delete(c.clients, client)
} }
// Clients returns a list of registered clients
func (c *ClientManager) Clients() []RouteTableClient {
ret := make([]RouteTableClient, 0)
for rtc := range c.clients {
ret = append(ret, rtc)
}
return ret
}
package routingtable
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/route"
)
type MockClient struct {
foo int
}
func (m MockClient) AddPath(net.Prefix, *route.Path) error {
return nil
}
func (m MockClient) RemovePath(net.Prefix, *route.Path) bool {
return false
}
func (m MockClient) UpdateNewClient(RouteTableClient) error {
return nil
}
func TestClients(t *testing.T) {
tests := []struct {
name string
clients []MockClient
expected []RouteTableClient
}{
{
name: "No clients",
clients: []MockClient{},
expected: []RouteTableClient{},
},
{
name: "No clients",
clients: []MockClient{
MockClient{
foo: 1,
},
MockClient{
foo: 2,
},
},
expected: []RouteTableClient{
MockClient{
foo: 1,
},
MockClient{
foo: 2,
},
},
},
}
for _, test := range tests {
cm := NewClientManager(MockClient{})
for _, client := range test.clients {
cm.Register(client)
}
ret := cm.Clients()
assert.Equal(t, test.expected, ret)
}
}
...@@ -23,6 +23,10 @@ func (rt *RoutingTable) AddPath(pfx net.Prefix, p *route.Path) error { ...@@ -23,6 +23,10 @@ func (rt *RoutingTable) AddPath(pfx net.Prefix, p *route.Path) error {
rt.mu.Lock() rt.mu.Lock()
defer rt.mu.Unlock() defer rt.mu.Unlock()
return rt.addPath(pfx, p)
}
func (rt *RoutingTable) addPath(pfx net.Prefix, p *route.Path) error {
if rt.root == nil { if rt.root == nil {
rt.root = newNode(pfx, p, pfx.Pfxlen(), false) rt.root = newNode(pfx, p, pfx.Pfxlen(), false)
return nil return nil
...@@ -32,13 +36,55 @@ func (rt *RoutingTable) AddPath(pfx net.Prefix, p *route.Path) error { ...@@ -32,13 +36,55 @@ func (rt *RoutingTable) AddPath(pfx net.Prefix, p *route.Path) error {
return nil return nil
} }
// ReplacePath replaces all paths for prefix `pfx` with path `p`
func (rt *RoutingTable) ReplacePath(pfx net.Prefix, p *route.Path) []*route.Path {
rt.mu.Lock()
defer rt.mu.Unlock()
r := rt.get(pfx)
if r == nil {
rt.addPath(pfx, p)
return nil
}
oldPaths := r.Paths()
rt.removePaths(pfx, oldPaths)
rt.addPath(pfx, p)
return oldPaths
}
// RemovePath removes a path from the trie // RemovePath removes a path from the trie
func (rt *RoutingTable) RemovePath(pfx net.Prefix, p *route.Path) error { func (rt *RoutingTable) RemovePath(pfx net.Prefix, p *route.Path) bool {
rt.mu.Lock() rt.mu.Lock()
defer rt.mu.Unlock() defer rt.mu.Unlock()
rt.root.removePath(pfx, p) return rt.removePath(pfx, p)
return nil }
func (rt *RoutingTable) removePath(pfx net.Prefix, p *route.Path) bool {
return rt.root.removePath(pfx, p)
}
func (rt *RoutingTable) removePaths(pfx net.Prefix, paths []*route.Path) {
for _, p := range paths {
rt.removePath(pfx, p)
}
}
// RemovePfx removes all paths for prefix `pfx`
func (rt *RoutingTable) RemovePfx(pfx net.Prefix) []*route.Path {
rt.mu.Lock()
defer rt.mu.Unlock()
r := rt.get(pfx)
if r == nil {
return nil
}
oldPaths := r.Paths()
rt.removePaths(pfx, oldPaths)
return oldPaths
} }
// LPM performs a longest prefix match for pfx on lpm // LPM performs a longest prefix match for pfx on lpm
...@@ -59,6 +105,10 @@ func (rt *RoutingTable) Get(pfx net.Prefix) *route.Route { ...@@ -59,6 +105,10 @@ func (rt *RoutingTable) Get(pfx net.Prefix) *route.Route {
rt.mu.RLock() rt.mu.RLock()
defer rt.mu.RUnlock() defer rt.mu.RUnlock()
return rt.get(pfx)
}
func (rt *RoutingTable) get(pfx net.Prefix) *route.Route {
if rt.root == nil { if rt.root == nil {
return nil return nil
} }
......
...@@ -27,9 +27,9 @@ func newNode(pfx net.Prefix, path *route.Path, skip uint8, dummy bool) *node { ...@@ -27,9 +27,9 @@ func newNode(pfx net.Prefix, path *route.Path, skip uint8, dummy bool) *node {
return n return n
} }
func (n *node) removePath(pfx net.Prefix, p *route.Path) { func (n *node) removePath(pfx net.Prefix, p *route.Path) (success bool) {
if n == nil { if n == nil {
return return false
} }
if n.route.Prefix() == pfx { if n.route.Prefix() == pfx {
...@@ -37,22 +37,21 @@ func (n *node) removePath(pfx net.Prefix, p *route.Path) { ...@@ -37,22 +37,21 @@ func (n *node) removePath(pfx net.Prefix, p *route.Path) {
return return
} }
n.route.RemovePath(p) nPaths := len(n.route.Paths())
nPathsAfterDel := n.route.RemovePath(p)
if len(n.route.Paths()) == 0 { if len(n.route.Paths()) == 0 {
// FIXME: Can this node actually be removed from the trie entirely? // FIXME: Can this node actually be removed from the trie entirely?
n.dummy = true n.dummy = true
} }
return return nPathsAfterDel < nPaths
} }
b := getBitUint32(pfx.Addr(), n.route.Pfxlen()+1) b := getBitUint32(pfx.Addr(), n.route.Pfxlen()+1)
if !b { if !b {
n.l.removePath(pfx, p) return n.l.removePath(pfx, p)
return
} }
n.h.removePath(pfx, p) return n.h.removePath(pfx, p)
return
} }
func (n *node) lpm(needle net.Prefix, res *[]*route.Route) { func (n *node) lpm(needle net.Prefix, res *[]*route.Route) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment