diff options
author | Marcin Ptaszynski <marcin.ptaszynski@ntti3.io> | 2018-07-12 17:13:15 -0700 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2018-08-17 16:50:18 +0900 |
commit | 08cd29ea36dffc21b9cead996254e5535868c71f (patch) | |
tree | 98884b8acd3b2c45c5887cc3c868a8990f2f1ec5 | |
parent | 540ee758f07696cfc5f35127dd11dfcd103ebd50 (diff) |
ignore duplicate RTC Membership announcements
-rw-r--r-- | internal/pkg/table/table_manager.go | 18 | ||||
-rw-r--r-- | pkg/server/server.go | 23 | ||||
-rw-r--r-- | pkg/server/server_test.go | 217 |
3 files changed, 246 insertions, 12 deletions
diff --git a/internal/pkg/table/table_manager.go b/internal/pkg/table/table_manager.go index e10f4d6a..2640f622 100644 --- a/internal/pkg/table/table_manager.go +++ b/internal/pkg/table/table_manager.go @@ -21,10 +21,10 @@ import ( "net" "time" - "github.com/osrg/gobgp/pkg/packet/bgp" - farm "github.com/dgryski/go-farm" log "github.com/sirupsen/logrus" + + "github.com/osrg/gobgp/pkg/packet/bgp" ) const ( @@ -335,6 +335,20 @@ func (manager *TableManager) GetPathListWithNexthop(id string, rfList []bgp.Rout return paths } +func (manager *TableManager) GetPathListWithSource(id string, rfList []bgp.RouteFamily, source *PeerInfo) []*Path { + paths := make([]*Path, 0, manager.getDestinationCount(rfList)) + for _, rf := range rfList { + if t, ok := manager.Tables[rf]; ok { + for _, path := range t.GetKnownPathList(id, 0) { + if path.GetSource().Equal(source) { + paths = append(paths, path) + } + } + } + } + return paths +} + func (manager *TableManager) GetDestination(path *Path) *Destination { if path == nil { return nil diff --git a/pkg/server/server.go b/pkg/server/server.go index c4177ab0..472da9ad 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -24,17 +24,16 @@ import ( "sync" "time" - api "github.com/osrg/gobgp/api" + "github.com/eapache/channels" + uuid "github.com/satori/go.uuid" + log "github.com/sirupsen/logrus" + api "github.com/osrg/gobgp/api" "github.com/osrg/gobgp/internal/pkg/apiutil" "github.com/osrg/gobgp/internal/pkg/config" "github.com/osrg/gobgp/internal/pkg/table" "github.com/osrg/gobgp/internal/pkg/zebra" "github.com/osrg/gobgp/pkg/packet/bgp" - - "github.com/eapache/channels" - uuid "github.com/satori/go.uuid" - log "github.com/sirupsen/logrus" ) type TCPListener struct { @@ -987,7 +986,19 @@ func (server *BgpServer) propagateUpdate(peer *Peer, pathList []*table.Path) { // given RT on RTM NLRI is already removed from adj-RIB-in. _, candidates = server.getBestFromLocal(peer, fs) } else { - candidates = server.globalRib.GetBestPathList(peer.TableID(), 0, fs) + // https://github.com/osrg/gobgp/issues/1777 + // Ignore duplicate Membership announcements + membershipsForSource := server.globalRib.GetPathListWithSource(table.GLOBAL_RIB_NAME, []bgp.RouteFamily{bgp.RF_RTC_UC}, path.GetSource()) + found := false + for _, membership := range membershipsForSource { + if membership.GetNlri().(*bgp.RouteTargetMembershipNLRI).RouteTarget.String() == rt.String() { + found = true + break + } + } + if !found { + candidates = server.globalRib.GetBestPathList(peer.TableID(), 0, fs) + } } paths := make([]*table.Path, 0, len(candidates)) for _, p := range candidates { diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go index a7142079..742d9b93 100644 --- a/pkg/server/server_test.go +++ b/pkg/server/server_test.go @@ -22,14 +22,15 @@ import ( "testing" "time" + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + api "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/internal/pkg/apiutil" "github.com/osrg/gobgp/internal/pkg/config" "github.com/osrg/gobgp/internal/pkg/table" "github.com/osrg/gobgp/pkg/packet/bgp" - - log "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestModPolicyAssign(t *testing.T) { @@ -722,3 +723,211 @@ func TestFamiliesForSoftreset(t *testing.T) { assert.Equal(t, len(families), 2) assert.NotContains(t, families, bgp.RF_RTC_UC) } + +func runNewServer(ctx context.Context, as uint32, routerId string, listenPort int32) (*BgpServer, error) { + s := NewBgpServer() + ctxInner, cancelInner := context.WithCancel(ctx) + go s.Serve() + go func() { + <-ctxInner.Done() + stopCtx, _ := context.WithTimeout(context.Background(), 10*time.Second) + if err := s.StopBgp(stopCtx, &api.StopBgpRequest{}); err != nil { + log.Fatalf("Failed to stop server %s: %s", s.bgpConfig.Global.Config.RouterId, err) + } + }() + + err := s.StartBgp(ctx, &api.StartBgpRequest{ + Global: &api.Global{ + As: as, + RouterId: routerId, + ListenPort: listenPort, + }, + }) + if err != nil { + cancelInner() + return nil, err + } + + return s, nil +} + +func peerServers(t *testing.T, ctx context.Context, servers []*BgpServer, families []config.AfiSafiType) error { + for i, server := range servers { + for j, peer := range servers { + if i == j { + continue + } + + neighborConfig := &config.Neighbor{ + Config: config.NeighborConfig{ + NeighborAddress: "127.0.0.1", + PeerAs: peer.bgpConfig.Global.Config.As, + }, + AfiSafis: config.AfiSafis{}, + Transport: config.Transport{ + Config: config.TransportConfig{ + RemotePort: uint16(peer.bgpConfig.Global.Config.Port), + }, + }, + } + + // first server to get neighbor config is passive to hopefully make handshake faster + if j > i { + neighborConfig.Transport.Config.PassiveMode = true + } + + for _, family := range families { + neighborConfig.AfiSafis = append(neighborConfig.AfiSafis, config.AfiSafi{ + Config: config.AfiSafiConfig{ + AfiSafiName: family, + Enabled: true, + }, + }) + } + + if err := server.AddPeer(ctx, &api.AddPeerRequest{Peer: NewPeerFromConfigStruct(neighborConfig)}); err != nil { + t.Fatal(err) + } + } + } + + return nil +} + +func parseRDRT(rdStr string) (bgp.RouteDistinguisherInterface, bgp.ExtendedCommunityInterface, error) { + rd, err := bgp.ParseRouteDistinguisher(rdStr) + if err != nil { + return nil, nil, err + } + + rt, err := bgp.ParseExtendedCommunity(bgp.EC_SUBTYPE_ROUTE_TARGET, rdStr) + if err != nil { + return nil, nil, err + } + return rd, rt, nil +} + +func addVrf(t *testing.T, ctx context.Context, s *BgpServer, vrfName, rdStr string, id uint32) { + rd, rt, err := parseRDRT(rdStr) + if err != nil { + t.Fatal(err) + } + + req := &api.AddVrfRequest{ + Vrf: &api.Vrf{ + Name: vrfName, + ImportRt: apiutil.MarshalRTs([]bgp.ExtendedCommunityInterface{rt}), + ExportRt: apiutil.MarshalRTs([]bgp.ExtendedCommunityInterface{rt}), + Rd: apiutil.MarshalRD(rd), + Id: id, + }, + } + if err = s.AddVrf(ctx, req); err != nil { + t.Fatal(err) + } +} + +func TestDoNotReactToDuplicateRTCMemberships(t *testing.T) { + // missing it may cause Test_DialTCP_FDleak to fail + defer time.Sleep(time.Second) + + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) + defer cancel() + log.SetLevel(log.DebugLevel) + + s1, err := runNewServer(ctx, 1, "1.1.1.1", 10179) + if err != nil { + t.Fatal(err) + } + s2, err := runNewServer(ctx, 1, "2.2.2.2", 20179) + if err != nil { + t.Fatal(err) + } + + addVrf(t, ctx, s1, "vrf1", "111:111", 1) + addVrf(t, ctx, s2, "vrf1", "111:111", 1) + + if err = peerServers(t, ctx, []*BgpServer{s1, s2}, []config.AfiSafiType{config.AFI_SAFI_TYPE_L3VPN_IPV4_UNICAST, config.AFI_SAFI_TYPE_RTC}); err != nil { + t.Fatal(err) + } + watcher := s1.Watch(WatchUpdate(true)) + + // Add route to vrf1 on s2 + attrs := []bgp.PathAttributeInterface{ + bgp.NewPathAttributeOrigin(0), + bgp.NewPathAttributeNextHop("2.2.2.2"), + } + prefix := bgp.NewIPAddrPrefix(24, "10.30.2.0") + path := apiutil.NewPath(prefix, false, attrs, time.Now()) + + if _, err := s2.AddPath(ctx, &api.AddPathRequest{ + Resource: api.Resource_VRF, + VrfId: "vrf1", + Path: path, + }); err != nil { + t.Fatal(err) + } + + // s1 should receive this route from s2 + + for found := false; !found; { + select { + case ev := <-watcher.Event(): + switch msg := ev.(type) { + case *WatchEventUpdate: + for _, path := range msg.PathList { + log.Infof("tester received path: %s", path.String()) + if vpnPath, ok := path.GetNlri().(*bgp.LabeledVPNIPAddrPrefix); ok { + if vpnPath.Prefix.Equal(prefix.Prefix) { + log.Infof("tester found expected prefix: %s", vpnPath.Prefix) + found = true + } else { + log.Infof("unknown prefix %s != %s", vpnPath.Prefix, prefix.Prefix) + } + } + } + } + case <-ctx.Done(): + t.Fatalf("timeout while waiting for update path event") + } + } + + // fabricate duplicated rtc message from s1 + // s2 should not send vpn route again + _, rt, err := parseRDRT("111:111") + if err != nil { + t.Fatal(err) + } + rtcNLRI := bgp.NewRouteTargetMembershipNLRI(1, rt) + rtcPath := table.NewPath(&table.PeerInfo{ + AS: 1, + Address: net.ParseIP("127.0.0.1"), + LocalID: net.ParseIP("2.2.2.2"), + ID: net.ParseIP("1.1.1.1"), + }, rtcNLRI, false, []bgp.PathAttributeInterface{ + bgp.NewPathAttributeOrigin(0), + bgp.NewPathAttributeNextHop("1.1.1.1"), + }, time.Now(), false) + + s1Peer := s2.neighborMap["127.0.0.1"] + s2.propagateUpdate(s1Peer, []*table.Path{rtcPath}) + + awaitUpdateCtx, _ := context.WithTimeout(ctx, time.Second) + for done := false; !done; { + select { + case ev := <-watcher.Event(): + switch msg := ev.(type) { + case *WatchEventUpdate: + for _, path := range msg.PathList { + log.Infof("tester received path: %s", path.String()) + if vpnPath, ok := path.GetNlri().(*bgp.LabeledVPNIPAddrPrefix); ok { + t.Fatalf("vpn prefix %s was unexpectedly received", vpnPath.Prefix) + } + } + } + case <-awaitUpdateCtx.Done(): + log.Infof("await update done") + done = true + } + } +} |