From f4cfa0b2a9a36f5bec917b7306aed4dd4d17f9ea Mon Sep 17 00:00:00 2001 From: Nischal Sheth Date: Wed, 6 Jan 2016 17:30:08 -0800 Subject: [PATCH] Prefer path with shorter cluster list length Highlights: - Define ClusterListSpec, ClusterList and ClusterListDB - Use cluster list length in path selection - Add cluster list to route introspect - Add unit tests for decode/encode and path selection Change-Id: If7c15cc84d85fe31f8aafc48b54586457806e433 Closes-Bug: 1531679 --- src/bgp/bgp_attr.cc | 48 +++++++++++++++++++ src/bgp/bgp_attr.h | 84 +++++++++++++++++++++++++++++++++- src/bgp/bgp_message_builder.cc | 6 +++ src/bgp/bgp_path.cc | 2 + src/bgp/bgp_peer.sandesh | 1 + src/bgp/bgp_proto.cc | 19 ++++++++ src/bgp/bgp_route.cc | 12 +++++ src/bgp/bgp_server.cc | 1 + src/bgp/bgp_server.h | 3 ++ src/bgp/test/bgp_attr_test.cc | 48 +++++++++++++++++++ src/bgp/test/bgp_proto_test.cc | 46 +++++++++++++++++++ src/bgp/test/bgp_route_test.cc | 69 ++++++++++++++++++++++++++++ 12 files changed, 338 insertions(+), 1 deletion(-) diff --git a/src/bgp/bgp_attr.cc b/src/bgp/bgp_attr.cc index e0c9d0a8831..bdc7b667f65 100644 --- a/src/bgp/bgp_attr.cc +++ b/src/bgp/bgp_attr.cc @@ -139,6 +139,44 @@ std::string BgpAttrOriginatorId::ToString() const { return std::string(repr); } +int ClusterListSpec::CompareTo(const BgpAttribute &rhs_attr) const { + int ret = BgpAttribute::CompareTo(rhs_attr); + if (ret != 0) return ret; + KEY_COMPARE(cluster_list, + static_cast(rhs_attr).cluster_list); + return 0; +} + +void ClusterListSpec::ToCanonical(BgpAttr *attr) { + attr->set_cluster_list(this); +} + +std::string ClusterListSpec::ToString() const { + std::stringstream repr; + repr << "CLUSTER_LIST :"; + for (std::vector::const_iterator iter = cluster_list.begin(); + iter != cluster_list.end(); ++iter) { + repr << " " << Ip4Address(*iter).to_string(); + } + repr << std::endl; + return repr.str(); +} + +ClusterList::ClusterList(ClusterListDB *cluster_list_db, + const ClusterListSpec &spec) + : cluster_list_db_(cluster_list_db), + spec_(spec) { + refcount_ = 0; +} + +void ClusterList::Remove() { + cluster_list_db_->Delete(this); +} + +ClusterListDB::ClusterListDB(BgpServer *server) { +} + int BgpMpNlri::CompareTo(const BgpAttribute &rhs_attr) const { int ret = BgpAttribute::CompareTo(rhs_attr); if (ret != 0) return ret; @@ -786,6 +824,7 @@ BgpAttr::BgpAttr(const BgpAttr &rhs) originator_id_(rhs.originator_id_), source_rd_(rhs.source_rd_), esi_(rhs.esi_), params_(rhs.params_), as_path_(rhs.as_path_), + cluster_list_(rhs.cluster_list_), community_(rhs.community_), ext_community_(rhs.ext_community_), origin_vn_path_(rhs.origin_vn_path_), @@ -810,6 +849,14 @@ void BgpAttr::set_as_path(const AsPathSpec *spec) { } } +void BgpAttr::set_cluster_list(const ClusterListSpec *spec) { + if (spec) { + cluster_list_ = attr_db_->server()->cluster_list_db()->Locate(*spec); + } else { + cluster_list_ = NULL; + } +} + void BgpAttr::set_community(CommunityPtr comm) { community_ = comm; } @@ -933,6 +980,7 @@ int BgpAttr::CompareTo(const BgpAttr &rhs) const { KEY_COMPARE(olist_.get(), rhs.olist_.get()); KEY_COMPARE(leaf_olist_.get(), rhs.leaf_olist_.get()); KEY_COMPARE(as_path_.get(), rhs.as_path_.get()); + KEY_COMPARE(cluster_list_.get(), rhs.cluster_list_.get()); KEY_COMPARE(community_.get(), rhs.community_.get()); KEY_COMPARE(ext_community_.get(), rhs.ext_community_.get()); KEY_COMPARE(origin_vn_path_.get(), rhs.origin_vn_path_.get()); diff --git a/src/bgp/bgp_attr.h b/src/bgp/bgp_attr.h index d0dad94fd37..7d60ff1ac47 100644 --- a/src/bgp/bgp_attr.h +++ b/src/bgp/bgp_attr.h @@ -29,7 +29,7 @@ // all information in the UPDATE except: NLRI (prefix) and label. class BgpAttr; -class AsPathDB; +class ClusterListDB; struct BgpAttrOrigin : public BgpAttribute { static const int kSize = 1; @@ -150,6 +150,82 @@ struct BgpAttrOriginatorId : public BgpAttribute { virtual std::string ToString() const; }; +struct ClusterListSpec : public BgpAttribute { + static const int kSize = -1; + static const uint8_t kFlags = Optional; + ClusterListSpec() : BgpAttribute(ClusterList, kFlags) { } + explicit ClusterListSpec(const BgpAttribute &rhs) : BgpAttribute(rhs) { } + explicit ClusterListSpec(const ClusterListSpec &rhs) + : BgpAttribute(BgpAttribute::ClusterList, kFlags) { + cluster_list = rhs.cluster_list; + } + virtual int CompareTo(const BgpAttribute &rhs) const; + virtual void ToCanonical(BgpAttr *attr); + virtual std::string ToString() const; + std::vector cluster_list; +}; + +class ClusterList { +public: + ClusterList(ClusterListDB *cluster_list_db, const ClusterListSpec &spec); + ~ClusterList() { } + void Remove(); + int CompareTo(const ClusterList &rhs) const { + return spec_.CompareTo(rhs.cluster_list()); + } + + const ClusterListSpec &cluster_list() const { return spec_; } + size_t size() const { return spec_.cluster_list.size(); } + + friend std::size_t hash_value(const ClusterList &cluster_list) { + size_t hash = 0; + return hash; + } + +private: + friend int intrusive_ptr_add_ref(const ClusterList *ccluster_list); + friend int intrusive_ptr_del_ref(const ClusterList *ccluster_list); + friend void intrusive_ptr_release(const ClusterList *ccluster_list); + + mutable tbb::atomic refcount_; + ClusterListDB *cluster_list_db_; + ClusterListSpec spec_; +}; + +inline int intrusive_ptr_add_ref(const ClusterList *ccluster_list) { + return ccluster_list->refcount_.fetch_and_increment(); +} + +inline int intrusive_ptr_del_ref(const ClusterList *ccluster_list) { + return ccluster_list->refcount_.fetch_and_decrement(); +} + +inline void intrusive_ptr_release(const ClusterList *ccluster_list) { + int prev = ccluster_list->refcount_.fetch_and_decrement(); + if (prev == 1) { + ClusterList *cluster_list = const_cast(ccluster_list); + cluster_list->Remove(); + assert(cluster_list->refcount_ == 0); + delete cluster_list; + } +} + +typedef boost::intrusive_ptr ClusterListPtr; + +struct ClusterListCompare { + bool operator()(const ClusterList *lhs, const ClusterList *rhs) { + return lhs->CompareTo(*rhs) < 0; + } +}; + +class ClusterListDB : public BgpPathAttributeDB { +public: + explicit ClusterListDB(BgpServer *server); +}; + struct BgpMpNlri : public BgpAttribute { static const int kSize = -1; static const uint8_t kFlags = Optional; @@ -703,6 +779,7 @@ class BgpAttr { void set_params(uint64_t params) { params_ = params; } void set_as_path(AsPathPtr aspath); void set_as_path(const AsPathSpec *spec); + void set_cluster_list(const ClusterListSpec *spec); void set_community(CommunityPtr comm); void set_community(const CommunitySpec *comm); void set_ext_community(ExtCommunityPtr extcomm); @@ -731,6 +808,10 @@ class BgpAttr { uint64_t params() const { return params_; } const AsPath *as_path() const { return as_path_.get(); } int as_path_count() const { return as_path_ ? as_path_->AsCount() : 0; } + const ClusterList *cluster_list() const { return cluster_list_.get(); } + size_t cluster_list_length() const { + return cluster_list_ ? cluster_list_->size() : 0; + } const Community *community() const { return community_.get(); } const ExtCommunity *ext_community() const { return ext_community_.get(); } const OriginVnPath *origin_vn_path() const { return origin_vn_path_.get(); } @@ -767,6 +848,7 @@ class BgpAttr { EthernetSegmentId esi_; uint64_t params_; AsPathPtr as_path_; + ClusterListPtr cluster_list_; CommunityPtr community_; ExtCommunityPtr ext_community_; OriginVnPathPtr origin_vn_path_; diff --git a/src/bgp/bgp_message_builder.cc b/src/bgp/bgp_message_builder.cc index a60a812b5b9..19727cda421 100644 --- a/src/bgp/bgp_message_builder.cc +++ b/src/bgp/bgp_message_builder.cc @@ -59,6 +59,12 @@ bool BgpMessage::StartReach(const RibOutAttr *roattr, const BgpRoute *route) { update.path_attributes.push_back(originator_id); } + if (attr->cluster_list()) { + ClusterListSpec *clist = + new ClusterListSpec(attr->cluster_list()->cluster_list()); + update.path_attributes.push_back(clist); + } + if (attr->as_path()) { AsPathSpec *path = new AsPathSpec(attr->as_path()->path()); update.path_attributes.push_back(path); diff --git a/src/bgp/bgp_path.cc b/src/bgp/bgp_path.cc index abe8b8501aa..6bca7a7db66 100644 --- a/src/bgp/bgp_path.cc +++ b/src/bgp/bgp_path.cc @@ -119,6 +119,8 @@ int BgpPath::PathCompare(const BgpPath &rhs, bool allow_ecmp) const { uint32_t rid = rorig_id ? rorig_id : rhs.peer_->bgp_identifier(); KEY_COMPARE(id, rid); + KEY_COMPARE(attr_->cluster_list_length(), rattr->cluster_list_length()); + const BgpPeer *lpeer = dynamic_cast(peer_); const BgpPeer *rpeer = dynamic_cast(rhs.peer_); if (lpeer != NULL && rpeer != NULL) { diff --git a/src/bgp/bgp_peer.sandesh b/src/bgp/bgp_peer.sandesh index 2ab4d516206..45689849ab0 100644 --- a/src/bgp/bgp_peer.sandesh +++ b/src/bgp/bgp_peer.sandesh @@ -152,6 +152,7 @@ struct ShowRoutePath { 20: list origin_vn_path; 21: ShowPmsiTunnel pmsi_tunnel; 22: ShowLoadBalance load_balance; + 23: list cluster_list; } struct ShowRoute { diff --git a/src/bgp/bgp_proto.cc b/src/bgp/bgp_proto.cc index 372c1680ac2..bff042f8ec1 100644 --- a/src/bgp/bgp_proto.cc +++ b/src/bgp/bgp_proto.cc @@ -876,6 +876,23 @@ class BgpPathAttributeExtendedCommunities : BgpPathAttributeExtendedCommunityList> Sequence; }; +class BgpPathAttributeClusterListData : + public ProtoElement { +public: + static const int kSize = -1; + typedef VectorAccessor Setter; +}; + +class BgpPathAttributeClusterList : + public ProtoSequence { +public: + typedef ClusterListSpec ContextType; + typedef BgpContextSwap ContextSwap; + typedef mpl::list Sequence; +}; + class BgpPathAttributeOriginVnList : public ProtoElement { public: @@ -1455,6 +1472,8 @@ class BgpPathAttribute : public ProtoChoice { mpl::pair, BgpAttrTemplate >, + mpl::pair, + BgpPathAttributeClusterList>, mpl::pair, BgpPathAttributeMpReachNlriSequence>, mpl::pair, diff --git a/src/bgp/bgp_route.cc b/src/bgp/bgp_route.cc index d6fe25c3ae4..b29e66b0885 100644 --- a/src/bgp/bgp_route.cc +++ b/src/bgp/bgp_route.cc @@ -310,6 +310,15 @@ void BgpRoute::FillRouteInfo(const BgpTable *table, show_route->set_paths(show_route_paths); } +static void FillRoutePathClusterListInfo(const ClusterList *clist, + ShowRoutePath *show_path) { + const vector &list = clist->cluster_list().cluster_list; + for (vector::const_iterator it = list.begin(); it != list.end(); + ++it) { + show_path->cluster_list.push_back(Ip4Address(*it).to_string()); + } +} + static void FillRoutePathCommunityInfo(const Community *comm, ShowRoutePath *show_path) { comm->BuildStringList(&show_path->communities); @@ -450,6 +459,9 @@ void BgpRoute::FillRouteInfo(const BgpTable *table, } else { srp.set_replicated(false); } + if (attr->cluster_list()) { + FillRoutePathClusterListInfo(attr->cluster_list(), &srp); + } if (attr->community()) { FillRoutePathCommunityInfo(attr->community(), &srp); } diff --git a/src/bgp/bgp_server.cc b/src/bgp/bgp_server.cc index a5f1ae871e8..7d438048ae3 100644 --- a/src/bgp/bgp_server.cc +++ b/src/bgp/bgp_server.cc @@ -293,6 +293,7 @@ BgpServer::BgpServer(EventManager *evm) destroyed_(false), aspath_db_(new AsPathDB(this)), olist_db_(new BgpOListDB(this)), + cluster_list_db_(new ClusterListDB(this)), comm_db_(new CommunityDB(this)), edge_discovery_db_(new EdgeDiscoveryDB(this)), edge_forwarding_db_(new EdgeForwardingDB(this)), diff --git a/src/bgp/bgp_server.h b/src/bgp/bgp_server.h index 8fd2aab5553..0b4d03d378f 100644 --- a/src/bgp/bgp_server.h +++ b/src/bgp/bgp_server.h @@ -28,6 +28,7 @@ class BgpConfigManager; class BgpOListDB; class BgpPeer; class BgpSessionManager; +class ClusterListDB; class CommunityDB; class EdgeDiscoveryDB; class EdgeForwardingDB; @@ -130,6 +131,7 @@ class BgpServer { AsPathDB *aspath_db() { return aspath_db_.get(); } BgpAttrDB *attr_db() { return attr_db_.get(); } BgpOListDB *olist_db() { return olist_db_.get(); } + ClusterListDB *cluster_list_db() { return cluster_list_db_.get(); } CommunityDB *comm_db() { return comm_db_.get(); } EdgeDiscoveryDB *edge_discovery_db() { return edge_discovery_db_.get(); } EdgeForwardingDB *edge_forwarding_db() { return edge_forwarding_db_.get(); } @@ -248,6 +250,7 @@ class BgpServer { // databases boost::scoped_ptr aspath_db_; boost::scoped_ptr olist_db_; + boost::scoped_ptr cluster_list_db_; boost::scoped_ptr comm_db_; boost::scoped_ptr edge_discovery_db_; boost::scoped_ptr edge_forwarding_db_; diff --git a/src/bgp/test/bgp_attr_test.cc b/src/bgp/test/bgp_attr_test.cc index c1aa84a7009..0735dfb4591 100644 --- a/src/bgp/test/bgp_attr_test.cc +++ b/src/bgp/test/bgp_attr_test.cc @@ -26,6 +26,7 @@ class BgpAttrTest : public ::testing::Test { : server_(&evm_), attr_db_(server_.attr_db()), aspath_db_(server_.aspath_db()), + cluster_list_db_(server_.cluster_list_db()), comm_db_(server_.comm_db()), edge_discovery_db_(server_.edge_discovery_db()), edge_forwarding_db_(server_.edge_forwarding_db()), @@ -53,6 +54,7 @@ class BgpAttrTest : public ::testing::Test { BgpServer server_; BgpAttrDB *attr_db_; AsPathDB *aspath_db_; + ClusterListDB *cluster_list_db_; CommunityDB *comm_db_; EdgeDiscoveryDB *edge_discovery_db_; EdgeForwardingDB *edge_forwarding_db_; @@ -388,6 +390,52 @@ TEST_F(BgpAttrTest, AsPathFormat2) { EXPECT_EQ("100 200 {300 400} 600 500", path.path().ToString()); } +TEST_F(BgpAttrTest, ClusterList) { + BgpAttrSpec attr_spec; + ClusterListSpec spec; + for (int idx = 1; idx < 5; idx++) + spec.cluster_list.push_back(100 * idx); + attr_spec.push_back(&spec); + BgpAttrPtr attr_ptr = attr_db_->Locate(attr_spec); + EXPECT_EQ(4, attr_ptr->cluster_list_length()); + BgpAttr attr(*attr_ptr); + EXPECT_EQ(attr_ptr->cluster_list(), attr.cluster_list()); +} + +TEST_F(BgpAttrTest, ClusterListCompare1) { + ClusterListSpec spec1; + for (int idx = 1; idx < 5; idx++) + spec1.cluster_list.push_back(100 * idx); + ClusterList clist1(cluster_list_db_, spec1); + EXPECT_EQ(4, clist1.size()); + + ClusterListSpec spec2; + for (int idx = 4; idx >= 1; idx--) + spec2.cluster_list.push_back(100 * idx); + ClusterList clist2(cluster_list_db_, spec2); + EXPECT_EQ(4, clist2.size()); + + EXPECT_EQ(-1, clist1.CompareTo(clist2)); + EXPECT_EQ(1, clist2.CompareTo(clist1)); +} + +TEST_F(BgpAttrTest, ClusterListCompare2) { + ClusterListSpec spec1; + for (int idx = 1; idx < 3; idx++) + spec1.cluster_list.push_back(100 * idx); + ClusterList clist1(cluster_list_db_, spec1); + EXPECT_EQ(2, clist1.size()); + + ClusterListSpec spec2; + for (int idx = 1; idx < 5; idx++) + spec2.cluster_list.push_back(100 * idx); + ClusterList clist2(cluster_list_db_, spec2); + EXPECT_EQ(4, clist2.size()); + + EXPECT_EQ(-1, clist1.CompareTo(clist2)); + EXPECT_EQ(1, clist2.CompareTo(clist1)); +} + TEST_F(BgpAttrTest, CommunityCompare1) { CommunitySpec spec1; for (int idx = 1; idx < 5; idx++) diff --git a/src/bgp/test/bgp_proto_test.cc b/src/bgp/test/bgp_proto_test.cc index 7afd5e341d2..a66a55e8443 100644 --- a/src/bgp/test/bgp_proto_test.cc +++ b/src/bgp/test/bgp_proto_test.cc @@ -61,6 +61,17 @@ class BgpProtoTest : public testing::Test { BgpProto::BgpMessage *msg = BgpProto::Decode(new_data, data_size); if (msg) delete msg; } + + const BgpAttribute *BgpFindAttribute(const BgpProto::Update *update, + BgpAttribute::Code code) { + for (vector::const_iterator it = + update->path_attributes.begin(); + it != update->path_attributes.end(); ++it) { + if ((*it)->code == code) + return *it; + } + return NULL; + } }; class BuildUpdateMessage { @@ -483,6 +494,41 @@ TEST_F(BgpProtoTest, EvpnUpdate) { } } +// +// Decode, parse and encode the cluster-list attribute +// +TEST_F(BgpProtoTest, ClusterList) { + const uint8_t data[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x34, // Length + 0x02, // Update + 0x00, 0x00, // Withdrawn Routes Length + 0x00, 0x1d, // Path Attribute Length + 0x40, 0x01, 0x01, 0x01, // ORIGIN + 0x40, 0x02, 0x04, 0x02, 0x01, 0x00, 0x01, // AS_PATH + 0x40, 0x05, 0x04, 0x00, 0x00, 0x00, 0x64, // LOCAL_PREF + // CLUSTER_LIST + 0x80, 0x0a, 0x08, 0x0a, 0x0b, 0x0c, 0x0d, 0xca, 0xfe, 0xd0, 0xd0, + }; + ParseErrorContext context; + boost::scoped_ptr update( + static_cast( + BgpProto::Decode(data, sizeof(data), &context))); + ASSERT_TRUE(update.get() != NULL); + + const ClusterListSpec *clist_spec = static_cast( + BgpFindAttribute(update.get(), BgpAttribute::ClusterList)); + ASSERT_TRUE(clist_spec != NULL); + EXPECT_EQ(0x0a0b0c0dul, clist_spec->cluster_list[0]); + EXPECT_EQ(0xcafed0d0ul, clist_spec->cluster_list[1]); + + uint8_t buffer[4096]; + int res = BgpProto::Encode(update.get(), buffer, sizeof(buffer)); + EXPECT_EQ(sizeof(data), res); + EXPECT_EQ(0, memcmp(buffer, data, sizeof(data))); +} + TEST_F(BgpProtoTest, OpenError) { uint8_t data[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, diff --git a/src/bgp/test/bgp_route_test.cc b/src/bgp/test/bgp_route_test.cc index 72c33a3ad67..4b1a2c0c567 100644 --- a/src/bgp/test/bgp_route_test.cc +++ b/src/bgp/test/bgp_route_test.cc @@ -273,6 +273,75 @@ TEST_F(BgpRouteTest, PathCompareOriginatorId4) { EXPECT_EQ(1, path2.PathCompare(path1, false)); } +// +// Path with shorter cluster list length is better. +// Both paths have a cluster list. +// Path with longer cluster list is learnt from peer with lower address. +// +TEST_F(BgpRouteTest, PathCompareClusterList1) { + boost::system::error_code ec; + BgpAttrDB *db = server_.attr_db(); + + BgpAttrSpec spec1; + Ip4Address origid1 = Ip4Address::from_string("10.1.1.100", ec); + BgpAttrOriginatorId origid_spec1(origid1.to_ulong()); + spec1.push_back(&origid_spec1); + ClusterListSpec clist_spec1; + clist_spec1.cluster_list.push_back(100); + clist_spec1.cluster_list.push_back(200); + spec1.push_back(&clist_spec1); + BgpAttrPtr attr1 = db->Locate(spec1); + PeerMock peer1(BgpProto::IBGP, Ip4Address::from_string("10.1.1.251", ec)); + BgpPath path1(&peer1, BgpPath::BGP_XMPP, attr1, 0, 0); + + BgpAttrSpec spec2; + Ip4Address origid2 = Ip4Address::from_string("10.1.1.100", ec); + BgpAttrOriginatorId origid_spec2(origid2.to_ulong()); + spec2.push_back(&origid_spec2); + ClusterListSpec clist_spec2; + clist_spec2.cluster_list.push_back(100); + spec2.push_back(&clist_spec2); + BgpAttrPtr attr2 = db->Locate(spec2); + PeerMock peer2(BgpProto::IBGP, Ip4Address::from_string("10.1.1.252", ec)); + BgpPath path2(&peer2, BgpPath::BGP_XMPP, attr2, 0, 0); + + EXPECT_EQ(1, path1.PathCompare(path2, false)); + EXPECT_EQ(-1, path2.PathCompare(path1, false)); +} + +// +// Path with shorter cluster list length is better. +// One path has a cluster list while other one has none. +// Path with longer cluster list is learnt from peer with lower address. +// +TEST_F(BgpRouteTest, PathCompareClusterList2) { + boost::system::error_code ec; + BgpAttrDB *db = server_.attr_db(); + + BgpAttrSpec spec1; + BgpAttrLocalPref lpref_spec1(100); + spec1.push_back(&lpref_spec1); + Ip4Address origid1 = Ip4Address::from_string("10.1.1.100", ec); + BgpAttrOriginatorId origid_spec1(origid1.to_ulong()); + spec1.push_back(&origid_spec1); + ClusterListSpec clist_spec1; + clist_spec1.cluster_list.push_back(100); + spec1.push_back(&clist_spec1); + BgpAttrPtr attr1 = db->Locate(spec1); + PeerMock peer1(BgpProto::IBGP, Ip4Address::from_string("10.1.1.1", ec)); + BgpPath path1(&peer1, BgpPath::BGP_XMPP, attr1, 0, 0); + + BgpAttrSpec spec2; + BgpAttrLocalPref lpref_spec2(100); + spec2.push_back(&lpref_spec2); + BgpAttrPtr attr2 = db->Locate(spec2); + PeerMock peer2(BgpProto::IBGP, Ip4Address::from_string("10.1.1.100", ec)); + BgpPath path2(&peer2, BgpPath::BGP_XMPP, attr2, 0, 0); + + EXPECT_EQ(1, path1.PathCompare(path2, false)); + EXPECT_EQ(-1, path2.PathCompare(path1, false)); +} + // // Path with lower med is better. //