From 4715900ca115beda056023c71a637a8e990126e2 Mon Sep 17 00:00:00 2001 From: Oliver Herms <oliver.herms@exaring.de> Date: Sat, 23 Jun 2018 13:43:32 +0200 Subject: [PATCH] Adding moved files --- .../adjRIBOut/adj_rib_out_add_path.go | 144 ++++++++++++++++++ routingtable/adjRIBOut/path_id_manager.go | 47 ++++++ .../adjRIBOut/path_id_manager_test.go | 109 +++++++++++++ 3 files changed, 300 insertions(+) create mode 100644 routingtable/adjRIBOut/adj_rib_out_add_path.go create mode 100644 routingtable/adjRIBOut/path_id_manager.go create mode 100644 routingtable/adjRIBOut/path_id_manager_test.go diff --git a/routingtable/adjRIBOut/adj_rib_out_add_path.go b/routingtable/adjRIBOut/adj_rib_out_add_path.go new file mode 100644 index 00000000..44661441 --- /dev/null +++ b/routingtable/adjRIBOut/adj_rib_out_add_path.go @@ -0,0 +1,144 @@ +package adjRIBOut + +import ( + "fmt" + "sync" + + "github.com/bio-routing/bio-rd/route" + "github.com/bio-routing/bio-rd/routingtable" + "github.com/bio-routing/bio-rd/routingtable/filter" + + bnet "github.com/bio-routing/bio-rd/net" + log "github.com/sirupsen/logrus" +) + +// AdjRIBOut represents an Adjacency RIB Out with BGP add path +type AdjRIBOut struct { + routingtable.ClientManager + rt *routingtable.RoutingTable + neighbor *routingtable.Neighbor + pathIDManager *pathIDManager + mu sync.RWMutex + exportFilter *filter.Filter +} + +// New creates a new Adjacency RIB Out with BGP add path +func New(neighbor *routingtable.Neighbor, exportFilter *filter.Filter) *AdjRIBOut { + a := &AdjRIBOut{ + rt: routingtable.NewRoutingTable(), + neighbor: neighbor, + pathIDManager: newPathIDManager(), + exportFilter: exportFilter, + } + a.ClientManager = routingtable.NewClientManager(a) + return a +} + +// UpdateNewClient sends current state to a new client +func (a *AdjRIBOut) UpdateNewClient(client routingtable.RouteTableClient) error { + return nil +} + +// AddPath adds path p to prefix `pfx` +func (a *AdjRIBOut) AddPath(pfx bnet.Prefix, p *route.Path) error { + if !routingtable.ShouldPropagateUpdate(pfx, p, a.neighbor) { + return nil + } + + p = p.Copy() + if !a.neighbor.IBGP && !a.neighbor.RouteServerClient { + p.BGPPath.ASPath = fmt.Sprintf("%d %s", a.neighbor.LocalASN, p.BGPPath.ASPath) + } + + if !a.neighbor.IBGP && !a.neighbor.RouteServerClient { + p.BGPPath.NextHop = a.neighbor.LocalAddress + } + + p, reject := a.exportFilter.ProcessTerms(pfx, p) + if reject { + return nil + } + + a.mu.Lock() + defer a.mu.Unlock() + + if !a.neighbor.CapAddPathRX { + oldPaths := a.rt.ReplacePath(pfx, p) + a.removePathsFromClients(pfx, oldPaths) + } + + pathID, err := a.pathIDManager.getNewID() + if err != nil { + return fmt.Errorf("Unable to get path ID: %v", err) + } + + p.BGPPath.PathIdentifier = pathID + a.rt.AddPath(pfx, p) + + for _, client := range a.ClientManager.Clients() { + err := client.AddPath(pfx, p) + if err != nil { + log.WithField("Sender", "AdjRIBOutAddPath").WithError(err).Error("Could not send update to client") + } + } + return nil +} + +// RemovePath removes the path for prefix `pfx` +func (a *AdjRIBOut) RemovePath(pfx bnet.Prefix, p *route.Path) bool { + if !routingtable.ShouldPropagateUpdate(pfx, p, a.neighbor) { + return false + } + + a.mu.Lock() + defer a.mu.Unlock() + + r := a.rt.Get(pfx) + if r == nil { + return false + } + + a.rt.RemovePath(pfx, p) + a.pathIDManager.releaseID(p.BGPPath.PathIdentifier) + a.removePathFromClients(pfx, p) + return true +} + +func (a *AdjRIBOut) isOwnPath(p *route.Path) bool { + if p.Type != a.neighbor.Type { + return false + } + + switch p.Type { + case route.BGPPathType: + return p.BGPPath.Source == a.neighbor.Address + } + + return false +} + +func (a *AdjRIBOut) removePathsFromClients(pfx bnet.Prefix, paths []*route.Path) { + for _, p := range paths { + a.removePathFromClients(pfx, p) + } +} + +func (a *AdjRIBOut) removePathFromClients(pfx bnet.Prefix, path *route.Path) { + for _, client := range a.ClientManager.Clients() { + client.RemovePath(pfx, path) + } +} + +// Print dumps all prefixes in the Adj-RIB +func (a *AdjRIBOut) Print() string { + a.mu.RLock() + defer a.mu.RUnlock() + + ret := fmt.Sprintf("DUMPING ADJ-RIB-OUT:\n") + routes := a.rt.Dump() + for _, r := range routes { + ret += fmt.Sprintf("%s\n", r.Prefix().String()) + } + + return ret +} diff --git a/routingtable/adjRIBOut/path_id_manager.go b/routingtable/adjRIBOut/path_id_manager.go new file mode 100644 index 00000000..53ab2a98 --- /dev/null +++ b/routingtable/adjRIBOut/path_id_manager.go @@ -0,0 +1,47 @@ +package adjRIBOut + +import ( + "fmt" +) + +var maxUint32 = ^uint32(0) + +// pathIDManager manages BGP path identifiers for add-path. This is no thread safe (and doesn't need to be). +type pathIDManager struct { + ids map[uint32]struct{} + last uint32 + used uint32 +} + +func newPathIDManager() *pathIDManager { + return &pathIDManager{ + ids: make(map[uint32]struct{}), + } +} + +func (fm *pathIDManager) getNewID() (uint32, error) { + if fm.used == maxUint32 { + return 0, fmt.Errorf("Out of path IDs") + } + + fm.last++ + for { + if _, exists := fm.ids[fm.last]; exists { + fm.last++ + continue + } + break + } + + ret := fm.last + fm.used++ + + return ret, nil +} + +func (fm *pathIDManager) releaseID(id uint32) { + if _, exists := fm.ids[id]; exists { + delete(fm.ids, id) + fm.used-- + } +} diff --git a/routingtable/adjRIBOut/path_id_manager_test.go b/routingtable/adjRIBOut/path_id_manager_test.go new file mode 100644 index 00000000..0b4ec577 --- /dev/null +++ b/routingtable/adjRIBOut/path_id_manager_test.go @@ -0,0 +1,109 @@ +package adjRIBOut + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetNewID(t *testing.T) { + tests := []struct { + name string + maxIDs uint32 + count int + wantFail bool + }{ + { + name: "Out of path IDs", + maxIDs: 10, + count: 11, + wantFail: true, + }, + { + name: "Success", + maxIDs: 10, + count: 10, + wantFail: false, + }, + } + +X: + for _, test := range tests { + maxUint32 = test.maxIDs + m := newPathIDManager() + for i := 0; i < test.count; i++ { + _, err := m.getNewID() + if err != nil { + if test.wantFail { + continue X + } + + t.Errorf("Unexpected failure for test %q: %v", test.name, err) + continue X + } + } + + if test.wantFail { + t.Errorf("Unexpected success for test %q", test.name) + continue + } + } +} + +func TestReleaseID(t *testing.T) { + tests := []struct { + name string + pm *pathIDManager + release uint32 + expected *pathIDManager + }{ + { + name: "Release existent", + pm: &pathIDManager{ + ids: map[uint32]struct{}{ + 0: struct{}{}, + 1: struct{}{}, + 2: struct{}{}, + }, + last: 2, + used: 3, + }, + release: 1, + expected: &pathIDManager{ + ids: map[uint32]struct{}{ + 0: struct{}{}, + 2: struct{}{}, + }, + last: 2, + used: 2, + }, + }, + { + name: "Release non-existent", + pm: &pathIDManager{ + ids: map[uint32]struct{}{ + 0: struct{}{}, + 1: struct{}{}, + 2: struct{}{}, + }, + last: 2, + used: 3, + }, + release: 3, + expected: &pathIDManager{ + ids: map[uint32]struct{}{ + 0: struct{}{}, + 1: struct{}{}, + 2: struct{}{}, + }, + last: 2, + used: 3, + }, + }, + } + + for _, test := range tests { + test.pm.releaseID(test.release) + assert.Equalf(t, test.expected, test.pm, "%s", test.name) + } +} -- GitLab