diff --git a/config/peer.go b/config/peer.go index 8f7692b085e39fae5b1ec998233bf2d174a84581..355e058379721a041d029ea403216941529342fd 100644 --- a/config/peer.go +++ b/config/peer.go @@ -2,6 +2,8 @@ package config import ( "net" + + "github.com/bio-routing/bio-rd/routingtable" ) type Peer struct { @@ -14,4 +16,6 @@ type Peer struct { PeerAS uint32 Passive bool RouterID uint32 + AddPathSend routingtable.ClientOptions + AddPathRecv uint } diff --git a/main.go b/main.go index 78171d7c20df8a77c4cdb95553591b4b19381c92..ac5805fc38a21f4b4acae1a51b838f6666a71996 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( "github.com/bio-routing/bio-rd/config" "github.com/bio-routing/bio-rd/protocols/bgp/server" + "github.com/bio-routing/bio-rd/routingtable" "github.com/bio-routing/bio-rd/routingtable/locRIB" ) @@ -50,6 +51,10 @@ func main() { KeepAlive: 30, Passive: true, RouterID: b.RouterID(), + AddPathSend: routingtable.ClientOptions{ + MaxPaths: 10, + }, + AddPathRecv: 1, }, rib) go func() { diff --git a/protocols/bgp/packet/bgp.go b/protocols/bgp/packet/bgp.go index ba1d3d32206fe90680798ec1bf7338a9ee6fee22..e6ac0e6b3f030ef905a6325fb7550237459d02be 100644 --- a/protocols/bgp/packet/bgp.go +++ b/protocols/bgp/packet/bgp.go @@ -76,6 +76,14 @@ const ( OtherConfigChange = 8 ConnectionCollisionResolution = 7 OutOfResoutces = 8 + + IPv4AFI = 1 + UnicastSAFI = 1 + CapabilitiesParamType = 2 + AddPathCapabilityCode = 69 + AddPathReceive = 1 + AddPathSend = 2 + AddPathSendReceive = 3 ) type BGPError struct { @@ -104,6 +112,7 @@ type BGPOpen struct { HoldTime uint16 BGPIdentifier uint32 OptParmLen uint8 + OptParams []OptParam } type BGPNotification struct { @@ -144,6 +153,6 @@ type ASPathSegment struct { } type Aggretator struct { - Addr [4]byte + Addr uint32 ASN uint16 } diff --git a/protocols/bgp/packet/decoder.go b/protocols/bgp/packet/decoder.go index 8ba824ec9fd0620a32416bc0c66c8fd3b8d4c0ef..c5274122cef840355231b3521ced3579ff9be031 100644 --- a/protocols/bgp/packet/decoder.go +++ b/protocols/bgp/packet/decoder.go @@ -154,9 +154,92 @@ func _decodeOpenMsg(buf *bytes.Buffer) (interface{}, error) { return nil, err } + msg.OptParams, err = decodeOptParams(buf, msg.OptParmLen) + if err != nil { + return nil, fmt.Errorf("Unable to decode optional parameters: %v", err) + } + return msg, nil } +func decodeOptParams(buf *bytes.Buffer, optParmLen uint8) ([]OptParam, error) { + optParams := make([]OptParam, 0) + read := uint8(0) + for read < optParmLen { + o := OptParam{} + fields := []interface{}{ + &o.Type, + &o.Length, + } + + err := decode(buf, fields) + if err != nil { + return nil, err + } + + read += 2 + + fmt.Printf("Type: %d\n", o.Type) + switch o.Type { + case CapabilitiesParamType: + cap, err := decodeCapability(buf) + if err != nil { + return nil, fmt.Errorf("Unable to decode capability: %v", err) + } + o.Value = cap + optParams = append(optParams, o) + read += cap.Length + 2 + default: + return nil, fmt.Errorf("Unrecognized option") + } + + } + + return optParams, nil +} + +func decodeCapability(buf *bytes.Buffer) (Capability, error) { + cap := Capability{} + fields := []interface{}{ + &cap.Code, + &cap.Length, + } + + err := decode(buf, fields) + if err != nil { + return cap, err + } + + switch cap.Code { + case AddPathCapabilityCode: + addPathCap, err := decodeAddPathCapability(buf) + if err != nil { + return cap, fmt.Errorf("Unable to decode add path capability") + } + cap.Value = addPathCap + default: + return cap, fmt.Errorf("Unknown capability: %d", cap.Code) + } + + return cap, nil +} + +func decodeAddPathCapability(buf *bytes.Buffer) (AddPathCapability, error) { + addPathCap := AddPathCapability{} + fields := []interface{}{ + &addPathCap.AFI, + &addPathCap.SAFI, + &addPathCap.SendReceive, + } + + err := decode(buf, fields) + if err != nil { + return addPathCap, err + } + + return addPathCap, nil +} + func validateOpen(msg *BGPOpen) error { if msg.Version != BGP4Version { return BGPError{ diff --git a/protocols/bgp/packet/decoder_test.go b/protocols/bgp/packet/decoder_test.go index 63b5631d7c0a4e165a2fea093edc9083a2ffdc06..53681371a5d7ca2bdb2a703efc25bf85ea8b0d61 100644 --- a/protocols/bgp/packet/decoder_test.go +++ b/protocols/bgp/packet/decoder_test.go @@ -160,6 +160,7 @@ func TestDecode(t *testing.T) { HoldTime: 15, BGPIdentifier: uint32(169090600), OptParmLen: 0, + OptParams: []OptParam{}, }, }, }, @@ -706,7 +707,7 @@ func TestDecodeUpdateMsg(t *testing.T) { ExtendedLength: false, Length: 4, TypeCode: 3, - Value: [4]byte{10, 11, 12, 13}, + Value: strAddr("10.11.12.13"), }, }, }, @@ -799,7 +800,7 @@ func TestDecodeUpdateMsg(t *testing.T) { ExtendedLength: false, Length: 4, TypeCode: 3, - Value: [4]byte{10, 11, 12, 13}, + Value: strAddr("10.11.12.13"), Next: &PathAttribute{ Optional: false, Transitive: false, @@ -905,7 +906,7 @@ func TestDecodeUpdateMsg(t *testing.T) { ExtendedLength: false, Length: 4, TypeCode: 3, - Value: [4]byte{10, 11, 12, 13}, + Value: strAddr("10.11.12.13"), Next: &PathAttribute{ Optional: false, Transitive: false, @@ -1023,7 +1024,7 @@ func TestDecodeUpdateMsg(t *testing.T) { ExtendedLength: false, Length: 4, TypeCode: 3, - Value: [4]byte{10, 11, 12, 13}, + Value: strAddr("10.11.12.13"), Next: &PathAttribute{ Optional: false, Transitive: false, @@ -1157,7 +1158,7 @@ func TestDecodeUpdateMsg(t *testing.T) { ExtendedLength: false, Length: 4, TypeCode: 3, - Value: [4]byte{10, 11, 12, 13}, + Value: strAddr("10.11.12.13"), Next: &PathAttribute{ Optional: false, Transitive: false, @@ -1190,7 +1191,7 @@ func TestDecodeUpdateMsg(t *testing.T) { TypeCode: 7, Value: Aggretator{ ASN: uint16(258), - Addr: [4]byte{10, 11, 12, 13}, + Addr: strAddr("10.11.12.13"), }, }, }, @@ -1201,7 +1202,7 @@ func TestDecodeUpdateMsg(t *testing.T) { }, NLRI: &NLRI{ Pfxlen: 8, - IP: strAddr("1.0.0.0"), + IP: strAddr("11.0.0.0"), }, }, }, @@ -1266,7 +1267,7 @@ func TestDecodeUpdateMsg(t *testing.T) { continue } - assert.Equal(t, test.expected, msg) + assert.Equalf(t, test.expected, msg, "%d", test.testNum) } } @@ -1313,6 +1314,7 @@ func TestDecodeOpenMsg(t *testing.T) { HoldTime: 15, BGPIdentifier: 169090600, OptParmLen: 0, + OptParams: make([]OptParam, 0), }, }, { @@ -1452,7 +1454,7 @@ func genericTest(f decodeFunc, tests []test, t *testing.T) { continue } - assert.Equal(t, test.expected, msg) + assert.Equalf(t, test.expected, msg, "%d", test.testNum) } } @@ -1536,6 +1538,157 @@ func TestValidateOpenMessage(t *testing.T) { } } +func TestDecodeOptParams(t *testing.T) { + tests := []struct { + name string + input []byte + wantFail bool + expected []OptParam + }{ + { + name: "Add path capability", + input: []byte{ + 2, // Type + 6, // Length + 69, // Code + 4, // Length + 0, 1, // AFI + 1, // SAFI + 3, // Send/Receive + }, + wantFail: false, + expected: []OptParam{ + { + Type: 2, + Length: 6, + Value: Capability{ + Code: 69, + Length: 4, + Value: AddPathCapability{ + AFI: 1, + SAFI: 1, + SendReceive: 3, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + buf := bytes.NewBuffer(test.input) + res, err := decodeOptParams(buf, uint8(len(test.input))) + if err != nil { + if test.wantFail { + continue + } + + t.Errorf("Unexpected failure for test %q: %v", test.name, err) + continue + } + + if test.wantFail { + t.Errorf("Unexpected success for test %q", test.name) + continue + } + + assert.Equal(t, test.expected, res) + } +} + +func TestDecodeCapability(t *testing.T) { + tests := []struct { + name string + input []byte + expected Capability + wantFail bool + }{ + { + name: "Add Path", + input: []byte{69, 4, 0, 1, 1, 3}, + expected: Capability{ + Code: 69, + Length: 4, + Value: AddPathCapability{ + AFI: 1, + SAFI: 1, + SendReceive: 3, + }, + }, + wantFail: false, + }, + { + name: "Fail", + input: []byte{69, 4, 0, 1}, + wantFail: true, + }, + } + + for _, test := range tests { + cap, err := decodeCapability(bytes.NewBuffer(test.input)) + if err != nil { + if test.wantFail { + continue + } + + t.Errorf("Unexpected failure for test %q: %v", test.name, err) + continue + } + + if test.wantFail { + t.Errorf("Unexpected success for test %q", err) + continue + } + + assert.Equal(t, test.expected, cap) + } +} + +func TestDecodeAddPathCapability(t *testing.T) { + tests := []struct { + name string + input []byte + expected AddPathCapability + wantFail bool + }{ + { + name: "ok", + input: []byte{0, 1, 1, 3}, + wantFail: false, + expected: AddPathCapability{ + AFI: 1, + SAFI: 1, + SendReceive: 3, + }, + }, + { + name: "Incomplete", + input: []byte{0, 1, 1}, + wantFail: true, + }, + } + + for _, test := range tests { + buf := bytes.NewBuffer(test.input) + cap, err := decodeAddPathCapability(buf) + if err != nil { + if test.wantFail { + continue + } + + t.Errorf("Unexpected failure for test %q: %v", test.name, err) + continue + } + + if test.wantFail { + t.Errorf("Unexpected success for test %q", test.name) + continue + } + + assert.Equal(t, test.expected, cap) + } +} + func strAddr(s string) uint32 { ret, _ := net.StrToAddr(s) return ret diff --git a/protocols/bgp/packet/encoder.go b/protocols/bgp/packet/encoder.go index 3cfe702d14ffb66605919fe60839d98a8308b761..3f44047a0a9e86c5aeaf6271bacebf56f5fe32d1 100644 --- a/protocols/bgp/packet/encoder.go +++ b/protocols/bgp/packet/encoder.go @@ -34,11 +34,28 @@ func SerializeOpenMsg(msg *BGPOpen) []byte { buf.Write(convert.Uint16Byte(msg.AS)) buf.Write(convert.Uint16Byte(msg.HoldTime)) buf.Write(convert.Uint32Byte(msg.BGPIdentifier)) - buf.WriteByte(uint8(0)) + + optParmsBuf := bytes.NewBuffer(make([]byte, 0)) + serializeOptParams(optParmsBuf, msg.OptParams) + optParms := optParmsBuf.Bytes() + buf.WriteByte(uint8(len(optParms))) + buf.Write(optParms) return buf.Bytes() } +func serializeOptParams(buf *bytes.Buffer, params []OptParam) { + for _, param := range params { + tmpBuf := bytes.NewBuffer(make([]byte, 0)) + param.Value.serialize(tmpBuf) + payload := tmpBuf.Bytes() + + buf.WriteByte(param.Type) + buf.WriteByte(uint8(len(payload))) + buf.Write(payload) + } +} + func serializeHeader(buf *bytes.Buffer, length uint16, typ uint8) { buf.Write([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}) buf.Write(convert.Uint16Byte(length)) diff --git a/protocols/bgp/packet/encoder_test.go b/protocols/bgp/packet/encoder_test.go index eccfdfdcace6be4eeac998bdbcaa29749d0a2581..cc6968d92f353e4130abacf3298b3d80872fd25c 100644 --- a/protocols/bgp/packet/encoder_test.go +++ b/protocols/bgp/packet/encoder_test.go @@ -94,6 +94,53 @@ func TestSerializeOpenMsg(t *testing.T) { } } +/*func TestSerializeOptParams(t *testing.T) { + tests := []struct { + name string + optParams []OptParam + expected []byte + }{ + { + name: "empty", + optParams: []OptParam{}, + expected: []byte{}, + }, + { + name: "1 Option", + optParams: []OptParam{ + { + Type: 100, + Length: 2, + Value: []byte{1, 2}, + }, + }, + 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}, + }, + } + + for _, test := range tests { + buf := bytes.NewBuffer(make([]byte, 0)) + serializeOptParams(buf, test.optParams) + assert.Equal(t, test.expected, buf.Bytes()) + } +}*/ + func TestSerializeHeader(t *testing.T) { tests := []struct { name string diff --git a/protocols/bgp/packet/parameters.go b/protocols/bgp/packet/parameters.go new file mode 100644 index 0000000000000000000000000000000000000000..543bc4470275b3db7d3384347c51e6845ed303a2 --- /dev/null +++ b/protocols/bgp/packet/parameters.go @@ -0,0 +1,45 @@ +package packet + +import ( + "bytes" + + "github.com/taktv6/tflow2/convert" +) + +type Serializable interface { + serialize(*bytes.Buffer) +} + +type OptParam struct { + Type uint8 + Length uint8 + Value Serializable +} + +type Capability struct { + Code uint8 + Length uint8 + Value Serializable +} + +func (c Capability) serialize(buf *bytes.Buffer) { + tmpBuf := bytes.NewBuffer(make([]byte, 0)) + c.Value.serialize(tmpBuf) + payload := tmpBuf.Bytes() + + buf.WriteByte(c.Code) + buf.WriteByte(uint8(len(payload))) + buf.Write(payload) +} + +type AddPathCapability struct { + AFI uint16 + SAFI uint8 + SendReceive uint8 +} + +func (a AddPathCapability) serialize(buf *bytes.Buffer) { + buf.Write(convert.Uint16Byte(a.AFI)) + buf.WriteByte(a.SAFI) + buf.WriteByte(a.SendReceive) +} diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go index c305ec27a45b899cad0b9aeeba44a23d690d412f..0390b6617944b5ea1933b542b97c29ac069c52c6 100644 --- a/protocols/bgp/packet/path_attributes.go +++ b/protocols/bgp/packet/path_attributes.go @@ -186,24 +186,27 @@ func (pa *PathAttribute) decodeLocalPref(buf *bytes.Buffer) error { func (pa *PathAttribute) decodeAggregator(buf *bytes.Buffer) error { aggr := Aggretator{} - p := uint16(0) + err := decode(buf, []interface{}{&aggr.ASN}) if err != nil { return err } p += 2 - n, err := buf.Read(aggr.Addr[:]) + addr := [4]byte{} + n, err := buf.Read(addr[:]) if err != nil { return err } if n != 4 { - return fmt.Errorf("Unable to read aggregator IP: buf.Read read %d bytes", n) + return fmt.Errorf("Unable to read next hop: buf.Read read %d bytes", n) } - p += 4 + aggr.Addr = fourBytesToUint32(addr) pa.Value = aggr + p += 4 + return dumpNBytes(buf, pa.Length-p) } diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go index e413d4f88f93a66dccca5b1d1444dbf3f64dff7d..5145fddbbd25a27ff8e403ecb4f66a4cfbd60163 100644 --- a/protocols/bgp/packet/path_attributes_test.go +++ b/protocols/bgp/packet/path_attributes_test.go @@ -34,7 +34,7 @@ func TestDecodePathAttrs(t *testing.T) { Next: &PathAttribute{ TypeCode: 3, Length: 4, - Value: [4]byte{10, 20, 30, 40}, + Value: strAddr("10.20.30.40"), }, }, }, @@ -62,7 +62,7 @@ func TestDecodePathAttrs(t *testing.T) { continue } - assert.Equal(t, test.expected, res) + assert.Equal(t, test.expected, res, "%s", test.name) } } @@ -367,9 +367,7 @@ func TestDecodeNextHop(t *testing.T) { wantFail: false, expected: &PathAttribute{ Length: 4, - Value: [4]byte{ - 10, 20, 30, 40, - }, + Value: strAddr("10.20.30.40"), }, }, { @@ -538,7 +536,7 @@ func TestDecodeAggregator(t *testing.T) { Length: 6, Value: Aggretator{ ASN: 222, - Addr: [4]byte{10, 20, 30, 40}, + Addr: strAddr("10.20.30.40"), }, }, }, @@ -892,9 +890,9 @@ func TestSerializeNextHop(t *testing.T) { name: "Test #1", input: &PathAttribute{ TypeCode: NextHopAttr, - Value: [4]byte{100, 110, 120, 130}, + Value: strAddr("100.110.120.130"), }, - expected: []byte{0, 3, 4, 100, 110, 120, 130}, + expected: []byte{64, 3, 4, 100, 110, 120, 130}, expectedLen: 7, }, } @@ -1186,7 +1184,7 @@ func TestSerialize(t *testing.T) { }, Next: &PathAttribute{ TypeCode: NextHopAttr, - Value: [4]byte{10, 20, 30, 40}, + Value: strAddr("10.20.30.40"), Next: &PathAttribute{ TypeCode: MEDAttr, Value: uint32(100), @@ -1216,7 +1214,7 @@ func TestSerialize(t *testing.T) { }, expected: []byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0, 85, // Length + 0, 86, // Length 2, // Msg Type // Withdraws @@ -1224,7 +1222,7 @@ func TestSerialize(t *testing.T) { 8, 10, // Withdraw 10/8 16, 192, 168, // Withdraw 192.168/16 - 0, 49, // Total Path Attribute Length + 0, 50, // Total Path Attribute Length // ORIGIN 64, // Attr. Flags @@ -1234,6 +1232,7 @@ func TestSerialize(t *testing.T) { // ASPath 64, // Attr. Flags 2, // Attr. Type Code + 14, // Attr. Length 2, // Path Segment Type = AS_SEQUENCE 3, // Path Segment Length 0, 100, 0, 155, 0, 200, // ASNs @@ -1288,7 +1287,7 @@ func TestSerialize(t *testing.T) { continue } - assert.Equal(t, test.expected, res) + assert.Equalf(t, test.expected, res, "%s", test.name) } } diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go index 051eaf03cd24f5d6c176935c7c288a1fe1e7c0f0..c41e03b19f90eeab6e732f14a00e5019b6ce1fd5 100644 --- a/protocols/bgp/server/fsm.go +++ b/protocols/bgp/server/fsm.go @@ -46,6 +46,8 @@ const ( ) type FSM struct { + peer *Peer + t tomb.Tomb stateReason string state int @@ -103,8 +105,9 @@ type msgRecvErr struct { con *net.TCPConn } -func NewFSM(c config.Peer, rib *locRIB.LocRIB) *FSM { +func NewFSM(peer *Peer, c config.Peer, rib *locRIB.LocRIB) *FSM { fsm := &FSM{ + peer: peer, state: Idle, passive: true, connectRetryTime: 5, @@ -300,6 +303,8 @@ 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) @@ -860,7 +865,7 @@ func (fsm *FSM) sendOpen(c *net.TCPConn) error { AS: fsm.localASN, HoldTime: uint16(fsm.holdTimeConfigured), BGPIdentifier: fsm.routerID, - OptParmLen: 0, + OptParams: fsm.peer.optOpenParams, }) _, err := c.Write(msg) diff --git a/protocols/bgp/server/peer.go b/protocols/bgp/server/peer.go index 5c65732b6a3247ecf88b833c1626a2cba96b2f43..e30912906d53f150e26873ebbb8868f0041c3700 100644 --- a/protocols/bgp/server/peer.go +++ b/protocols/bgp/server/peer.go @@ -3,26 +3,63 @@ package server import ( "net" + "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/routingtable" "github.com/bio-routing/bio-rd/routingtable/locRIB" "github.com/bio-routing/bio-rd/config" ) type Peer struct { - addr net.IP - asn uint32 - fsm *FSM - rib *locRIB.LocRIB - routerID uint32 + addr net.IP + asn uint32 + fsm *FSM + rib *locRIB.LocRIB + routerID uint32 + addPathSend routingtable.ClientOptions + addPathRecv uint + optOpenParams []packet.OptParam } func NewPeer(c config.Peer, rib *locRIB.LocRIB) (*Peer, error) { p := &Peer{ - addr: c.PeerAddress, - asn: c.PeerAS, - fsm: NewFSM(c, rib), - rib: rib, + addr: c.PeerAddress, + asn: c.PeerAS, + rib: rib, + addPathSend: c.AddPathSend, + addPathRecv: c.AddPathRecv, + optOpenParams: make([]packet.OptParam, 0), } + p.fsm = NewFSM(p, c, rib) + + caps := make([]packet.Capability, 0) + + addPath := uint8(0) + if c.AddPathRecv > 1 { + addPath += packet.AddPathReceive + } + if !c.AddPathSend.BestOnly { + addPath += packet.AddPathSend + } + + if addPath > 0 { + caps = append(caps, packet.Capability{ + Code: packet.AddPathCapabilityCode, + Value: packet.AddPathCapability{ + AFI: packet.IPv4AFI, + SAFI: packet.UnicastSAFI, + SendReceive: addPath, + }, + }) + } + + for _, cap := range caps { + p.optOpenParams = append(p.optOpenParams, packet.OptParam{ + Type: packet.CapabilitiesParamType, + Value: cap, + }) + } + return p, nil } diff --git a/routingtable/adjRIBOutAddPath/adj_rib_out_add_path.go b/routingtable/adjRIBOutAddPath/adj_rib_out_add_path.go new file mode 100644 index 0000000000000000000000000000000000000000..b3e440a8aaa676b836276cceeac6a8e9391a82b5 --- /dev/null +++ b/routingtable/adjRIBOutAddPath/adj_rib_out_add_path.go @@ -0,0 +1,100 @@ +package adjRIBOutAddPath + +import ( + "fmt" + "sync" + + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/route" + "github.com/bio-routing/bio-rd/routingtable" +) + +// AdjRIBOutAddPath represents an Adjacency RIB Out with BGP add path +type AdjRIBOutAddPath struct { + routingtable.ClientManager + rt *routingtable.RoutingTable + neighbor *routingtable.Neighbor + mu sync.RWMutex +} + +// New creates a new Adjacency RIB Out with BGP add path +func New(neighbor *routingtable.Neighbor) *AdjRIBOutAddPath { + a := &AdjRIBOutAddPath{ + rt: routingtable.NewRoutingTable(), + neighbor: neighbor, + } + a.ClientManager = routingtable.NewClientManager(a) + return a +} + +// UpdateNewClient sends current state to a new client +func (a *AdjRIBOutAddPath) UpdateNewClient(client routingtable.RouteTableClient) error { + return fmt.Errorf("Not supported") +} + +// AddPath adds path p to prefix `pfx` +func (a *AdjRIBOutAddPath) AddPath(pfx net.Prefix, p *route.Path) error { + if a.isOwnPath(p) { + return nil + } + + a.mu.Lock() + defer a.mu.Unlock() + + for _, client := range a.ClientManager.Clients() { + client.AddPath(pfx, p) + } + return nil +} + +// RemovePath removes the path for prefix `pfx` +func (a *AdjRIBOutAddPath) RemovePath(pfx net.Prefix, p *route.Path) bool { + if a.isOwnPath(p) { + 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.removePathFromClients(pfx, p) + return true +} + +func (a *AdjRIBOutAddPath) 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 *AdjRIBOutAddPath) removePathFromClients(pfx net.Prefix, path *route.Path) { + for _, client := range a.ClientManager.Clients() { + client.RemovePath(pfx, path) + } +} + +// Print dumps all prefixes in the Adj-RIB +func (a *AdjRIBOutAddPath) 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 +}