From 6d0d613ca3cbfd543092c05dd0ba6c0809a309f7 Mon Sep 17 00:00:00 2001
From: Maximilian Wilhelm <max@sdn.clinic>
Date: Sat, 23 Jun 2018 23:30:29 +0200
Subject: [PATCH] Add tracking of contributing ASNs to a LocRIB.

  The tracked contributing ASNs of a LocRIB will be used in detecting loops
  within AS paths.

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
---
 protocols/bgp/server/fsm.go             |  3 +-
 protocols/bgp/server/fsm_established.go |  6 ++-
 protocols/bgp/server/peer.go            |  6 ++-
 protocols/bgp/server/server.go          |  7 +--
 routingtable/adjRIBIn/adj_rib_in.go     |  2 +-
 routingtable/contributing_asn_list.go   | 64 +++++++++++++++++++++++++
 routingtable/locRIB/loc_rib.go          | 13 +++--
 7 files changed, 90 insertions(+), 11 deletions(-)
 create mode 100644 routingtable/contributing_asn_list.go

diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go
index 33e000d1..0c522139 100644
--- a/protocols/bgp/server/fsm.go
+++ b/protocols/bgp/server/fsm.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/bio-routing/bio-rd/routingtable"
+	"github.com/bio-routing/bio-rd/routingtable/locRIB"
 	log "github.com/sirupsen/logrus"
 )
 
@@ -61,7 +62,7 @@ type FSM struct {
 	ribsInitialized bool
 	adjRIBIn        routingtable.RouteTableClient
 	adjRIBOut       routingtable.RouteTableClient
-	rib             routingtable.RouteTableClient
+	rib             *locRIB.LocRIB
 	updateSender    routingtable.RouteTableClient
 
 	neighborID uint32
diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go
index 80ff7fa7..58facab0 100644
--- a/protocols/bgp/server/fsm_established.go
+++ b/protocols/bgp/server/fsm_established.go
@@ -55,7 +55,10 @@ func (s establishedState) run() (state, string) {
 }
 
 func (s *establishedState) init() error {
-	s.fsm.adjRIBIn = adjRIBIn.New(s.fsm.peer.importFilter)
+	contributingASNs := s.fsm.rib.GetContributingASNs()
+
+	s.fsm.adjRIBIn = adjRIBIn.New(s.fsm.peer.importFilter, contributingASNs)
+	contributingASNs.Add(s.fsm.peer.localASN)
 	s.fsm.adjRIBIn.Register(s.fsm.rib)
 
 	host, _, err := net.SplitHostPort(s.fsm.con.LocalAddr().String())
@@ -96,6 +99,7 @@ func (s *establishedState) init() error {
 }
 
 func (s *establishedState) uninit() {
+	s.fsm.rib.GetContributingASNs().Remove(s.fsm.peer.localASN)
 	s.fsm.adjRIBIn.Unregister(s.fsm.rib)
 	s.fsm.rib.Unregister(s.fsm.adjRIBOut)
 	s.fsm.adjRIBOut.Unregister(s.fsm.updateSender)
diff --git a/protocols/bgp/server/peer.go b/protocols/bgp/server/peer.go
index bad26828..3e5e6aee 100644
--- a/protocols/bgp/server/peer.go
+++ b/protocols/bgp/server/peer.go
@@ -5,6 +5,8 @@ import (
 	"sync"
 	"time"
 
+	"github.com/bio-routing/bio-rd/routingtable/locRIB"
+
 	"github.com/bio-routing/bio-rd/config"
 	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/bio-routing/bio-rd/routingtable"
@@ -27,7 +29,7 @@ type peer struct {
 	fsms   []*FSM
 	fsmsMu sync.Mutex
 
-	rib               routingtable.RouteTableClient
+	rib               *locRIB.LocRIB
 	routerID          uint32
 	addPathSend       routingtable.ClientOptions
 	addPathRecv       bool
@@ -100,7 +102,7 @@ func isEstablishedState(s state) bool {
 
 // NewPeer creates a new peer with the given config. If an connection is established, the adjRIBIN of the peer is connected
 // to the given rib. To actually connect the peer, call Start() on the returned peer.
-func newPeer(c config.Peer, rib routingtable.RouteTableClient, server *bgpServer) (*peer, error) {
+func newPeer(c config.Peer, rib *locRIB.LocRIB, server *bgpServer) (*peer, error) {
 	if c.LocalAS == 0 {
 		c.LocalAS = server.localASN
 	}
diff --git a/protocols/bgp/server/server.go b/protocols/bgp/server/server.go
index 61c79de1..f0354f7e 100644
--- a/protocols/bgp/server/server.go
+++ b/protocols/bgp/server/server.go
@@ -7,9 +7,10 @@ import (
 	"strings"
 	"sync"
 
+	"github.com/bio-routing/bio-rd/routingtable/locRIB"
+
 	"github.com/bio-routing/bio-rd/config"
 	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
-	"github.com/bio-routing/bio-rd/routingtable"
 	log "github.com/sirupsen/logrus"
 )
 
@@ -29,7 +30,7 @@ type bgpServer struct {
 type BGPServer interface {
 	RouterID() uint32
 	Start(*config.Global) error
-	AddPeer(config.Peer, routingtable.RouteTableClient) error
+	AddPeer(config.Peer, *locRIB.LocRIB) error
 	GetPeerInfoAll() map[string]PeerInfo
 }
 
@@ -113,7 +114,7 @@ func (b *bgpServer) incomingConnectionWorker() {
 	}
 }
 
-func (b *bgpServer) AddPeer(c config.Peer, rib routingtable.RouteTableClient) error {
+func (b *bgpServer) AddPeer(c config.Peer, rib *locRIB.LocRIB) error {
 	if c.LocalAS > uint16max || c.PeerAS > uint16max {
 		return fmt.Errorf("32bit ASNs are not supported yet")
 	}
diff --git a/routingtable/adjRIBIn/adj_rib_in.go b/routingtable/adjRIBIn/adj_rib_in.go
index ca9d0bb0..81416d9a 100644
--- a/routingtable/adjRIBIn/adj_rib_in.go
+++ b/routingtable/adjRIBIn/adj_rib_in.go
@@ -20,7 +20,7 @@ type AdjRIBIn struct {
 }
 
 // New creates a new Adjacency RIB In
-func New(exportFilter *filter.Filter) *AdjRIBIn {
+func New(exportFilter *filter.Filter, contributingASNs *routingtable.ContributingASNs) *AdjRIBIn {
 	a := &AdjRIBIn{
 		rt:           routingtable.NewRoutingTable(),
 		exportFilter: exportFilter,
diff --git a/routingtable/contributing_asn_list.go b/routingtable/contributing_asn_list.go
new file mode 100644
index 00000000..0b0efca2
--- /dev/null
+++ b/routingtable/contributing_asn_list.go
@@ -0,0 +1,64 @@
+package routingtable
+
+import (
+	"sync"
+)
+
+type contributingASN struct {
+	asn   uint32
+	count uint32
+}
+
+// ContributingASNs contains a list of contributing ASN to a LocRIB to check ASPaths for possible routing loops.
+type ContributingASNs struct {
+	contributingASNs []contributingASN
+	mu               sync.RWMutex
+}
+
+// NewContributingASNs creates a list of contributing ASNs to a LocRIB for routing loop prevention.
+func NewContributingASNs() *ContributingASNs {
+	c := &ContributingASNs{
+		contributingASNs: []contributingASN{},
+	}
+
+	return c
+}
+
+// Add a new ASN to the list of contributing ASNs or add the ref count of an existing one.
+func (c *ContributingASNs) Add(asn uint32) {
+	c.mu.RLock()
+	defer c.mu.RUnlock()
+
+	for _, cASN := range c.contributingASNs {
+		if cASN.asn == asn {
+			cASN.count++
+			return
+		}
+	}
+
+	c.contributingASNs = append(c.contributingASNs, contributingASN{
+		asn:   asn,
+		count: 1,
+	})
+}
+
+// Remove a ASN to the list of contributing ASNs or decrement the ref count of an existing one.
+func (c *ContributingASNs) Remove(asn uint32) {
+	c.mu.RLock()
+	defer c.mu.RUnlock()
+
+	asnList := c.contributingASNs
+
+	for i, cASN := range asnList {
+		if cASN.asn == asn {
+			cASN.count--
+
+			if cASN.count == 0 {
+				copy(asnList[i:], asnList[i+1:])
+				asnList = asnList[:len(asnList)]
+				c.contributingASNs = asnList[:len(asnList)-1]
+			}
+			return
+		}
+	}
+}
diff --git a/routingtable/locRIB/loc_rib.go b/routingtable/locRIB/loc_rib.go
index ff5750c3..a8ddf1e6 100644
--- a/routingtable/locRIB/loc_rib.go
+++ b/routingtable/locRIB/loc_rib.go
@@ -14,19 +14,26 @@ import (
 // LocRIB represents a routing information base
 type LocRIB struct {
 	routingtable.ClientManager
-	rt *routingtable.RoutingTable
-	mu sync.RWMutex
+	rt               *routingtable.RoutingTable
+	mu               sync.RWMutex
+	contributingASNs *routingtable.ContributingASNs
 }
 
 // New creates a new routing information base
 func New() *LocRIB {
 	a := &LocRIB{
-		rt: routingtable.NewRoutingTable(),
+		rt:               routingtable.NewRoutingTable(),
+		contributingASNs: routingtable.NewContributingASNs(),
 	}
 	a.ClientManager = routingtable.NewClientManager(a)
 	return a
 }
 
+// GetContributingASNs returns a pointer to the list of contributing ASNs
+func (a *LocRIB) GetContributingASNs() *routingtable.ContributingASNs {
+	return a.contributingASNs
+}
+
 // UpdateNewClient sends current state to a new client
 func (a *LocRIB) UpdateNewClient(client routingtable.RouteTableClient) error {
 	a.mu.RLock()
-- 
GitLab