diff --git a/route/route.go b/route/route.go index f9251bbcef51371005215b99b1efa58fae17d26a..e6b46ee6bd3e4bd69927f9cb97ec386dae0aaa56 100644 --- a/route/route.go +++ b/route/route.go @@ -28,7 +28,7 @@ type Route struct { ecmpPaths uint } -// NewRoute generates a new route with paths p +// NewRoute generates a new route with path p func NewRoute(pfx net.Prefix, p *Path) *Route { r := &Route{ pfx: pfx, @@ -43,6 +43,23 @@ func NewRoute(pfx net.Prefix, p *Path) *Route { return r } +// NewRouteAddPath generates a new route with paths p +func NewRouteAddPath(pfx net.Prefix, p []*Path) *Route { + r := &Route{ + pfx: pfx, + } + + if p == nil { + r.paths = make([]*Path, 0) + return r + } + + for _, path := range p { + r.paths = append(r.paths, path) + } + return r +} + // Copy returns a copy of route r func (r *Route) Copy() *Route { if r == nil { 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.go b/routingtable/adjRIBOut/adj_rib_out.go index a2179fc02d3d9cff82bc67f95ba1e86fa2a293bd..2685ce37e45bd40fb91f891190a82e2054fee472 100644 --- a/routingtable/adjRIBOut/adj_rib_out.go +++ b/routingtable/adjRIBOut/adj_rib_out.go @@ -47,6 +47,9 @@ func (a *AdjRIBOut) RouteCount() int64 { // AddPath adds path p to prefix `pfx` func (a *AdjRIBOut) AddPath(pfx bnet.Prefix, p *route.Path) error { if !routingtable.ShouldPropagateUpdate(pfx, p, a.neighbor) { + if a.neighbor.CapAddPathRX { + a.removePathsForPrefix(pfx) + } return nil } @@ -55,6 +58,7 @@ func (a *AdjRIBOut) AddPath(pfx bnet.Prefix, p *route.Path) error { return nil } + // If the neighbor is an eBGP peer and not a Route Server client modify ASPath and Next Hop p = p.Copy() if !a.neighbor.IBGP && !a.neighbor.RouteServerClient { p.BGPPath.Prepend(a.neighbor.LocalASN, 1) @@ -130,6 +134,25 @@ func (a *AdjRIBOut) RemovePath(pfx bnet.Prefix, p *route.Path) bool { return true } +func (a *AdjRIBOut) removePathsForPrefix(pfx bnet.Prefix) bool { + // We were called before a.AddPath() had a lock, so we need to lock here and release it + // after the get to prevent a dead lock as RemovePath() will acquire a lock itself! + a.mu.Lock() + r := a.rt.Get(pfx) + a.mu.Unlock() + + // If no path with this prefix is present, we're done + if r == nil { + return false + } + + for _, path := range r.Paths() { + a.RemovePath(pfx, path) + } + + return true +} + func (a *AdjRIBOut) isOwnPath(p *route.Path) bool { if p.Type != a.neighbor.Type { return false 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) + } + } +}