diff --git a/src/vnsw/agent/controller/controller_route_path.cc b/src/vnsw/agent/controller/controller_route_path.cc index 83fb5cb200a..05a71a949f6 100644 --- a/src/vnsw/agent/controller/controller_route_path.cc +++ b/src/vnsw/agent/controller/controller_route_path.cc @@ -42,6 +42,12 @@ bool ControllerEcmpRoute::AddChangePath(Agent *agent, AgentPath *path, if (!comp_nh_policy) { comp_key->SetPolicy(new_comp_nh_policy); } + + if (path->ecmp_load_balance() != ecmp_load_balance_) { + path->UpdateEcmpHashFields(agent, ecmp_load_balance_, + nh_req_); + } + return InetUnicastRouteEntry::ModifyEcmpPath(dest_addr_, plen_, vn_list_, label_, local_ecmp_nh_, vrf_name_, sg_list_, diff --git a/src/vnsw/agent/oper/agent_path.cc b/src/vnsw/agent/oper/agent_path.cc index b8b45e600c5..ca5caf0b7d4 100644 --- a/src/vnsw/agent/oper/agent_path.cc +++ b/src/vnsw/agent/oper/agent_path.cc @@ -1401,3 +1401,27 @@ bool MacVmBindingPathData::AddChangePath(Agent *agent, AgentPath *path, return ret; } + +void AgentPath::UpdateEcmpHashFields(const Agent *agent, + const EcmpLoadBalance &ecmp_load_balance, + DBRequest &nh_req) { + + NextHop *nh = NULL; + nh = static_cast(agent->nexthop_table()-> + FindActiveEntry(nh_req.key.get())); + CompositeNH *cnh = dynamic_cast< CompositeNH *>(nh); + if (cnh) { + ecmp_hash_fields_.CalculateChangeInEcmpFields(ecmp_load_balance, + cnh->CompEcmpHashFields()); + } else { + agent->nexthop_table()->Process(nh_req); + nh = static_cast(agent->nexthop_table()-> + FindActiveEntry(nh_req.key.get())); + CompositeNH *cnh = static_cast< CompositeNH *>(nh); + if (cnh) { + ecmp_hash_fields_.CalculateChangeInEcmpFields(ecmp_load_balance, + cnh->CompEcmpHashFields()); + } + } +} + diff --git a/src/vnsw/agent/oper/agent_path.h b/src/vnsw/agent/oper/agent_path.h index bee978b1dee..60a59573820 100644 --- a/src/vnsw/agent/oper/agent_path.h +++ b/src/vnsw/agent/oper/agent_path.h @@ -12,7 +12,6 @@ #include #include #include - //Forward declaration class AgentXmppChannel; class InterfaceKey; @@ -304,6 +303,10 @@ class AgentPath : public Path { is_health_check_service_ = val; } + void UpdateEcmpHashFields(const Agent *agent, + const EcmpLoadBalance &ecmp_load_balance, + DBRequest &nh_req); + private: PeerConstPtr peer_; // Nexthop for route. Not used for gateway routes @@ -377,6 +380,9 @@ class AgentPath : public Path { // TODO(prabhjot) need to find more genric solution for marking proxy arp // to move all the logic of identifing proxy arp in one place bool is_health_check_service_; + // These Ecmp fields will hold the corresponding composite nh ecmp fields reference + // if the path's ecmp load balance field is not set + EcmpHashFields ecmp_hash_fields_; DISALLOW_COPY_AND_ASSIGN(AgentPath); }; diff --git a/src/vnsw/agent/oper/agent_route.cc b/src/vnsw/agent/oper/agent_route.cc index 895fe4bc764..dc18c4fae49 100644 --- a/src/vnsw/agent/oper/agent_route.cc +++ b/src/vnsw/agent/oper/agent_route.cc @@ -238,7 +238,6 @@ void AgentRouteTable::DeletePathFromPeer(DBTablePartBase *part, if (rt == NULL) { return; } - bool deleted_path_was_active_path = (rt->GetActivePath() == path); RouteInfo rt_info; rt->FillTrace(rt_info, AgentRoute::DELETE_PATH, path); @@ -249,10 +248,14 @@ void AgentRouteTable::DeletePathFromPeer(DBTablePartBase *part, } const Peer *peer = path->peer(); + CompositeNH *cnh = dynamic_cast(path->nexthop()); //Recompute paths since one is going off before deleting. rt->ReComputePathDeletion(path); // Remove path from the route rt->RemovePath(path); + if (cnh) { + cnh->UpdateEcmpHashFieldsUponRouteDelete(agent_, vrf_name()); + } // Local path(non BGP type) is going away and so will route. // For active peers reflector will remove the route but for // non active peers explicitly squash the paths. diff --git a/src/vnsw/agent/oper/ecmp_load_balance.h b/src/vnsw/agent/oper/ecmp_load_balance.h index 87fa43e17d9..de23a57a88a 100644 --- a/src/vnsw/agent/oper/ecmp_load_balance.h +++ b/src/vnsw/agent/oper/ecmp_load_balance.h @@ -4,7 +4,7 @@ #ifndef vnsw_agent_ecmp_load_balance_hpp #define vnsw_agent_ecmp_load_balance_hpp - +#include #include using namespace boost::uuids; @@ -219,4 +219,117 @@ class VmiEcmpLoadBalance : public EcmpLoadBalance { bool use_global_vrouter_; }; +class EcmpField { +public: + EcmpField() { + ref_count_= 0; + } + + uint32_t RefCount() const { + return ref_count_; + } +private: + friend void intrusive_ptr_add_ref(EcmpField* ptr); + friend void intrusive_ptr_release(EcmpField* ptr); + mutable tbb::atomic ref_count_; +}; + +inline void intrusive_ptr_add_ref(EcmpField* ptr) { + ptr->ref_count_.fetch_and_increment(); +} + +inline void intrusive_ptr_release(EcmpField* ptr) { + uint32_t prev = ptr->ref_count_.fetch_and_decrement(); + if (prev == 1) { + delete ptr; + } +} + +class EcmpHashFields { +public: + typedef boost::intrusive_ptr EcmpFieldPtr; + + EcmpHashFields() { + } + + EcmpHashFields(const uint8_t hash_fields_to_use ) { + hash_fields_to_use_ = hash_fields_to_use; + } + void operator = (const uint8_t hash_fields_to_use) { + hash_fields_to_use_ = hash_fields_to_use; + } + + void AllocateEcmpFields() { + sip_ = new EcmpField; + dip_ = new EcmpField; + proto_ = new EcmpField; + sport_ = new EcmpField; + dport_ = new EcmpField; + } + + uint8_t HashFieldsToUse() const { + return hash_fields_to_use_; + } + + void SetHashFieldtoUse(EcmpField *ptr, uint8_t key) { + if (ptr && ptr->RefCount() == 1) { + comp_hash_fields_to_use_ |= 1 << key; + } + } + // If the field is not set create intrusive pointer + // else release the pointer. + void SetChangeInHashField(bool is_field_set, EcmpFieldPtr& fieldPtr, + EcmpFieldPtr &objFieldPtr) { + if (!is_field_set) { + if (!objFieldPtr.get()) { + objFieldPtr = fieldPtr; + } + } else { + objFieldPtr.reset(); + } + } + // This function will be called to ge intersection of ecmp fields + uint8_t CalculateHashFieldsToUse() { + comp_hash_fields_to_use_ = 0; + SetHashFieldtoUse(sip_.get(), EcmpLoadBalance::SOURCE_IP); + SetHashFieldtoUse(dip_.get(), EcmpLoadBalance::DESTINATION_IP); + SetHashFieldtoUse(proto_.get(), EcmpLoadBalance::IP_PROTOCOL); + SetHashFieldtoUse(sport_.get(), EcmpLoadBalance::SOURCE_PORT); + SetHashFieldtoUse(dport_.get(), EcmpLoadBalance::DESTINATION_PORT); + return comp_hash_fields_to_use_; + } + //This function used to calculate the Change in ecmp fields + void CalculateChangeInEcmpFields(const EcmpLoadBalance &ecmp_load_balance, + EcmpHashFields& ecmp_hash_fields) { + SetChangeInHashField(ecmp_load_balance.is_source_ip_set(), + ecmp_hash_fields.sip_, sip_); + SetChangeInHashField(ecmp_load_balance.is_destination_ip_set(), + ecmp_hash_fields.dip_, dip_); + SetChangeInHashField(ecmp_load_balance.is_ip_protocol_set(), + ecmp_hash_fields.proto_, proto_); + SetChangeInHashField(ecmp_load_balance.is_source_port_set(), + ecmp_hash_fields.sport_, sport_); + SetChangeInHashField(ecmp_load_balance.is_destination_port_set(), + ecmp_hash_fields.dport_, dport_); + } + + bool IsFieldsInUseChanged() { + return hash_fields_to_use_ != CalculateHashFieldsToUse(); + } + + void SetHashFieldstoUse() { + hash_fields_to_use_ = comp_hash_fields_to_use_; + } + +private: + // This will have latest computed value + uint8_t comp_hash_fields_to_use_; + uint8_t hash_fields_to_use_; + EcmpFieldPtr sip_; + EcmpFieldPtr dip_; + EcmpFieldPtr proto_; + EcmpFieldPtr sport_; + EcmpFieldPtr dport_; + DISALLOW_COPY_AND_ASSIGN(EcmpHashFields); +}; #endif diff --git a/src/vnsw/agent/oper/nexthop.cc b/src/vnsw/agent/oper/nexthop.cc index 172acef2cfe..b9de6a2cea3 100644 --- a/src/vnsw/agent/oper/nexthop.cc +++ b/src/vnsw/agent/oper/nexthop.cc @@ -1469,6 +1469,10 @@ bool CompositeNH::Change(const DBRequest* req) { changed = true; } + if (comp_ecmp_hash_fields_.IsFieldsInUseChanged()) { + comp_ecmp_hash_fields_.SetHashFieldstoUse(); + changed = true; + } component_nh_list_ = component_nh_list; return changed; } @@ -1778,6 +1782,19 @@ uint32_t CompositeNH::GetRemoteLabel(const Ip4Address &ip) const { return -1; } +void CompositeNH::UpdateEcmpHashFieldsUponRouteDelete(Agent *agent, + const string &vrf_name) { + if (comp_ecmp_hash_fields_.IsFieldsInUseChanged()) { + DBRequest nh_req(DBRequest::DB_ENTRY_ADD_CHANGE); + DBEntryBase::KeyPtr key = GetDBRequestKey(); + NextHopKey *nh_key = static_cast(key.get()); + nh_key->sub_op_ = AgentKey::RESYNC; + nh_req.key = key; + nh_req.data.reset(NULL); + agent->nexthop_table()->Process(nh_req); + } +} + void CompositeNHKey::CreateTunnelNH(Agent *agent) { BOOST_FOREACH(ComponentNHKeyPtr component_nh_key, component_nh_key_list_) { if (component_nh_key.get() && diff --git a/src/vnsw/agent/oper/nexthop.h b/src/vnsw/agent/oper/nexthop.h index 195cf7d238d..14dce4fede0 100644 --- a/src/vnsw/agent/oper/nexthop.h +++ b/src/vnsw/agent/oper/nexthop.h @@ -13,6 +13,7 @@ #include #include +#include using namespace boost::uuids; using namespace std; @@ -1356,6 +1357,7 @@ class CompositeNH : public NextHop { const ComponentNHKeyList &component_nh_key_list, VrfEntry *vrf): NextHop(COMPOSITE, policy), composite_nh_type_(type), component_nh_key_list_(component_nh_key_list), vrf_(vrf, this) { + comp_ecmp_hash_fields_.AllocateEcmpFields(); } virtual ~CompositeNH() { }; @@ -1458,7 +1460,11 @@ class CompositeNH : public NextHop { virtual bool MatchEgressData(const NextHop *nh) const { return false; } - + uint8_t EcmpHashFieldInUse() const { + return comp_ecmp_hash_fields_.HashFieldsToUse(); + } + EcmpHashFields& CompEcmpHashFields() { return comp_ecmp_hash_fields_; } + void UpdateEcmpHashFieldsUponRouteDelete(Agent *agent, const string &vrf_name); private: void CreateComponentNH(Agent *agent, TunnelType::Type type) const; void ChangeComponentNHKeyTunnelType(ComponentNHKeyList &component_nh_list, @@ -1467,6 +1473,7 @@ class CompositeNH : public NextHop { ComponentNHKeyList component_nh_key_list_; ComponentNHList component_nh_list_; VrfEntryRef vrf_; + EcmpHashFields comp_ecmp_hash_fields_; DISALLOW_COPY_AND_ASSIGN(CompositeNH); }; diff --git a/src/vnsw/agent/test/test_cmn_util.h b/src/vnsw/agent/test/test_cmn_util.h index e12dc61e85b..2c28fc6be9f 100644 --- a/src/vnsw/agent/test/test_cmn_util.h +++ b/src/vnsw/agent/test/test_cmn_util.h @@ -213,6 +213,10 @@ bool EcmpTunnelRouteAdd(const Peer *peer, const string &vrf_name, const Ip4Addre uint8_t plen, ComponentNHKeyList &comp_nh_list, bool local_ecmp, const string &vn_name, const SecurityGroupList &sg, const PathPreference &path_preference); +bool EcmpTunnelRouteAdd(const Peer *peer, const string &vrf_name, const Ip4Address &vm_ip, + uint8_t plen, ComponentNHKeyList &comp_nh_list, + bool local_ecmp, const string &vn_name, const SecurityGroupList &sg, + const PathPreference &path_preference, EcmpLoadBalance& ecmp_load_balnce); bool EcmpTunnelRouteAdd(Agent *agent, const Peer *peer, const string &vrf_name, const string &prefix, uint8_t plen, const string &remote_server_1, uint32_t label1, diff --git a/src/vnsw/agent/test/test_ecmp_nh.cc b/src/vnsw/agent/test/test_ecmp_nh.cc index 3e4722e64a4..3ee2ed17fc5 100644 --- a/src/vnsw/agent/test/test_ecmp_nh.cc +++ b/src/vnsw/agent/test/test_ecmp_nh.cc @@ -26,6 +26,7 @@ #include "testing/gunit.h" #include "test_cmn_util.h" #include "vr_types.h" +#include "vr_flow.h" using namespace std; using namespace boost::assign; @@ -182,8 +183,6 @@ TEST_F(EcmpNhTest, EcmpNH_1) { //Expect MPLS label to be not present EXPECT_FALSE(FindMplsLabel(MplsLabel::VPORT_NH, mpls_label)); } - -//Create multiple VM with same virtual IP and verify //ecmp NH gets created and also verify that it gets deleted //upon VM deletion. TEST_F(EcmpNhTest, EcmpNH_2) { @@ -1570,7 +1569,154 @@ TEST_F(EcmpNhTest, EcmpNH_17) { WAIT_FOR(100, 1000, (VrfFind("vrf1") == false)); EXPECT_FALSE(RouteFind("vrf1", ip, 32)); } +//Add multiple remote routes with same set of composite NH +//make sure they share the composite NH +//Add ecmp load balance fields to route entry. +//and check that more common feilds picked by composite NEXTHop +TEST_F(EcmpNhTest, EcmpNH_18) { + AddVrf("vrf1"); + client->WaitForIdle(); + + Ip4Address remote_server_ip1 = Ip4Address::from_string("10.10.10.100"); + Ip4Address remote_server_ip2 = Ip4Address::from_string("10.10.10.101"); + + ComponentNHKeyPtr nh_data1(new ComponentNHKey(30, agent_->fabric_vrf_name(), + agent_->router_id(), + remote_server_ip1, + false, + TunnelType::DefaultType())); + ComponentNHKeyPtr nh_data2(new ComponentNHKey(20, agent_->fabric_vrf_name(), + agent_->router_id(), + remote_server_ip2, + false, + TunnelType::DefaultType())); + + ComponentNHKeyList comp_nh_list; + comp_nh_list.push_back(nh_data1); + comp_nh_list.push_back(nh_data2); + + Ip4Address ip1 = Ip4Address::from_string("100.1.1.1"); + SecurityGroupList sg_id_list; + EcmpLoadBalance ecmp_load_balance; + ecmp_load_balance.ResetAll(); + ecmp_load_balance.set_source_ip(); + ecmp_load_balance.set_destination_ip(); + ecmp_load_balance.set_source_port(); + EcmpTunnelRouteAdd(bgp_peer, "vrf1", ip1, 32, + comp_nh_list, false, "vn1", sg_id_list, PathPreference(), ecmp_load_balance); + client->WaitForIdle(); + + Ip4Address ip2 = Ip4Address::from_string("100.1.1.2"); + ecmp_load_balance.ResetAll(); + ecmp_load_balance.set_source_ip(); + ecmp_load_balance.set_destination_ip(); + ecmp_load_balance.set_ip_protocol(); + EcmpTunnelRouteAdd(bgp_peer, "vrf1", ip2, 32, + comp_nh_list, false, "vn1", sg_id_list, PathPreference(), ecmp_load_balance); + client->WaitForIdle(); + + InetUnicastRouteEntry *rt1 = RouteGet("vrf1", ip1, 32); + InetUnicastRouteEntry *rt2 = RouteGet("vrf1", ip2, 32); + EXPECT_TRUE(rt1->GetActiveNextHop()->GetType() == NextHop::COMPOSITE); + EXPECT_TRUE(rt1->GetActiveNextHop()->PolicyEnabled() == false); + EXPECT_TRUE(rt1->GetActiveNextHop() == rt2->GetActiveNextHop()); + const CompositeNH *cnh = static_cast(rt1->GetActiveNextHop()); + + EXPECT_TRUE(cnh->EcmpHashFieldInUse() == + (1<WaitForIdle(); + DeleteRoute("vrf1", "100.1.1.2", 32, bgp_peer); + client->WaitForIdle(); + + CompositeNHKey composite_nh_key(Composite::ECMP, true, comp_nh_list, "vrf1"); + EXPECT_FALSE(FindNH(&composite_nh_key)); + WAIT_FOR(100, 1000, (FindNH(&composite_nh_key) ==false)); + DelVrf("vrf1"); +} +//Add multiple remote routes with same set of composite NH +//make sure they share the composite NH +//Add ecmp load balance feilds to route entry. +//and check that more common feilds picked by composite NEXTHop +//and delete the route entry and check that recompute happens +TEST_F(EcmpNhTest, EcmpNH_19) { + AddVrf("vrf1"); + client->WaitForIdle(); + + Ip4Address remote_server_ip1 = Ip4Address::from_string("10.10.10.100"); + Ip4Address remote_server_ip2 = Ip4Address::from_string("10.10.10.101"); + + ComponentNHKeyPtr nh_data1(new ComponentNHKey(30, agent_->fabric_vrf_name(), + agent_->router_id(), + remote_server_ip1, + false, + TunnelType::DefaultType())); + ComponentNHKeyPtr nh_data2(new ComponentNHKey(20, agent_->fabric_vrf_name(), + agent_->router_id(), + remote_server_ip2, + false, + TunnelType::DefaultType())); + + ComponentNHKeyList comp_nh_list; + comp_nh_list.push_back(nh_data1); + comp_nh_list.push_back(nh_data2); + + Ip4Address ip1 = Ip4Address::from_string("100.1.1.1"); + SecurityGroupList sg_id_list; + EcmpLoadBalance ecmp_load_balance; + ecmp_load_balance.ResetAll(); + ecmp_load_balance.set_source_ip(); + ecmp_load_balance.set_destination_ip(); + ecmp_load_balance.set_ip_protocol(); + EcmpTunnelRouteAdd(bgp_peer, "vrf1", ip1, 32, + comp_nh_list, false, "vn1", sg_id_list, PathPreference(), ecmp_load_balance); + client->WaitForIdle(); + + Ip4Address ip2 = Ip4Address::from_string("100.1.1.2"); + ecmp_load_balance.ResetAll(); + ecmp_load_balance.set_source_ip(); + ecmp_load_balance.set_destination_ip(); + EcmpTunnelRouteAdd(bgp_peer, "vrf1", ip2, 32, + comp_nh_list, false, "vn1", sg_id_list, PathPreference(), ecmp_load_balance); + client->WaitForIdle(); + Ip4Address ip3 = Ip4Address::from_string("100.1.1.3"); + ecmp_load_balance.ResetAll(); + ecmp_load_balance.set_source_ip(); + EcmpTunnelRouteAdd(bgp_peer, "vrf1", ip3, 32, + comp_nh_list, false, "vn1", sg_id_list, PathPreference(), ecmp_load_balance); + client->WaitForIdle(); + InetUnicastRouteEntry *rt1 = RouteGet("vrf1", ip1, 32); + InetUnicastRouteEntry *rt2 = RouteGet("vrf1", ip2, 32); + InetUnicastRouteEntry *rt3 = RouteGet("vrf1", ip3, 32); + EXPECT_TRUE(rt1->GetActiveNextHop()->GetType() == NextHop::COMPOSITE); + EXPECT_TRUE(rt1->GetActiveNextHop()->PolicyEnabled() == false); + EXPECT_TRUE(rt1->GetActiveNextHop() == rt2->GetActiveNextHop()); + EXPECT_TRUE(rt1->GetActiveNextHop() == rt3->GetActiveNextHop()); + const CompositeNH *cnh = static_cast(rt1->GetActiveNextHop()); + EXPECT_TRUE(cnh->EcmpHashFieldInUse() == (1<WaitForIdle(); + DeleteRoute("vrf1", "100.1.1.3", 32, bgp_peer); + client->WaitForIdle(); + const CompositeNH *cnh1 = static_cast(rt1->GetActiveNextHop()); + EXPECT_TRUE(cnh1->EcmpHashFieldInUse() == + (1<WaitForIdle(); + const CompositeNH *cnh2 = static_cast(rt1->GetActiveNextHop()); + EXPECT_TRUE(cnh2->EcmpHashFieldInUse() == + (1<WaitForIdle(); + + CompositeNHKey composite_nh_key(Composite::ECMP, true, comp_nh_list, "vrf1"); + EXPECT_FALSE(FindNH(&composite_nh_key)); + DelVrf("vrf1"); +} int main(int argc, char **argv) { GETUSERARGS(); client = TestInit(init_file, ksync_init); diff --git a/src/vnsw/agent/test/test_util.cc b/src/vnsw/agent/test/test_util.cc index 69d4b403216..9ca6ad5f2cb 100644 --- a/src/vnsw/agent/test/test_util.cc +++ b/src/vnsw/agent/test/test_util.cc @@ -1557,6 +1557,29 @@ bool EcmpTunnelRouteAdd(const Peer *peer, const string &vrf_name, const Ip4Addre InetUnicastAgentRouteTable::AddRemoteVmRouteReq(peer, vrf_name, vm_ip, plen, data); } +bool EcmpTunnelRouteAdd(const Peer *peer, const string &vrf_name, const Ip4Address &vm_ip, + uint8_t plen, ComponentNHKeyList &comp_nh_list, + bool local_ecmp, const string &vn_name, const SecurityGroupList &sg, + const PathPreference &path_preference, + EcmpLoadBalance &ecmp_load_balance) { + COMPOSITETYPE type = Composite::ECMP; + if (local_ecmp) { + type = Composite::LOCAL_ECMP; + } + DBRequest nh_req(DBRequest::DB_ENTRY_ADD_CHANGE); + nh_req.key.reset(new CompositeNHKey(type, false, + comp_nh_list, vrf_name)); + nh_req.data.reset(new CompositeNHData()); + + VnListType vn_list; + vn_list.insert(vn_name); + ControllerEcmpRoute *data = + new ControllerEcmpRoute(peer, vm_ip, plen, vn_list, -1, false, vrf_name, + sg, path_preference, TunnelType::MplsType(), + ecmp_load_balance, nh_req); + InetUnicastAgentRouteTable::AddRemoteVmRouteReq(peer, vrf_name, vm_ip, plen, data); +} + bool Inet6TunnelRouteAdd(const Peer *peer, const string &vm_vrf, const Ip6Address &vm_addr, uint8_t plen, const Ip4Address &server_ip, TunnelType::TypeBmap bmap, uint32_t label, const string &dest_vn_name, diff --git a/src/vnsw/agent/vrouter/ksync/nexthop_ksync.cc b/src/vnsw/agent/vrouter/ksync/nexthop_ksync.cc index a76085202b6..51fc1086ab4 100644 --- a/src/vnsw/agent/vrouter/ksync/nexthop_ksync.cc +++ b/src/vnsw/agent/vrouter/ksync/nexthop_ksync.cc @@ -27,6 +27,7 @@ #include "vrouter/ksync/nexthop_ksync.h" #include "vrouter/ksync/ksync_init.h" #include "vr_types.h" +#include "oper/ecmp_load_balance.h" NHKSyncEntry::NHKSyncEntry(NHKSyncObject *obj, const NHKSyncEntry *entry, uint32_t index) : @@ -44,8 +45,9 @@ NHKSyncEntry::NHKSyncEntry(NHKSyncObject *obj, const NHKSyncEntry *entry, nh_id_(entry->nh_id()), component_nh_key_list_(entry->component_nh_key_list_), vxlan_nh_(entry->vxlan_nh_), - flood_unknown_unicast_(entry->flood_unknown_unicast_) { -} + flood_unknown_unicast_(entry->flood_unknown_unicast_), + ecmp_hash_fieds_(entry->ecmp_hash_fieds_.HashFieldsToUse()) { + } NHKSyncEntry::NHKSyncEntry(NHKSyncObject *obj, const NextHop *nh) : KSyncNetlinkDBEntry(kInvalidIndex), ksync_obj_(obj), type_(nh->GetType()), @@ -156,6 +158,8 @@ NHKSyncEntry::NHKSyncEntry(NHKSyncObject *obj, const NextHop *nh) : component_nh_list_.clear(); vrf_id_ = comp_nh->vrf()->vrf_id(); comp_type_ = comp_nh->composite_nh_type(); + ecmp_hash_fieds_ = + const_cast(comp_nh)->CompEcmpHashFields().HashFieldsToUse(); component_nh_key_list_ = comp_nh->component_nh_key_list(); ComponentNHList::const_iterator component_nh_it = comp_nh->begin(); @@ -615,6 +619,11 @@ bool NHKSyncEntry::Sync(DBEntry *e) { component_nh_list_.push_back(ksync_component_nh); component_nh_it++; } + + if (comp_nh->EcmpHashFieldInUse() != ecmp_hash_fieds_.HashFieldsToUse()) { + ecmp_hash_fieds_ = comp_nh->CompEcmpHashFields().HashFieldsToUse(); + ret = true; + } break; } @@ -654,7 +663,6 @@ bool NHKSyncEntry::Sync(DBEntry *e) { return ret; }; - int NHKSyncEntry::Encode(sandesh_op::type op, char *buf, int buf_len) { vr_nexthop_req encoder; int encode_len; @@ -820,6 +828,7 @@ int NHKSyncEntry::Encode(sandesh_op::type op, char *buf, int buf_len) { encoder.set_nhr_tun_sip(htonl(sip_.to_v4().to_ulong())); encoder.set_nhr_tun_dip(htonl(dip_.to_v4().to_ulong())); encoder.set_nhr_encap_family(ETHERTYPE_ARP); + encoder.set_nhr_ecmp_config_hash(SetEcmpFieldsToUse()); /* Proto encode in Network byte order */ switch (comp_type_) { case Composite::L2INTERFACE: @@ -1159,6 +1168,29 @@ void NHKSyncEntry::SetEncap(InterfaceKSyncEntry *if_ksync, encap.push_back(0x00); } +uint8_t NHKSyncEntry::SetEcmpFieldsToUse() { + uint8_t ecmp_hash_fields_in_use = VR_FLOW_KEY_NONE; + uint8_t fields_in_byte = ecmp_hash_fieds_.HashFieldsToUse(); + if (fields_in_byte & 1 << EcmpLoadBalance::SOURCE_IP) { + ecmp_hash_fields_in_use |= VR_FLOW_KEY_SRC_IP; + } + if (fields_in_byte & 1 << EcmpLoadBalance::DESTINATION_IP) { + ecmp_hash_fields_in_use |= VR_FLOW_KEY_DST_IP; + } + if (fields_in_byte & 1 << EcmpLoadBalance::IP_PROTOCOL) { + ecmp_hash_fields_in_use |= VR_FLOW_KEY_PROTO; + } + if (fields_in_byte & 1 << EcmpLoadBalance::SOURCE_PORT) { + ecmp_hash_fields_in_use |= VR_FLOW_KEY_SRC_PORT; + } + if (fields_in_byte & 1 << EcmpLoadBalance::DESTINATION_PORT) { + ecmp_hash_fields_in_use |= VR_FLOW_KEY_DST_PORT; + } + + return ecmp_hash_fields_in_use; + +} + NHKSyncObject::NHKSyncObject(KSync *ksync) : KSyncDBObject("KSync Nexthop"), ksync_(ksync) { } diff --git a/src/vnsw/agent/vrouter/ksync/nexthop_ksync.h b/src/vnsw/agent/vrouter/ksync/nexthop_ksync.h index 3eedae1195e..00acb52c24a 100644 --- a/src/vnsw/agent/vrouter/ksync/nexthop_ksync.h +++ b/src/vnsw/agent/vrouter/ksync/nexthop_ksync.h @@ -55,6 +55,7 @@ class NHKSyncEntry : public KSyncNetlinkDBEntry { else return 2 * kDefaultNhMsgSize; } + uint8_t SetEcmpFieldsToUse(); private: class KSyncComponentNH { public: @@ -105,6 +106,7 @@ class NHKSyncEntry : public KSyncNetlinkDBEntry { ComponentNHKeyList component_nh_key_list_; bool vxlan_nh_; bool flood_unknown_unicast_; + EcmpHashFields ecmp_hash_fieds_; DISALLOW_COPY_AND_ASSIGN(NHKSyncEntry); };