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