From 9520337496ed0b9784361d4119aef81647c78ef2 Mon Sep 17 00:00:00 2001
From: Oliver Herms <oliver.herms@exaring.de>
Date: Wed, 16 May 2018 14:10:34 +0200
Subject: [PATCH] Added addpath support

---
 config/peer.go                                |  2 +-
 main.go                                       |  7 +-
 protocols/bgp/packet/bgp.go                   | 16 ++++
 protocols/bgp/packet/decoder.go               | 39 ++++++--
 protocols/bgp/packet/encoder.go               | 68 +++++++++++++-
 protocols/bgp/packet/encoder_test.go          | 36 +++----
 protocols/bgp/packet/nlri.go                  | 13 +++
 protocols/bgp/packet/nlri_test.go             | 43 +++++++++
 protocols/bgp/packet/parameters.go            |  9 ++
 protocols/bgp/server/fsm.go                   | 94 ++++++++++++++++---
 protocols/bgp/server/peer.go                  |  4 +-
 protocols/bgp/server/update_sender.go         |  1 +
 .../bgp/server/update_sender_add_path.go      | 73 ++++++++++++++
 routingtable/adjRIBOut/adj_rib_out.go         |  5 +-
 .../adjRIBOutAddPath/adj_rib_out_add_path.go  |  9 +-
 routingtable/client_interface.go              |  2 +
 routingtable/locRIB/loc_rib.go                |  6 +-
 17 files changed, 374 insertions(+), 53 deletions(-)
 create mode 100644 protocols/bgp/server/update_sender_add_path.go

diff --git a/config/peer.go b/config/peer.go
index 355e0583..9e057b9d 100644
--- a/config/peer.go
+++ b/config/peer.go
@@ -17,5 +17,5 @@ type Peer struct {
 	Passive      bool
 	RouterID     uint32
 	AddPathSend  routingtable.ClientOptions
-	AddPathRecv  uint
+	AddPathRecv  bool
 }
diff --git a/main.go b/main.go
index ac5805fc..7e19a5aa 100644
--- a/main.go
+++ b/main.go
@@ -37,9 +37,12 @@ func main() {
 		KeepAlive:    30,
 		Passive:      true,
 		RouterID:     b.RouterID(),
+		AddPathSend: routingtable.ClientOptions{
+			MaxPaths: 10,
+		},
 	}, rib)
 
-	time.Sleep(time.Second * 30)
+	time.Sleep(time.Second * 15)
 
 	b.AddPeer(config.Peer{
 		AdminEnabled: true,
@@ -54,7 +57,7 @@ func main() {
 		AddPathSend: routingtable.ClientOptions{
 			MaxPaths: 10,
 		},
-		AddPathRecv: 1,
+		AddPathRecv: true,
 	}, rib)
 
 	go func() {
diff --git a/protocols/bgp/packet/bgp.go b/protocols/bgp/packet/bgp.go
index e6ac0e6b..41a2113f 100644
--- a/protocols/bgp/packet/bgp.go
+++ b/protocols/bgp/packet/bgp.go
@@ -4,6 +4,7 @@ const (
 	OctetLen       = 8
 	MaxASNsSegment = 255
 	BGP4Version    = 4
+	MinOpenLen     = 29
 
 	MarkerLen  = 16
 	HeaderLen  = 19
@@ -128,6 +129,14 @@ type BGPUpdate struct {
 	NLRI               *NLRI
 }
 
+type BGPUpdateAddPath struct {
+	WithdrawnRoutesLen uint16
+	WithdrawnRoutes    *NLRIAddPath
+	TotalPathAttrLen   uint16
+	PathAttributes     *PathAttribute
+	NLRI               *NLRIAddPath
+}
+
 type PathAttribute struct {
 	Length         uint16
 	Optional       bool
@@ -145,6 +154,13 @@ type NLRI struct {
 	Next   *NLRI
 }
 
+type NLRIAddPath struct {
+	PathIdentifier uint32
+	IP             uint32
+	Pfxlen         uint8
+	Next           *NLRIAddPath
+}
+
 type ASPath []ASPathSegment
 type ASPathSegment struct {
 	Type  uint8
diff --git a/protocols/bgp/packet/decoder.go b/protocols/bgp/packet/decoder.go
index c5274122..a0179812 100644
--- a/protocols/bgp/packet/decoder.go
+++ b/protocols/bgp/packet/decoder.go
@@ -130,6 +130,9 @@ func invalidErrCode(n *BGPNotification) (*BGPNotification, error) {
 
 func decodeOpenMsg(buf *bytes.Buffer) (*BGPOpen, error) {
 	msg, err := _decodeOpenMsg(buf)
+	if err != nil {
+		return nil, fmt.Errorf("Unable to decode OPEN message: %v", err)
+	}
 	return msg.(*BGPOpen), err
 }
 
@@ -182,15 +185,18 @@ func decodeOptParams(buf *bytes.Buffer, optParmLen uint8) ([]OptParam, error) {
 		fmt.Printf("Type: %d\n", o.Type)
 		switch o.Type {
 		case CapabilitiesParamType:
-			cap, err := decodeCapability(buf)
+			caps, err := decodeCapabilities(buf, o.Length)
 			if err != nil {
-				return nil, fmt.Errorf("Unable to decode capability: %v", err)
+				return nil, fmt.Errorf("Unable to decode capabilites: %v", err)
 			}
-			o.Value = cap
+
+			o.Value = caps
 			optParams = append(optParams, o)
-			read += cap.Length + 2
+			for _, cap := range caps {
+				read += cap.Length + 2
+			}
 		default:
-			return nil, fmt.Errorf("Unrecognized option")
+			return nil, fmt.Errorf("Unrecognized option: %d", o.Type)
 		}
 
 	}
@@ -198,6 +204,22 @@ func decodeOptParams(buf *bytes.Buffer, optParmLen uint8) ([]OptParam, error) {
 	return optParams, nil
 }
 
+func decodeCapabilities(buf *bytes.Buffer, length uint8) (Capabilities, error) {
+	ret := make(Capabilities, 0)
+	read := uint8(0)
+	for read < length {
+		cap, err := decodeCapability(buf)
+		if err != nil {
+			return nil, fmt.Errorf("Unable to decode capability: %v", err)
+		}
+
+		ret = append(ret, cap)
+		read += cap.Length + 2
+	}
+
+	return ret, nil
+}
+
 func decodeCapability(buf *bytes.Buffer) (Capability, error) {
 	cap := Capability{}
 	fields := []interface{}{
@@ -218,7 +240,12 @@ func decodeCapability(buf *bytes.Buffer) (Capability, error) {
 		}
 		cap.Value = addPathCap
 	default:
-		return cap, fmt.Errorf("Unknown capability: %d", cap.Code)
+		for i := uint8(0); i < cap.Length; i++ {
+			_, err := buf.ReadByte()
+			if err != nil {
+				return cap, fmt.Errorf("Read failed: %v", err)
+			}
+		}
 	}
 
 	return cap, nil
diff --git a/protocols/bgp/packet/encoder.go b/protocols/bgp/packet/encoder.go
index 3f44047a..ea87d1ff 100644
--- a/protocols/bgp/packet/encoder.go
+++ b/protocols/bgp/packet/encoder.go
@@ -26,7 +26,11 @@ func SerializeNotificationMsg(msg *BGPNotification) []byte {
 }
 
 func SerializeOpenMsg(msg *BGPOpen) []byte {
-	openLen := uint16(29)
+	optParmsBuf := bytes.NewBuffer(make([]byte, 0))
+	serializeOptParams(optParmsBuf, msg.OptParams)
+	optParms := optParmsBuf.Bytes()
+	openLen := uint16(len(optParms) + MinOpenLen)
+
 	buf := bytes.NewBuffer(make([]byte, 0, openLen))
 	serializeHeader(buf, openLen, OpenMsg)
 
@@ -35,9 +39,6 @@ func SerializeOpenMsg(msg *BGPOpen) []byte {
 	buf.Write(convert.Uint16Byte(msg.HoldTime))
 	buf.Write(convert.Uint32Byte(msg.BGPIdentifier))
 
-	optParmsBuf := bytes.NewBuffer(make([]byte, 0))
-	serializeOptParams(optParmsBuf, msg.OptParams)
-	optParms := optParmsBuf.Bytes()
 	buf.WriteByte(uint8(len(optParms)))
 	buf.Write(optParms)
 
@@ -62,6 +63,65 @@ func serializeHeader(buf *bytes.Buffer, length uint16, typ uint8) {
 	buf.WriteByte(typ)
 }
 
+func (b *BGPUpdateAddPath) SerializeUpdate() ([]byte, error) {
+	budget := MaxLen - MinLen
+	buf := bytes.NewBuffer(nil)
+
+	withdrawBuf := bytes.NewBuffer(nil)
+	for withdraw := b.WithdrawnRoutes; withdraw != nil; withdraw = withdraw.Next {
+		nlriLen := int(withdraw.serialize(withdrawBuf))
+		budget -= nlriLen
+		if budget < 0 {
+			return nil, fmt.Errorf("update too long")
+		}
+	}
+
+	pathAttributesBuf := bytes.NewBuffer(nil)
+	for pa := b.PathAttributes; pa != nil; pa = pa.Next {
+		paLen := int(pa.serialize(pathAttributesBuf))
+		budget -= paLen
+		if budget < 0 {
+			return nil, fmt.Errorf("update too long")
+		}
+	}
+
+	nlriBuf := bytes.NewBuffer(nil)
+	for nlri := b.NLRI; nlri != nil; nlri = nlri.Next {
+		nlriLen := int(nlri.serialize(nlriBuf))
+		budget -= nlriLen
+		if budget < 0 {
+			return nil, fmt.Errorf("update too long")
+		}
+	}
+
+	withdrawnRoutesLen := withdrawBuf.Len()
+	if withdrawnRoutesLen > 65535 {
+		return nil, fmt.Errorf("Invalid Withdrawn Routes Length: %d", withdrawnRoutesLen)
+	}
+
+	totalPathAttributesLen := pathAttributesBuf.Len()
+	if totalPathAttributesLen > 65535 {
+		return nil, fmt.Errorf("Invalid Total Path Attribute Length: %d", totalPathAttributesLen)
+	}
+
+	totalLength := 2 + withdrawnRoutesLen + totalPathAttributesLen + 2 + nlriBuf.Len() + 19
+	if totalLength > 4096 {
+		return nil, fmt.Errorf("Update too long: %d bytes", totalLength)
+	}
+
+	serializeHeader(buf, uint16(totalLength), UpdateMsg)
+
+	buf.Write(convert.Uint16Byte(uint16(withdrawnRoutesLen)))
+	buf.Write(withdrawBuf.Bytes())
+
+	buf.Write(convert.Uint16Byte(uint16(totalPathAttributesLen)))
+	buf.Write(pathAttributesBuf.Bytes())
+
+	buf.Write(nlriBuf.Bytes())
+
+	return buf.Bytes(), nil
+}
+
 func (b *BGPUpdate) SerializeUpdate() ([]byte, error) {
 	budget := MaxLen - MinLen
 	buf := bytes.NewBuffer(nil)
diff --git a/protocols/bgp/packet/encoder_test.go b/protocols/bgp/packet/encoder_test.go
index cc6968d9..0d4f70a3 100644
--- a/protocols/bgp/packet/encoder_test.go
+++ b/protocols/bgp/packet/encoder_test.go
@@ -94,7 +94,7 @@ func TestSerializeOpenMsg(t *testing.T) {
 	}
 }
 
-/*func TestSerializeOptParams(t *testing.T) {
+func TestSerializeOptParams(t *testing.T) {
 	tests := []struct {
 		name      string
 		optParams []OptParam
@@ -109,28 +109,20 @@ func TestSerializeOpenMsg(t *testing.T) {
 			name: "1 Option",
 			optParams: []OptParam{
 				{
-					Type:   100,
-					Length: 2,
-					Value:  []byte{1, 2},
+					Type:   2,
+					Length: 6,
+					Value: Capability{
+						Code:   69,
+						Length: 4,
+						Value: AddPathCapability{
+							AFI:         1,
+							SAFI:        1,
+							SendReceive: 3,
+						},
+					},
 				},
 			},
-			expected: []byte{100, 2, 1, 2},
-		},
-		{
-			name: "2 Options",
-			optParams: []OptParam{
-				{
-					Type:   100,
-					Length: 2,
-					Value:  []byte{1, 2},
-				},
-				{
-					Type:   200,
-					Length: 2,
-					Value:  []byte{3, 4},
-				},
-			},
-			expected: []byte{100, 2, 1, 2, 200, 2, 3, 4},
+			expected: []byte{2, 6, 69, 4, 0, 1, 1, 3},
 		},
 	}
 
@@ -139,7 +131,7 @@ func TestSerializeOpenMsg(t *testing.T) {
 		serializeOptParams(buf, test.optParams)
 		assert.Equal(t, test.expected, buf.Bytes())
 	}
-}*/
+}
 
 func TestSerializeHeader(t *testing.T) {
 	tests := []struct {
diff --git a/protocols/bgp/packet/nlri.go b/protocols/bgp/packet/nlri.go
index 05fecbff..6de92a6c 100644
--- a/protocols/bgp/packet/nlri.go
+++ b/protocols/bgp/packet/nlri.go
@@ -73,6 +73,19 @@ func (n *NLRI) serialize(buf *bytes.Buffer) uint8 {
 	return nBytes + 1
 }
 
+func (n *NLRIAddPath) serialize(buf *bytes.Buffer) uint8 {
+	a := convert.Uint32Byte(n.IP)
+
+	addr := [4]byte{a[0], a[1], a[2], a[3]}
+	nBytes := bytesInAddr(n.Pfxlen)
+
+	buf.Write(convert.Uint32Byte(n.PathIdentifier))
+	buf.WriteByte(n.Pfxlen)
+	buf.Write(addr[:nBytes])
+
+	return nBytes + 1
+}
+
 func bytesInAddr(pfxlen uint8) uint8 {
 	return uint8(math.Ceil(float64(pfxlen) / 8))
 }
diff --git a/protocols/bgp/packet/nlri_test.go b/protocols/bgp/packet/nlri_test.go
index b97509b8..17a17dd3 100644
--- a/protocols/bgp/packet/nlri_test.go
+++ b/protocols/bgp/packet/nlri_test.go
@@ -208,3 +208,46 @@ func TestNLRISerialize(t *testing.T) {
 		assert.Equal(t, test.expected, res)
 	}
 }
+
+func TestNLRIAddPathSerialize(t *testing.T) {
+	tests := []struct {
+		name     string
+		nlri     *NLRIAddPath
+		expected []byte
+	}{
+		{
+			name: "Test #1",
+			nlri: &NLRIAddPath{
+				PathIdentifier: 100,
+				IP:             strAddr("1.2.3.0"),
+				Pfxlen:         25,
+			},
+			expected: []byte{0, 0, 0, 100, 25, 1, 2, 3, 0},
+		},
+		{
+			name: "Test #2",
+			nlri: &NLRIAddPath{
+				PathIdentifier: 100,
+				IP:             strAddr("1.2.3.0"),
+				Pfxlen:         24,
+			},
+			expected: []byte{0, 0, 0, 100, 24, 1, 2, 3},
+		},
+		{
+			name: "Test #3",
+			nlri: &NLRIAddPath{
+				PathIdentifier: 100,
+				IP:             strAddr("100.200.128.0"),
+				Pfxlen:         17,
+			},
+			expected: []byte{0, 0, 0, 100, 17, 100, 200, 128},
+		},
+	}
+
+	for _, test := range tests {
+		buf := bytes.NewBuffer(nil)
+		test.nlri.serialize(buf)
+		res := buf.Bytes()
+		assert.Equal(t, test.expected, res)
+	}
+}
diff --git a/protocols/bgp/packet/parameters.go b/protocols/bgp/packet/parameters.go
index 543bc447..8edf42fc 100644
--- a/protocols/bgp/packet/parameters.go
+++ b/protocols/bgp/packet/parameters.go
@@ -16,12 +16,21 @@ type OptParam struct {
 	Value  Serializable
 }
 
+type Capabilities []Capability
+
 type Capability struct {
 	Code   uint8
 	Length uint8
 	Value  Serializable
 }
 
+func (c Capabilities) serialize(buf *bytes.Buffer) {
+	tmpBuf := bytes.NewBuffer(make([]byte, 0))
+	for _, cap := range c {
+		cap.serialize(tmpBuf)
+	}
+}
+
 func (c Capability) serialize(buf *bytes.Buffer) {
 	tmpBuf := bytes.NewBuffer(make([]byte, 0))
 	c.Value.serialize(tmpBuf)
diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go
index c41e03b1..c53696fb 100644
--- a/protocols/bgp/server/fsm.go
+++ b/protocols/bgp/server/fsm.go
@@ -15,6 +15,7 @@ import (
 	"github.com/bio-routing/bio-rd/route"
 	"github.com/bio-routing/bio-rd/routingtable/adjRIBIn"
 	"github.com/bio-routing/bio-rd/routingtable/adjRIBOut"
+	"github.com/bio-routing/bio-rd/routingtable/adjRIBOutAddPath"
 	"github.com/bio-routing/bio-rd/routingtable/locRIB"
 	log "github.com/sirupsen/logrus"
 	tomb "gopkg.in/tomb.v2"
@@ -90,9 +91,12 @@ type FSM struct {
 	stopMsgRecvCh chan struct{}
 
 	adjRIBIn     *adjRIBIn.AdjRIBIn
-	adjRIBOut    *adjRIBOut.AdjRIBOut
+	adjRIBOut    routingtable.RouteTableClient
 	rib          *locRIB.LocRIB
-	updateSender *UpdateSender
+	updateSender routingtable.RouteTableClient
+
+	capAddPathSend bool
+	capAddPathRecv bool
 }
 
 type msgRecvMsg struct {
@@ -134,7 +138,6 @@ func NewFSM(peer *Peer, c config.Peer, rib *locRIB.LocRIB) *FSM {
 		rib: rib,
 	}
 
-	fsm.updateSender = newUpdateSender(fsm)
 	return fsm
 }
 
@@ -303,8 +306,6 @@ func (fsm *FSM) connect() int {
 }
 
 func (fsm *FSM) connectSendOpen() int {
-	optOpenParams := make([]packet.OptParam, 0)
-
 	err := fsm.sendOpen(fsm.con)
 	if err != nil {
 		stopTimer(fsm.connectRetryTimer)
@@ -459,6 +460,9 @@ func (fsm *FSM) openSent() int {
 					fsm.keepaliveTime = fsm.holdTime / 3
 					fsm.keepaliveTimer.Reset(time.Second * fsm.keepaliveTime)
 				}
+
+				fsm.processOpenOptions(openMsg.OptParams)
+
 				return fsm.changeState(OpenConfirm, "Received OPEN message")
 			default:
 				sendNotification(fsm.con, packet.FiniteStateMachineError, 0)
@@ -485,6 +489,56 @@ func (fsm *FSM) openSent() int {
 	}
 }
 
+func (fsm *FSM) processOpenOptions(optParams []packet.OptParam) {
+	for _, optParam := range optParams {
+		if optParam.Type != packet.CapabilitiesParamType {
+			continue
+		}
+
+		fsm.processCapabilities(optParam.Value.(packet.Capabilities))
+	}
+}
+
+func (fsm *FSM) processCapabilities(caps packet.Capabilities) {
+	for _, cap := range caps {
+		fsm.processCapability(cap)
+	}
+}
+
+func (fsm *FSM) processCapability(cap packet.Capability) {
+	switch cap.Code {
+	case packet.AddPathCapabilityCode:
+		fsm.processAddPathCapability(cap.Value.(packet.AddPathCapability))
+
+	}
+}
+
+func (fsm *FSM) processAddPathCapability(addPathCap packet.AddPathCapability) {
+	if addPathCap.AFI != 1 {
+		return
+	}
+	if addPathCap.SAFI != 1 {
+		return
+	}
+	switch addPathCap.SendReceive {
+	case packet.AddPathReceive:
+		if !fsm.peer.addPathSend.BestOnly {
+			fsm.capAddPathSend = true
+		}
+	case packet.AddPathSend:
+		if fsm.peer.addPathRecv {
+			fsm.capAddPathRecv = true
+		}
+	case packet.AddPathSendReceive:
+		if !fsm.peer.addPathSend.BestOnly {
+			fsm.capAddPathSend = true
+		}
+		if fsm.peer.addPathRecv {
+			fsm.capAddPathRecv = true
+		}
+	}
+}
+
 func (fsm *FSM) openSentTCPFail(err error) int {
 	fsm.con.Close()
 	fsm.resetConnectRetryTimer()
@@ -607,7 +661,7 @@ func (fsm *FSM) openConfirm() int {
 
 			switch msg.Header.Type {
 			case packet.NotificationMsg:
-				nMsg := msg.Body.(packet.BGPNotification)
+				nMsg := msg.Body.(*packet.BGPNotification)
 				if nMsg.ErrorCode == packet.UnsupportedVersionNumber {
 					stopTimer(fsm.connectRetryTimer)
 					fsm.con.Close()
@@ -669,18 +723,30 @@ func (fsm *FSM) established() int {
 		Type:    route.BGPPathType,
 		Address: tnet.IPv4ToUint32(fsm.remote),
 	}
-	fsm.adjRIBOut = adjRIBOut.New(n)
-	fsm.adjRIBOut.Register(fsm.updateSender)
 
-	fsm.rib.RegisterWithOptions(fsm.adjRIBOut, routingtable.ClientOptions{BestOnly: true})
+	clientOptions := routingtable.ClientOptions{}
+	if fsm.capAddPathSend {
+		fsm.updateSender = newUpdateSenderAddPath(fsm)
+		fsm.adjRIBOut = adjRIBOutAddPath.New(n)
+		clientOptions = fsm.peer.addPathSend
+	} else {
+		fsm.updateSender = newUpdateSender(fsm)
+		fsm.adjRIBOut = adjRIBOut.New(n)
+	}
+
+	fsm.adjRIBOut.Register(fsm.updateSender)
+	fsm.rib.RegisterWithOptions(fsm.adjRIBOut, clientOptions)
 
-	go func() {
+	/*go func() {
 		for {
+			if fsm.adjRIBOut == nil {
+				return
+			}
 			fmt.Printf("ADJ-RIB-OUT: %s\n", fsm.remote.String())
 			fmt.Print(fsm.adjRIBOut.Print())
 			time.Sleep(time.Second * 11)
 		}
-	}()
+	}()*/
 
 	for {
 		select {
@@ -756,8 +822,10 @@ func (fsm *FSM) established() int {
 					fmt.Printf("LPM: Adding prefix %s\n", pfx.String())
 
 					path := &route.Path{
-						Type:    route.BGPPathType,
-						BGPPath: &route.BGPPath{},
+						Type: route.BGPPathType,
+						BGPPath: &route.BGPPath{
+							Source: tnet.IPv4ToUint32(fsm.remote),
+						},
 					}
 
 					for pa := u.PathAttributes; pa != nil; pa = pa.Next {
diff --git a/protocols/bgp/server/peer.go b/protocols/bgp/server/peer.go
index e3091290..7bfcbc56 100644
--- a/protocols/bgp/server/peer.go
+++ b/protocols/bgp/server/peer.go
@@ -17,7 +17,7 @@ type Peer struct {
 	rib           *locRIB.LocRIB
 	routerID      uint32
 	addPathSend   routingtable.ClientOptions
-	addPathRecv   uint
+	addPathRecv   bool
 	optOpenParams []packet.OptParam
 }
 
@@ -35,7 +35,7 @@ func NewPeer(c config.Peer, rib *locRIB.LocRIB) (*Peer, error) {
 	caps := make([]packet.Capability, 0)
 
 	addPath := uint8(0)
-	if c.AddPathRecv > 1 {
+	if c.AddPathRecv {
 		addPath += packet.AddPathReceive
 	}
 	if !c.AddPathSend.BestOnly {
diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go
index 329e9713..07af48dd 100644
--- a/protocols/bgp/server/update_sender.go
+++ b/protocols/bgp/server/update_sender.go
@@ -12,6 +12,7 @@ import (
 )
 
 type UpdateSender struct {
+	routingtable.ClientManager
 	fsm *FSM
 }
 
diff --git a/protocols/bgp/server/update_sender_add_path.go b/protocols/bgp/server/update_sender_add_path.go
new file mode 100644
index 00000000..fc7fd0a7
--- /dev/null
+++ b/protocols/bgp/server/update_sender_add_path.go
@@ -0,0 +1,73 @@
+package server
+
+import (
+	"fmt"
+
+	log "github.com/sirupsen/logrus"
+
+	"github.com/bio-routing/bio-rd/net"
+	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
+	"github.com/bio-routing/bio-rd/route"
+	"github.com/bio-routing/bio-rd/routingtable"
+)
+
+type UpdateSenderAddPath struct {
+	routingtable.ClientManager
+	fsm *FSM
+}
+
+func newUpdateSenderAddPath(fsm *FSM) *UpdateSenderAddPath {
+	return &UpdateSenderAddPath{
+		fsm: fsm,
+	}
+}
+
+func (u *UpdateSenderAddPath) AddPath(pfx net.Prefix, p *route.Path) error {
+	fmt.Printf("SENDING AN BGP UPDATE WITH ADD PATH TO %s\n", u.fsm.remote.String())
+	asPathPA, err := packet.ParseASPathStr(fmt.Sprintf("%d %s", u.fsm.localASN, p.BGPPath.ASPath))
+	if err != nil {
+		return fmt.Errorf("Unable to parse AS path: %v", err)
+	}
+
+	update := &packet.BGPUpdateAddPath{
+		PathAttributes: &packet.PathAttribute{
+			TypeCode: packet.OriginAttr,
+			Value:    p.BGPPath.Origin,
+			Next: &packet.PathAttribute{
+				TypeCode: packet.ASPathAttr,
+				Value:    asPathPA.Value,
+				Next: &packet.PathAttribute{
+					TypeCode: packet.NextHopAttr,
+					Value:    p.BGPPath.NextHop,
+				},
+			},
+		},
+		NLRI: &packet.NLRIAddPath{
+			PathIdentifier: p.BGPPath.PathIdentifier,
+			IP:             pfx.Addr(),
+			Pfxlen:         pfx.Pfxlen(),
+		},
+	}
+
+	updateBytes, err := update.SerializeUpdate()
+	if err != nil {
+		log.Errorf("Unable to serialize BGP Update: %v", err)
+		return nil
+	}
+	fmt.Printf("Sending Update: %v\n", updateBytes)
+	_, err = u.fsm.con.Write(updateBytes)
+	if err != nil {
+		return fmt.Errorf("Failed sending Update: %v", err)
+	}
+	return nil
+}
+
+func (u *UpdateSenderAddPath) RemovePath(pfx net.Prefix, p *route.Path) bool {
+	log.Warningf("BGP Update Sender: RemovePath not implemented")
+	return false
+}
+
+func (u *UpdateSenderAddPath) UpdateNewClient(client routingtable.RouteTableClient) error {
+	log.Warningf("BGP Update Sender: RemovePath not implemented")
+	return nil
+}
diff --git a/routingtable/adjRIBOut/adj_rib_out.go b/routingtable/adjRIBOut/adj_rib_out.go
index 8691c5ac..086d8654 100644
--- a/routingtable/adjRIBOut/adj_rib_out.go
+++ b/routingtable/adjRIBOut/adj_rib_out.go
@@ -7,6 +7,7 @@ import (
 	"github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/route"
 	"github.com/bio-routing/bio-rd/routingtable"
+	"github.com/taktv6/tflow2/convert"
 )
 
 // AdjRIBOut represents an Adjacency RIB In as described in RFC4271
@@ -29,11 +30,13 @@ func New(neighbor *routingtable.Neighbor) *AdjRIBOut {
 
 // UpdateNewClient sends current state to a new client
 func (a *AdjRIBOut) UpdateNewClient(client routingtable.RouteTableClient) error {
-	return fmt.Errorf("Not supported")
+	return nil
 }
 
 // AddPath replaces the path for prefix `pfx`. If the prefix doesn't exist it is added.
 func (a *AdjRIBOut) AddPath(pfx net.Prefix, p *route.Path) error {
+	fmt.Printf("THIS IS ADJ RIB OUT NON ADD PATH FOR %v\n", convert.Uint32Byte(a.neighbor.Address))
+
 	if a.isOwnPath(p) {
 		return nil
 	}
diff --git a/routingtable/adjRIBOutAddPath/adj_rib_out_add_path.go b/routingtable/adjRIBOutAddPath/adj_rib_out_add_path.go
index b3e440a8..5c661734 100644
--- a/routingtable/adjRIBOutAddPath/adj_rib_out_add_path.go
+++ b/routingtable/adjRIBOutAddPath/adj_rib_out_add_path.go
@@ -7,6 +7,7 @@ import (
 	"github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/route"
 	"github.com/bio-routing/bio-rd/routingtable"
+	"github.com/taktv6/tflow2/convert"
 )
 
 // AdjRIBOutAddPath represents an Adjacency RIB Out with BGP add path
@@ -29,11 +30,13 @@ func New(neighbor *routingtable.Neighbor) *AdjRIBOutAddPath {
 
 // UpdateNewClient sends current state to a new client
 func (a *AdjRIBOutAddPath) UpdateNewClient(client routingtable.RouteTableClient) error {
-	return fmt.Errorf("Not supported")
+	return nil
 }
 
 // AddPath adds path p to prefix `pfx`
 func (a *AdjRIBOutAddPath) AddPath(pfx net.Prefix, p *route.Path) error {
+	fmt.Printf("THIS IS ADD PATH ON ADJ RIB OUT FOR %v", convert.Uint32Byte(a.neighbor.Address))
+
 	if a.isOwnPath(p) {
 		return nil
 	}
@@ -41,6 +44,10 @@ func (a *AdjRIBOutAddPath) AddPath(pfx net.Prefix, p *route.Path) error {
 	a.mu.Lock()
 	defer a.mu.Unlock()
 
+	p.BGPPath.PathIdentifier = 7
+
+	a.rt.AddPath(pfx, p)
+
 	for _, client := range a.ClientManager.Clients() {
 		client.AddPath(pfx, p)
 	}
diff --git a/routingtable/client_interface.go b/routingtable/client_interface.go
index 63ba81e8..0800ceec 100644
--- a/routingtable/client_interface.go
+++ b/routingtable/client_interface.go
@@ -10,4 +10,6 @@ type RouteTableClient interface {
 	AddPath(net.Prefix, *route.Path) error
 	RemovePath(net.Prefix, *route.Path) bool
 	UpdateNewClient(RouteTableClient) error
+	Register(RouteTableClient)
+	Unregister(RouteTableClient)
 }
diff --git a/routingtable/locRIB/loc_rib.go b/routingtable/locRIB/loc_rib.go
index 6e030de3..8d8632c0 100644
--- a/routingtable/locRIB/loc_rib.go
+++ b/routingtable/locRIB/loc_rib.go
@@ -51,7 +51,7 @@ func (a *LocRIB) AddPath(pfx net.Prefix, p *route.Path) error {
 		oldRoute = r.Copy()
 		routeExisted = true
 	}
-	
+
 	// FIXME: in AddPath() we assume that the same reference of route (r) is modified (not responsibility of locRIB). If this implementation changes in the future this code will break.
 	a.rt.AddPath(pfx, p)
 	if !routeExisted {
@@ -99,6 +99,7 @@ func (a *LocRIB) propagateChanges(oldRoute *route.Route, newRoute *route.Route)
 }
 
 func (a *LocRIB) addPathsToClients(oldRoute *route.Route, newRoute *route.Route) {
+	fmt.Printf("LocRIB: Updating %d clients\n", len(a.ClientManager.Clients()))
 	for _, client := range a.ClientManager.Clients() {
 		opts := a.ClientManager.GetOptions(client)
 		oldMaxPaths := opts.GetMaxPaths(oldRoute.ECMPPathCount())
@@ -107,6 +108,9 @@ func (a *LocRIB) addPathsToClients(oldRoute *route.Route, newRoute *route.Route)
 		oldPathsLimit := int(math.Min(float64(oldMaxPaths), float64(len(oldRoute.Paths()))))
 		newPathsLimit := int(math.Min(float64(newMaxPaths), float64(len(newRoute.Paths()))))
 
+		fmt.Printf("oldPathsLimit: %v\n", oldPathsLimit)
+		fmt.Printf("newPathsLimit: %v\n", newPathsLimit)
+
 		advertise := route.PathsDiff(newRoute.Paths()[0:newPathsLimit], oldRoute.Paths()[0:oldPathsLimit])
 		fmt.Printf("ADVERTISING PATHS %v TO CLIENTS\n", advertise)
 
-- 
GitLab