diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go index 1527f8cf6e7d3d3ff9365068272aa3fad52e41d0..248b731714389096a62e2381a548780beb8318e7 100644 --- a/protocols/bgp/packet/path_attributes_test.go +++ b/protocols/bgp/packet/path_attributes_test.go @@ -1580,6 +1580,212 @@ func TestSerialize(t *testing.T) { } } +func TestSerializeAddPath(t *testing.T) { + tests := []struct { + name string + msg *BGPUpdateAddPath + expected []byte + wantFail bool + }{ + { + name: "Withdraw only", + msg: &BGPUpdateAddPath{ + WithdrawnRoutes: &NLRIAddPath{ + PathIdentifier: 257, + IP: strAddr("100.110.120.0"), + Pfxlen: 24, + }, + }, + expected: []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, 31, // Length + 2, // Msg Type + 0, 8, // Withdrawn Routes Length + 0, 0, 1, 1, // Path Identifier + 24, 100, 110, 120, // NLRI + 0, 0, // Total Path Attribute Length + }, + }, + { + name: "NLRI only", + msg: &BGPUpdateAddPath{ + NLRI: &NLRIAddPath{ + PathIdentifier: 257, + IP: strAddr("100.110.128.0"), + Pfxlen: 17, + }, + }, + expected: []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, 31, // Length + 2, // Msg Type + 0, 0, // Withdrawn Routes Length + 0, 0, // Total Path Attribute Length + 0, 0, 1, 1, // Path Identifier + 17, 100, 110, 128, // NLRI + }, + }, + { + name: "Path Attributes only", + msg: &BGPUpdateAddPath{ + PathAttributes: &PathAttribute{ + Optional: true, + Transitive: true, + TypeCode: OriginAttr, + Value: uint8(0), // IGP + }, + }, + expected: []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, 27, // Length + 2, // Msg Type + 0, 0, // Withdrawn Routes Length + 0, 4, // Total Path Attribute Length + 64, // Attr. Flags + 1, // Attr. Type Code + 1, // Length + 0, // Value + }, + }, + { + name: "Full test", + msg: &BGPUpdateAddPath{ + WithdrawnRoutes: &NLRIAddPath{ + IP: strAddr("10.0.0.0"), + Pfxlen: 8, + Next: &NLRIAddPath{ + IP: strAddr("192.168.0.0"), + Pfxlen: 16, + }, + }, + PathAttributes: &PathAttribute{ + TypeCode: OriginAttr, + Value: uint8(0), + Next: &PathAttribute{ + TypeCode: ASPathAttr, + Value: ASPath{ + { + Type: 2, + ASNs: []uint32{100, 155, 200}, + }, + { + Type: 1, + ASNs: []uint32{10, 20}, + }, + }, + Next: &PathAttribute{ + TypeCode: NextHopAttr, + Value: strAddr("10.20.30.40"), + Next: &PathAttribute{ + TypeCode: MEDAttr, + Value: uint32(100), + Next: &PathAttribute{ + TypeCode: LocalPrefAttr, + Value: uint32(500), + Next: &PathAttribute{ + TypeCode: AtomicAggrAttr, + Next: &PathAttribute{ + TypeCode: AggregatorAttr, + Value: uint16(200), + }, + }, + }, + }, + }, + }, + }, + NLRI: &NLRIAddPath{ + IP: strAddr("8.8.8.0"), + Pfxlen: 24, + Next: &NLRIAddPath{ + IP: strAddr("185.65.240.0"), + Pfxlen: 22, + }, + }, + }, + expected: []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, 102, // Length + 2, // Msg Type + + // Withdraws + 0, 13, // Withdrawn Routes Length + 0, 0, 0, 0, // Path Identifier + 8, 10, // Withdraw 10/8 + 0, 0, 0, 0, // Path Identifier + 16, 192, 168, // Withdraw 192.168/16 + + 0, 50, // Total Path Attribute Length + + // ORIGIN + 64, // Attr. Flags + 1, // Attr. Type Code + 1, // Length + 0, // Value + // 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 + 1, // Path Segment Type = AS_SET + 2, // Path Segment Type = AS_SET + 0, 10, 0, 20, // ASNs + // Next Hop + 64, // Attr. Flags + 3, // Attr. Type Code + 4, // Length + 10, 20, 30, 40, // Next Hop Address + // MED + 128, // Attr. Flags + 4, // Attr Type Code + 4, // Length + 0, 0, 0, 100, // MED = 100 + // LocalPref + 64, // Attr. Flags + 5, // Attr. Type Code + 4, // Length + 0, 0, 1, 244, // Localpref + // Atomic Aggregate + 64, // Attr. Flags + 6, // Attr. Type Code + 0, // Length + // Aggregator + 192, // Attr. Flags + 7, // Attr. Type Code + 2, // Length + 0, 200, // Aggregator ASN = 200 + + // NLRI + 0, 0, 0, 0, // Path Identifier + 24, 8, 8, 8, // 8.8.8.0/24 + 0, 0, 0, 0, // Path Identifier + 22, 185, 65, 240, // 185.65.240.0/22 + }, + }, + } + + for _, test := range tests { + res, err := test.msg.SerializeUpdate() + 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.Equalf(t, test.expected, res, "%s", test.name) + } +} + func TestParseASPathStr(t *testing.T) { tests := []struct { name string diff --git a/protocols/bgp/server/update_helper.go b/protocols/bgp/server/update_helper.go index 6873ca8db7c50ce449f23d43777fdc2d27744965..4a50f1d7ef6903c415a99108f8540fc5d0b40bcd 100644 --- a/protocols/bgp/server/update_helper.go +++ b/protocols/bgp/server/update_helper.go @@ -2,10 +2,12 @@ package server import ( "fmt" + "io" "strings" "github.com/bio-routing/bio-rd/protocols/bgp/packet" "github.com/bio-routing/bio-rd/route" + log "github.com/sirupsen/logrus" ) func pathAttribues(p *route.Path, fsm *FSM) (*packet.PathAttribute, error) { @@ -68,3 +70,21 @@ func addOptionalPathAttribues(p *route.Path, parent *packet.PathAttribute) error return nil } + +type serializeAbleUpdate interface { + SerializeUpdate() ([]byte, error) +} + +func serializeAndSendUpdate(out io.Writer, update serializeAbleUpdate) error { + updateBytes, err := update.SerializeUpdate() + if err != nil { + log.Errorf("Unable to serialize BGP Update: %v", err) + return nil + } + + _, err = out.Write(updateBytes) + if err != nil { + return fmt.Errorf("Failed sending Update: %v", err) + } + return nil +} diff --git a/protocols/bgp/server/update_helper_test.go b/protocols/bgp/server/update_helper_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5752edded4aba03b66acd88950796e21c2c82620 --- /dev/null +++ b/protocols/bgp/server/update_helper_test.go @@ -0,0 +1,109 @@ +package server + +import ( + "io" + "testing" + + "bytes" + + "github.com/bio-routing/bio-rd/protocols/bgp/packet" + + "errors" + + "github.com/bio-routing/bio-rd/net" + "github.com/stretchr/testify/assert" +) + +type failingUpdate struct{} + +func (f *failingUpdate) SerializeUpdate() ([]byte, error) { + return nil, errors.New("general error") +} + +type WriterByter interface { + Bytes() []byte + io.Writer +} + +type failingReadWriter struct { +} + +func (f *failingReadWriter) Write(p []byte) (n int, err error) { + return 0, errors.New("general error") +} + +func (f *failingReadWriter) Bytes() []byte { + return []byte{} +} + +func TestSerializeAndSendUpdate(t *testing.T) { + tests := []struct { + name string + buf WriterByter + err error + testUpdate serializeAbleUpdate + expected []byte + }{ + { + name: "normal bgp update", + buf: bytes.NewBuffer(nil), + err: nil, + testUpdate: &packet.BGPUpdate{ + WithdrawnRoutesLen: 5, + WithdrawnRoutes: &packet.NLRI{ + IP: strAddr("10.0.0.0"), + Pfxlen: 8, + Next: &packet.NLRI{ + IP: strAddr("192.168.0.0"), + Pfxlen: 16, + }, + }, + }, + + expected: []byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // Marker + 0, 28, // Length + 2, // Type = Update + 0, 5, 8, 10, 16, 192, 168, 0, 0, // 2 withdraws + }, + }, + { + name: "failed serialization", + buf: bytes.NewBuffer(nil), + err: nil, + testUpdate: &failingUpdate{}, + expected: nil, + }, + { + name: "failed connection", + buf: &failingReadWriter{}, + err: errors.New("Failed sending Update: general error"), + testUpdate: &packet.BGPUpdate{ + WithdrawnRoutesLen: 5, + WithdrawnRoutes: &packet.NLRI{ + IP: strAddr("10.0.0.0"), + Pfxlen: 8, + Next: &packet.NLRI{ + IP: strAddr("192.168.0.0"), + Pfxlen: 16, + }, + }, + }, + expected: []byte{}, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := serializeAndSendUpdate(test.buf, test.testUpdate) + assert.Equal(t, test.err, err) + + assert.Equal(t, test.expected, test.buf.Bytes()) + }) + + } +} + +func strAddr(s string) uint32 { + ret, _ := net.StrToAddr(s) + return ret +} diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go index 54c1d1b0cfa28c9094015d50483af1fd12bfd2e6..6c6b7dfd152c0cf387055e8e86a5d9fc03078000 100644 --- a/protocols/bgp/server/update_sender.go +++ b/protocols/bgp/server/update_sender.go @@ -34,26 +34,15 @@ func (u *UpdateSender) AddPath(pfx net.Prefix, p *route.Path) error { return nil } - update := &packet.BGPUpdateAddPath{ + update := &packet.BGPUpdate{ PathAttributes: pathAttrs, - NLRI: &packet.NLRIAddPath{ - PathIdentifier: p.BGPPath.PathIdentifier, - IP: pfx.Addr(), - Pfxlen: pfx.Pfxlen(), + NLRI: &packet.NLRI{ + IP: pfx.Addr(), + Pfxlen: pfx.Pfxlen(), }, } - updateBytes, err := update.SerializeUpdate() - if err != nil { - log.Errorf("Unable to serialize BGP Update: %v", err) - return nil - } - - _, err = u.fsm.con.Write(updateBytes) - if err != nil { - return fmt.Errorf("Failed sending Update: %v", err) - } - return nil + return serializeAndSendUpdate(u.fsm.con, update) } // RemovePath withdraws prefix `pfx` from a peer diff --git a/protocols/bgp/server/update_sender_add_path.go b/protocols/bgp/server/update_sender_add_path.go index 84d6796046b8b3380a6afae2f7ab1257b59a699c..5c9f545720e797309c80039a8ce466be2e72878b 100644 --- a/protocols/bgp/server/update_sender_add_path.go +++ b/protocols/bgp/server/update_sender_add_path.go @@ -1,8 +1,6 @@ package server import ( - "fmt" - log "github.com/sirupsen/logrus" "github.com/bio-routing/bio-rd/net" @@ -32,26 +30,15 @@ func (u *UpdateSenderAddPath) AddPath(pfx net.Prefix, p *route.Path) error { log.Errorf("Unable to create BGP Update: %v", err) return nil } - - update := &packet.BGPUpdate{ + update := &packet.BGPUpdateAddPath{ PathAttributes: pathAttrs, - NLRI: &packet.NLRI{ - IP: pfx.Addr(), - Pfxlen: pfx.Pfxlen(), + 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 - } - - _, err = u.fsm.con.Write(updateBytes) - if err != nil { - return fmt.Errorf("Failed sending Update: %v", err) - } - return nil + return serializeAndSendUpdate(u.fsm.con, update) } // RemovePath withdraws prefix `pfx` from a peer diff --git a/protocols/bgp/server/withdraw.go b/protocols/bgp/server/withdraw.go index 3769c63fa1dd5d9a59f200aadff4b945fae61d20..42e97a399cdcf8c208fc29063f7213e4d780ad96 100644 --- a/protocols/bgp/server/withdraw.go +++ b/protocols/bgp/server/withdraw.go @@ -35,12 +35,8 @@ func withDrawPrefixes(out io.Writer, prefixes ...net.Prefix) error { update := &packet.BGPUpdate{ WithdrawnRoutes: rootNLRI, } - data, err := update.SerializeUpdate() - if err != nil { - return err - } - _, err = out.Write(data) - return err + return serializeAndSendUpdate(out, update) + } // withDrawPrefixesAddPath generates a BGPUpdateAddPath message and write it to the given @@ -59,10 +55,5 @@ func withDrawPrefixesAddPath(out io.Writer, pfx net.Prefix, p *route.Path) error Pfxlen: pfx.Pfxlen(), }, } - data, err := update.SerializeUpdate() - if err != nil { - return err - } - _, err = out.Write(data) - return err + return serializeAndSendUpdate(out, update) } diff --git a/routingtable/table_test.go b/routingtable/table_test.go index a2eb10f76f93046fe71e98e59998ff52e15283b5..ef18c5175533a649504f4d6ade2a43edcda102ff 100644 --- a/routingtable/table_test.go +++ b/routingtable/table_test.go @@ -262,6 +262,12 @@ func TestGetLonger(t *testing.T) { route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil), }, }, + { + name: "Test 2: Empty root", + routes: nil, + needle: net.NewPfx(strAddr("10.0.0.0"), 8), + expected: []*route.Route{}, + }, } for _, test := range tests { @@ -445,6 +451,372 @@ func TestRemovePath(t *testing.T) { } } +func TestReplacePath(t *testing.T) { + tests := []struct { + name string + routes []*route.Route + replacePfx net.Prefix + replacePath *route.Path + expected []*route.Route + expectedOld []*route.Path + }{ + { + name: "replace in empty table", + replacePfx: net.NewPfx(strAddr("10.0.0.0"), 8), + replacePath: &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{}, + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{}, + }), + }, + expectedOld: nil, + }, + { + name: "replace not existing prefix with multiple paths", + routes: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1001, + NextHop: 101, + }, + }), + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + replacePfx: net.NewPfx(strAddr("10.0.0.0"), 8), + replacePath: &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1000, + }, + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1000, + }, + }), + newMultiPathRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1001, + NextHop: 101, + }, + }, &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + expectedOld: []*route.Path{}, + }, + { + name: "replace existing prefix with multiple paths", + routes: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 2, + }, + }), + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1001, + NextHop: 101, + }, + }), + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + replacePfx: net.NewPfx(strAddr("10.0.0.0"), 8), + replacePath: &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1000, + }, + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1000, + }, + }), + newMultiPathRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1001, + NextHop: 101, + }, + }, &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + expectedOld: []*route.Path{ + { + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }, + { + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 2, + }, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + rt := NewRoutingTable() + for _, route := range test.routes { + for _, p := range route.Paths() { + rt.AddPath(route.Prefix(), p) + } + } + + old := rt.ReplacePath(test.replacePfx, test.replacePath) + assert.ElementsMatch(t, test.expectedOld, old) + assert.ElementsMatch(t, test.expected, rt.Dump()) + }) + + } +} + +func TestRemovePrefix(t *testing.T) { + tests := []struct { + name string + routes []*route.Route + removePfx net.Prefix + expected []*route.Route + expectedOld []*route.Path + }{ + { + name: "remove in empty table", + removePfx: net.NewPfx(strAddr("10.0.0.0"), 8), + expected: []*route.Route{}, + expectedOld: nil, + }, + { + name: "remove not exist prefix", + routes: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + removePfx: net.NewPfx(strAddr("12.0.0.0"), 8), + expected: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + expectedOld: nil, + }, + { + name: "remove not existing more specific prefix", + routes: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + removePfx: net.NewPfx(strAddr("10.0.0.0"), 9), + expected: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + expectedOld: nil, + }, + { + name: "remove not existing more less prefix", + routes: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + removePfx: net.NewPfx(strAddr("10.0.0.0"), 7), + expected: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + expectedOld: nil, + }, + { + name: "remove existing prefix", + routes: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 2, + }, + }), + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + removePfx: net.NewPfx(strAddr("10.0.0.0"), 8), + expected: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + expectedOld: []*route.Path{ + { + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }, + { + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 2, + }, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + rt := NewRoutingTable() + for _, route := range test.routes { + for _, p := range route.Paths() { + rt.AddPath(route.Prefix(), p) + } + } + + old := rt.RemovePfx(test.removePfx) + assert.ElementsMatch(t, test.expectedOld, old) + assert.ElementsMatch(t, test.expected, rt.Dump()) + }) + + } +} + +func newMultiPathRoute(pfx net.Prefix, paths ...*route.Path) *route.Route { + if len(paths) == 0 { + return route.NewRoute(pfx, nil) + } + r := route.NewRoute(pfx, paths[0]) + for i := 1; i < len(paths); i++ { + r.AddPath(paths[i]) + } + return r + +} + func strAddr(s string) uint32 { ret, _ := net.StrToAddr(s) return ret