diff --git a/routingtable/adjRIBOut/BUILD.bazel b/routingtable/adjRIBOut/BUILD.bazel index b7077150d43164d6ce1dcfee524452c0f1c47232..f4c213395b35ca5b183522605f0e130f54140be9 100644 --- a/routingtable/adjRIBOut/BUILD.bazel +++ b/routingtable/adjRIBOut/BUILD.bazel @@ -19,10 +19,17 @@ go_library( go_test( name = "go_default_test", - srcs = ["path_id_manager_test.go"], + srcs = [ + "adj_rib_out_test.go", + "path_id_manager_test.go", + ], embed = [":go_default_library"], deps = [ + "//net:go_default_library", + "//protocols/bgp/types:go_default_library", "//route:go_default_library", + "//routingtable:go_default_library", + "//routingtable/filter:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library", ], ) diff --git a/routingtable/adjRIBOut/adj_rib_out_test.go b/routingtable/adjRIBOut/adj_rib_out_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0d01dd0890f06f1c518101205755162b10081c3e --- /dev/null +++ b/routingtable/adjRIBOut/adj_rib_out_test.go @@ -0,0 +1,1121 @@ +package adjRIBOut + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/protocols/bgp/types" + "github.com/bio-routing/bio-rd/routingtable/filter" + + "github.com/bio-routing/bio-rd/route" + "github.com/bio-routing/bio-rd/routingtable" +) + +func TestBestPathOnlyEBGP(t *testing.T) { + neighborBestOnlyEBGP := &routingtable.Neighbor{ + Type: route.BGPPathType, + LocalAddress: net.IPv4FromOctets(127, 0, 0, 1), + Address: net.IPv4FromOctets(127, 0, 0, 2), + IBGP: false, + LocalASN: 41981, + RouteServerClient: false, + CapAddPathRX: false, + } + + adjRIBOut := New(neighborBestOnlyEBGP, filter.NewAcceptAllFilter()) + + tests := []struct { + name string + routesAdd []*route.Route + routesRemove []*route.Route + expected []*route.Route + expectedCount int64 + }{ + { + name: "Add a valid route", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{}, + }), + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{NextHop: neighborBestOnlyEBGP.LocalAddress, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + neighborBestOnlyEBGP.LocalASN, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: false, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 0, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expectedCount: 1, + }, + { + name: "Try to remove unpresent route", + routesRemove: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{NextHop: neighborBestOnlyEBGP.LocalAddress, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + neighborBestOnlyEBGP.LocalASN, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 1, + EBGP: false, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 0, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{NextHop: neighborBestOnlyEBGP.LocalAddress, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + neighborBestOnlyEBGP.LocalASN, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: false, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 0, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expectedCount: 1, + }, + { + name: "Remove route added in first step", + routesRemove: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{NextHop: neighborBestOnlyEBGP.LocalAddress, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + neighborBestOnlyEBGP.LocalASN, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: false, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 0, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expected: []*route.Route{}, + expectedCount: 0, + }, + { + name: "Try to add route with NO_EXPORT community set", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + Communities: []uint32{ + types.WellKnownCommunityNoExport, + }, + }, + }), + }, + expected: []*route.Route{}, + }, + { + name: "Try to add route with NO_ADVERTISE community set", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + Communities: []uint32{ + types.WellKnownCommunityNoAdvertise, + }, + }, + }), + }, + expected: []*route.Route{}, + expectedCount: 0, + }, + { + name: "Re-add valid route again", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{}, + }), + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{NextHop: neighborBestOnlyEBGP.LocalAddress, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + neighborBestOnlyEBGP.LocalASN, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: false, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 0, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expectedCount: 1, + }, + { + name: "Try to remove route with NO_EXPORT community set", + routesRemove: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{NextHop: neighborBestOnlyEBGP.LocalAddress, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + neighborBestOnlyEBGP.LocalASN, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: false, + Communities: []uint32{ + types.WellKnownCommunityNoExport, + }, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 0, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{NextHop: neighborBestOnlyEBGP.LocalAddress, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + neighborBestOnlyEBGP.LocalASN, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: false, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 0, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expectedCount: 1, + }, + { + name: "Try to remove non-existent prefix", + routesRemove: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 23, 42, 0), 24), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{}, + }), + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{NextHop: neighborBestOnlyEBGP.LocalAddress, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + neighborBestOnlyEBGP.LocalASN, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: false, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 0, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expectedCount: 1, + }, + } + + for i, test := range tests { + fmt.Printf("Running eBGP best only test #%d: %s\n", i+1, test.name) + for _, route := range test.routesAdd { + adjRIBOut.AddPath(route.Prefix(), route.Paths()[0]) + } + + for _, route := range test.routesRemove { + adjRIBOut.RemovePath(route.Prefix(), route.Paths()[0]) + } + + assert.Equal(t, test.expected, adjRIBOut.rt.Dump()) + + actualCount := adjRIBOut.RouteCount() + if test.expectedCount != actualCount { + t.Errorf("Expected route count %d differs from actual route count %d!\n", test.expectedCount, actualCount) + } + } +} + +func TestBestPathOnlyIBGP(t *testing.T) { + neighborBestOnlyEBGP := &routingtable.Neighbor{ + Type: route.BGPPathType, + LocalAddress: net.IPv4FromOctets(127, 0, 0, 1), + Address: net.IPv4FromOctets(127, 0, 0, 2), + IBGP: true, + LocalASN: 41981, + RouteServerClient: false, + CapAddPathRX: false, + } + + adjRIBOut := New(neighborBestOnlyEBGP, filter.NewAcceptAllFilter()) + + tests := []struct { + name string + routesAdd []*route.Route + routesRemove []*route.Route + expected []*route.Route + expectedCount int64 + }{ + { + name: "Add an iBGP route (without success)", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{}, + }), + }, + expected: []*route.Route{}, + expectedCount: 0, + }, + { + name: "Add an eBGP route (with success)", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + EBGP: true, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + }, + }), + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 0, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expectedCount: 1, + }, + { + name: "Try to remove slightly different route", + routesRemove: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 1, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 0, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 0, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expectedCount: 1, + }, + { + name: "Remove route added in 2nd step", + routesRemove: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 0, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expected: []*route.Route{}, + expectedCount: 0, + }, + { + name: "Try to add route with NO_EXPORT community set (without success)", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + Communities: []uint32{ + types.WellKnownCommunityNoExport, + }, + }, + }), + }, + expected: []*route.Route{}, + }, + { + name: "Try to add route with NO_EXPORT community set (without success)", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + Communities: []uint32{ + types.WellKnownCommunityNoAdvertise, + }, + }, + }), + }, + expected: []*route.Route{}, + }, + } + + for i, test := range tests { + fmt.Printf("Running iBGP best only test #%d: %s\n", i+1, test.name) + for _, route := range test.routesAdd { + adjRIBOut.AddPath(route.Prefix(), route.Paths()[0]) + } + + for _, route := range test.routesRemove { + adjRIBOut.RemovePath(route.Prefix(), route.Paths()[0]) + } + + assert.Equal(t, test.expected, adjRIBOut.rt.Dump()) + + actualCount := adjRIBOut.RouteCount() + if test.expectedCount != actualCount { + t.Errorf("Expected route count %d differs from actual route count %d!\n", test.expectedCount, actualCount) + } + } +} + +/* + * Test for AddPath capabale peer / AdjRIBOut + */ + +func TestAddPathIBGP(t *testing.T) { + neighborBestOnlyEBGP := &routingtable.Neighbor{ + Type: route.BGPPathType, + LocalAddress: net.IPv4FromOctets(127, 0, 0, 1), + Address: net.IPv4FromOctets(127, 0, 0, 2), + IBGP: true, + LocalASN: 41981, + RouteServerClient: false, + CapAddPathRX: true, + } + + adjRIBOut := New(neighborBestOnlyEBGP, filter.NewAcceptAllFilter()) + + tests := []struct { + name string + routesAdd []*route.Route + routesRemove []*route.Route + expected []*route.Route + expectedCount int64 + }{ + { + name: "Add an iBGP route (without success)", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{}, + }), + }, + expected: []*route.Route{}, + expectedCount: 0, + }, + { + name: "Add an eBGP route (with success)", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + EBGP: true, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + }, + }), + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 1, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expectedCount: 1, + }, + { + name: "Try to remove slightly different route", + routesRemove: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 1, // MED of route present in table is 0 + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 0, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 1, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expectedCount: 1, + }, + { + name: "Remove route added in 2nd step", + routesRemove: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 0, // We calculate PathID in RIBOut so none is present when removing from RIBOut + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expected: []*route.Route{}, + expectedCount: 0, + }, + { + name: "Try to add route with NO_EXPORT community set (without success)", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + Communities: []uint32{ + types.WellKnownCommunityNoExport, + }, + }, + }), + }, + expected: []*route.Route{}, + expectedCount: 0, + }, + { + name: "Try to add route with NO_EXPORT community set (without success)", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + Communities: []uint32{ + types.WellKnownCommunityNoAdvertise, + }, + }, + }), + }, + expected: []*route.Route{}, + expectedCount: 0, + }, + + // Ok table is empty, re add previous route + { + name: "Readd an eBGP route (with success)", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + EBGP: true, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + }, + }), + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 2, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expectedCount: 1, + }, + { + name: "Add 2nd path to existing one with different NH (with success)", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + EBGP: true, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + NextHop: net.IPv4FromOctets(2, 3, 4, 5), + }, + }), + }, + expected: []*route.Route{ + route.NewRouteAddPath(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), []*route.Path{ + &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 2, + LocalPref: 0, + Source: net.IP{}}, + }, + &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(2, 3, 4, 5), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 3, + LocalPref: 0, + Source: net.IP{}}, + }}), + }, + expectedCount: 1, + }, + { + name: "Remove 2nd path added above", + routesRemove: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(2, 3, 4, 5), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 0, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 2, + LocalPref: 0, + Source: net.IP{}}, + }), + }, + expectedCount: 1, + }, + { + name: "Re-add 2nd path to existing one with different NH (with success)", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + EBGP: true, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + NextHop: net.IPv4FromOctets(3, 4, 5, 6), + }, + }), + }, + expected: []*route.Route{ + route.NewRouteAddPath(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), []*route.Path{ + &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 2, + LocalPref: 0, + Source: net.IP{}}, + }, + &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(3, 4, 5, 6), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 4, + LocalPref: 0, + Source: net.IP{}}, + }}), + }, + expectedCount: 1, + }, + { + name: "Add 3rd path to existing ones, containing NO_EXPORT community (successful)", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + EBGP: true, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + NextHop: net.IPv4FromOctets(4, 5, 6, 7), + Communities: []uint32{ + types.WellKnownCommunityNoExport, + }, + }, + }), + }, + expected: []*route.Route{ + route.NewRouteAddPath(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), []*route.Path{ + &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(1, 2, 3, 4), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 2, + LocalPref: 0, + Source: net.IP{}}, + }, + &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(3, 4, 5, 6), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{}, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 4, + LocalPref: 0, + Source: net.IP{}}, + }, + &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + NextHop: net.IPv4FromOctets(4, 5, 6, 7), + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + Origin: 0, + MED: 0, + EBGP: true, + Communities: []uint32{ + types.WellKnownCommunityNoExport, + }, + LargeCommunities: []types.LargeCommunity{}, + UnknownAttributes: nil, + PathIdentifier: 5, + LocalPref: 0, + Source: net.IP{}}, + }, + }), + }, + expectedCount: 1, + }, + { + name: "Add 4th path to existing ones, containing NO_ADVERTISE community", + routesAdd: []*route.Route{ + route.NewRoute(net.NewPfx(net.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + EBGP: true, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{ + 201701, + }, + }, + }, + ASPathLen: 1, + NextHop: net.IPv4FromOctets(5, 6, 7, 8), + Communities: []uint32{ + types.WellKnownCommunityNoAdvertise, + }, + }, + }), + }, + expected: []*route.Route{}, + expectedCount: 0, + }, + } + + for i, test := range tests { + fmt.Printf("Running iBGP AddPath test #%d: %s\n", i+1, test.name) + for _, route := range test.routesAdd { + adjRIBOut.AddPath(route.Prefix(), route.Paths()[0]) + } + + for _, route := range test.routesRemove { + adjRIBOut.RemovePath(route.Prefix(), route.Paths()[0]) + } + + assert.Equal(t, test.expected, adjRIBOut.rt.Dump()) + + actualCount := adjRIBOut.RouteCount() + if test.expectedCount != actualCount { + t.Errorf("Expected route count %d differs from actual route count %d!\n", test.expectedCount, actualCount) + } + } +}