From ef9400a00149855505de10ec218c07e1fb46203b Mon Sep 17 00:00:00 2001
From: Daniel Czerwonk <daniel@dan-nrw.de>
Date: Mon, 25 Jun 2018 21:21:15 +0200
Subject: [PATCH] as path refactoring

---
 protocols/bgp/packet/as_path.go         | 44 ++++++++++++
 protocols/bgp/packet/bgp.go             |  7 --
 protocols/bgp/packet/path_attributes.go | 93 -------------------------
 protocols/bgp/server/fsm_established.go |  4 +-
 protocols/bgp/server/update_helper.go   | 11 ++-
 route/{bgp.go => bgp_path.go}           | 35 +++++++---
 routingtable/adjRIBIn/adj_rib_in.go     |  6 +-
 routingtable/adjRIBOut/adj_rib_out.go   |  2 +-
 8 files changed, 78 insertions(+), 124 deletions(-)
 create mode 100644 protocols/bgp/packet/as_path.go
 rename route/{bgp.go => bgp_path.go} (84%)

diff --git a/protocols/bgp/packet/as_path.go b/protocols/bgp/packet/as_path.go
new file mode 100644
index 00000000..0cdfc96a
--- /dev/null
+++ b/protocols/bgp/packet/as_path.go
@@ -0,0 +1,44 @@
+package packet
+
+import "fmt"
+
+type ASPath []ASPathSegment
+
+type ASPathSegment struct {
+	Type  uint8
+	Count uint8
+	ASNs  []uint32
+}
+
+func (pa ASPath) String() (ret string) {
+	for _, p := range pa {
+		if p.Type == ASSet {
+			ret += " ("
+		}
+		n := len(p.ASNs)
+		for i, asn := range p.ASNs {
+			if i < n-1 {
+				ret += fmt.Sprintf("%d ", asn)
+				continue
+			}
+			ret += fmt.Sprintf("%d", asn)
+		}
+		if p.Type == ASSet {
+			ret += ")"
+		}
+	}
+
+	return
+}
+
+func (pa ASPath) Length() (ret uint16) {
+	for _, p := range pa {
+		if p.Type == ASSet {
+			ret++
+			continue
+		}
+		ret += uint16(len(p.ASNs))
+	}
+
+	return
+}
diff --git a/protocols/bgp/packet/bgp.go b/protocols/bgp/packet/bgp.go
index 8082f5dd..8aabec7f 100644
--- a/protocols/bgp/packet/bgp.go
+++ b/protocols/bgp/packet/bgp.go
@@ -173,13 +173,6 @@ type NLRIAddPath struct {
 	Next           *NLRIAddPath
 }
 
-type ASPath []ASPathSegment
-type ASPathSegment struct {
-	Type  uint8
-	Count uint8
-	ASNs  []uint32
-}
-
 type Aggretator struct {
 	Addr uint32
 	ASN  uint16
diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go
index a859f4a0..fa5e9f0e 100644
--- a/protocols/bgp/packet/path_attributes.go
+++ b/protocols/bgp/packet/path_attributes.go
@@ -3,7 +3,6 @@ package packet
 import (
 	"bytes"
 	"fmt"
-	"strconv"
 	"strings"
 
 	"github.com/taktv6/tflow2/convert"
@@ -344,39 +343,6 @@ func (pa *PathAttribute) setLength(buf *bytes.Buffer) (int, error) {
 	return bytesRead, nil
 }
 
-func (pa *PathAttribute) ASPathString() (ret string) {
-	for _, p := range pa.Value.(ASPath) {
-		if p.Type == ASSet {
-			ret += " ("
-		}
-		n := len(p.ASNs)
-		for i, asn := range p.ASNs {
-			if i < n-1 {
-				ret += fmt.Sprintf("%d ", asn)
-				continue
-			}
-			ret += fmt.Sprintf("%d", asn)
-		}
-		if p.Type == ASSet {
-			ret += ")"
-		}
-	}
-
-	return
-}
-
-func (pa *PathAttribute) ASPathLen() (ret uint16) {
-	for _, p := range pa.Value.(ASPath) {
-		if p.Type == ASSet {
-			ret++
-			continue
-		}
-		ret += uint16(len(p.ASNs))
-	}
-
-	return
-}
-
 func (a *PathAttribute) CommunityString() string {
 	s := ""
 	for _, com := range a.Value.([]uint32) {
@@ -587,57 +553,6 @@ func (pa *PathAttribute) serializeLargeCommunities(buf *bytes.Buffer) uint8 {
 	return length
 }
 
-// ParseASPathStr converts an AS path from string representation info an PathAttribute object
-func ParseASPathStr(asPathString string) (*PathAttribute, error) {
-	asPath := ASPath{}
-
-	currentType := ASSequence
-	newSegmentNeeded := true
-	currentSegment := -1
-	for _, asn := range strings.Split(asPathString, " ") {
-		if asn == "" {
-			continue
-		}
-
-		if isBeginOfASSet(asn) {
-			currentType = ASSet
-			newSegmentNeeded = true
-			asn = strings.Replace(asn, "(", "", 1)
-		}
-
-		if newSegmentNeeded {
-			seg := ASPathSegment{
-				Type: uint8(currentType),
-				ASNs: make([]uint32, 0),
-			}
-			asPath = append(asPath, seg)
-			currentSegment++
-			newSegmentNeeded = false
-		}
-
-		if isEndOfASSset(asn) {
-			currentType = ASSequence
-			newSegmentNeeded = true
-			asn = strings.Replace(asn, ")", "", 1)
-		}
-
-		numericASN, err := strconv.Atoi(asn)
-		if err != nil {
-			return nil, fmt.Errorf("Unable to convert ASN: %v", err)
-		}
-		asPath[currentSegment].ASNs = append(asPath[currentSegment].ASNs, uint32(numericASN))
-
-		if len(asPath[currentSegment].ASNs) == MaxASNsSegment {
-			newSegmentNeeded = true
-		}
-	}
-
-	return &PathAttribute{
-		TypeCode: ASPathAttr,
-		Value:    asPath,
-	}, nil
-}
-
 func LargeCommunityAttributeForString(s string) (*PathAttribute, error) {
 	strs := strings.Split(s, " ")
 	coms := make([]LargeCommunity, len(strs))
@@ -656,14 +571,6 @@ func LargeCommunityAttributeForString(s string) (*PathAttribute, error) {
 	}, nil
 }
 
-func isBeginOfASSet(asPathPart string) bool {
-	return strings.Contains(asPathPart, "(")
-}
-
-func isEndOfASSset(asPathPart string) bool {
-	return strings.Contains(asPathPart, ")")
-}
-
 func fourBytesToUint32(address [4]byte) uint32 {
 	return uint32(address[0])<<24 + uint32(address[1])<<16 + uint32(address[2])<<8 + uint32(address[3])
 }
diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go
index c1507eb5..9ce739ec 100644
--- a/protocols/bgp/server/fsm_established.go
+++ b/protocols/bgp/server/fsm_established.go
@@ -230,8 +230,8 @@ func (s *establishedState) updates(u *packet.BGPUpdate) {
 			case packet.NextHopAttr:
 				path.BGPPath.NextHop = pa.Value.(uint32)
 			case packet.ASPathAttr:
-				path.BGPPath.ASPath = pa.ASPathString()
-				path.BGPPath.ASPathLen = pa.ASPathLen()
+				path.BGPPath.ASPath = pa.Value.(packet.ASPath)
+				path.BGPPath.ASPathLen = path.BGPPath.ASPath.Length()
 			case packet.CommunitiesAttr:
 				path.BGPPath.Communities = pa.Value.([]uint32)
 			case packet.LargeCommunitiesAttr:
diff --git a/protocols/bgp/server/update_helper.go b/protocols/bgp/server/update_helper.go
index 393df4e9..2f46e811 100644
--- a/protocols/bgp/server/update_helper.go
+++ b/protocols/bgp/server/update_helper.go
@@ -3,7 +3,6 @@ package server
 import (
 	"fmt"
 	"io"
-	"strings"
 
 	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/bio-routing/bio-rd/route"
@@ -11,22 +10,22 @@ import (
 )
 
 func pathAttribues(p *route.Path) (*packet.PathAttribute, error) {
-	asPathPA, err := packet.ParseASPathStr(strings.TrimRight(p.BGPPath.ASPath, " "))
-	if err != nil {
-		return nil, fmt.Errorf("Unable to parse AS path: %v", err)
+	asPath := &packet.PathAttribute{
+		TypeCode: packet.ASPathAttr,
+		Value:    p.BGPPath.ASPath,
 	}
 
 	origin := &packet.PathAttribute{
 		TypeCode: packet.OriginAttr,
 		Value:    p.BGPPath.Origin,
-		Next:     asPathPA,
 	}
+	asPath.Next = origin
 
 	nextHop := &packet.PathAttribute{
 		TypeCode: packet.NextHopAttr,
 		Value:    p.BGPPath.NextHop,
 	}
-	asPathPA.Next = nextHop
+	origin.Next = nextHop
 
 	localPref := &packet.PathAttribute{
 		TypeCode: packet.LocalPrefAttr,
diff --git a/route/bgp.go b/route/bgp_path.go
similarity index 84%
rename from route/bgp.go
rename to route/bgp_path.go
index 3beb6b56..1222487b 100644
--- a/route/bgp.go
+++ b/route/bgp_path.go
@@ -3,9 +3,9 @@ package route
 import (
 	"crypto/sha256"
 	"fmt"
-	"strconv"
 	"strings"
 
+	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/taktv6/tflow2/convert"
 )
 
@@ -14,7 +14,7 @@ type BGPPath struct {
 	PathIdentifier   uint32
 	NextHop          uint32
 	LocalPref        uint32
-	ASPath           string
+	ASPath           packet.ASPath
 	ASPathLen        uint16
 	Origin           uint8
 	MED              uint32
@@ -155,7 +155,7 @@ func (b *BGPPath) Print() string {
 	}
 	ret := fmt.Sprintf("\t\tLocal Pref: %d\n", b.LocalPref)
 	ret += fmt.Sprintf("\t\tOrigin: %s\n", origin)
-	ret += fmt.Sprintf("\t\tAS Path: %s\n", b.ASPath)
+	ret += fmt.Sprintf("\t\tAS Path: %s\n")
 	nh := uint32To4Byte(b.NextHop)
 	ret += fmt.Sprintf("\t\tNEXT HOP: %d.%d.%d.%d\n", nh[0], nh[1], nh[2], nh[3])
 	ret += fmt.Sprintf("\t\tMED: %d\n", b.MED)
@@ -172,16 +172,31 @@ func (b *BGPPath) Prepend(asn uint32, times uint16) {
 		return
 	}
 
-	asnStr := strconv.FormatUint(uint64(asn), 10)
+	first := b.ASPath[0]
+	if len(b.ASPath) == 0 || first.Type == packet.ASSet {
+		b.insertNewASSequence()
+	}
+
+	for i := 0; i < int(times); i++ {
+		if len(b.ASPath) == packet.MaxASNsSegment {
+			b.insertNewASSequence()
+		}
+
+	}
+
+	b.ASPathLen = b.ASPath.Length()
+}
 
-	path := make([]string, times+1)
-	for i := 0; uint16(i) < times; i++ {
-		path[i] = asnStr
+func (b *BGPPath) insertNewASSequence() packet.ASPath {
+	pa := make(packet.ASPath, len(b.ASPath)+1)
+	copy(pa[1:], b.ASPath)
+	pa[0] = packet.ASPathSegment{
+		ASNs:  make([]uint32, 0),
+		Count: 0,
+		Type:  packet.ASSequence,
 	}
-	path[times] = b.ASPath
 
-	b.ASPath = strings.TrimSuffix(strings.Join(path, " "), " ")
-	b.ASPathLen = b.ASPathLen + times
+	return pa
 }
 
 func (p *BGPPath) Copy() *BGPPath {
diff --git a/routingtable/adjRIBIn/adj_rib_in.go b/routingtable/adjRIBIn/adj_rib_in.go
index 2d850024..2cf454be 100644
--- a/routingtable/adjRIBIn/adj_rib_in.go
+++ b/routingtable/adjRIBIn/adj_rib_in.go
@@ -3,7 +3,6 @@ package adjRIBIn
 import (
 	"sync"
 
-	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/bio-routing/bio-rd/routingtable/filter"
 
 	"github.com/bio-routing/bio-rd/net"
@@ -80,10 +79,7 @@ func (a *AdjRIBIn) AddPath(pfx net.Prefix, p *route.Path) error {
 }
 
 func (a *AdjRIBIn) ourASNsInPath(p *route.Path) bool {
-	// Don't accept path via iBGP which contain our ASN
-	ASPathAttr, _ := packet.ParseASPathStr(p.BGPPath.ASPath)
-
-	for _, pathSegment := range ASPathAttr.Value.(packet.ASPath) {
+	for _, pathSegment := range p.BGPPath.ASPath {
 		for _, asn := range pathSegment.ASNs {
 			if a.contributingASNs.IsContributingASN(asn) {
 				return true
diff --git a/routingtable/adjRIBOut/adj_rib_out.go b/routingtable/adjRIBOut/adj_rib_out.go
index 19b44b8b..088756dc 100644
--- a/routingtable/adjRIBOut/adj_rib_out.go
+++ b/routingtable/adjRIBOut/adj_rib_out.go
@@ -52,7 +52,7 @@ func (a *AdjRIBOut) AddPath(pfx bnet.Prefix, p *route.Path) error {
 
 	p = p.Copy()
 	if !a.neighbor.IBGP && !a.neighbor.RouteServerClient {
-		p.BGPPath.ASPath = fmt.Sprintf("%d %s", a.neighbor.LocalASN, p.BGPPath.ASPath)
+		p.BGPPath.Prepend(a.neighbor.LocalASN, 1)
 	}
 
 	if !a.neighbor.IBGP && !a.neighbor.RouteServerClient {
-- 
GitLab