diff --git a/src/vnsw/agent/oper/mirror_table.cc b/src/vnsw/agent/oper/mirror_table.cc index a3059fc12e4..103376e8454 100644 --- a/src/vnsw/agent/oper/mirror_table.cc +++ b/src/vnsw/agent/oper/mirror_table.cc @@ -54,7 +54,6 @@ DBEntry *MirrorTable::Add(const DBRequest *req) { MirrorEntry *mirror_entry = new MirrorEntry(key->analyzer_name_); //Get Mirror NH OnChange(mirror_entry, req); - LOG(DEBUG, "Mirror Add"); return mirror_entry; } @@ -63,24 +62,139 @@ bool MirrorTable::OnChange(DBEntry *entry, const DBRequest *req) { MirrorEntry *mirror_entry = static_cast(entry); MirrorEntryData *data = static_cast(req->data.get()); - MirrorNHKey nh_key(data->vrf_name_, data->sip_, data->sport_, - data->dip_, data->dport_); + mirror_entry->vrf_name_ = data->vrf_name_; + mirror_entry->sip_ = data->sip_; + mirror_entry->sport_ = data->sport_; + mirror_entry->dip_ = data->dip_; + mirror_entry->dport_ = data->dport_; + + DBRequest nh_req; + nh_req.oper = DBRequest::DB_ENTRY_ADD_CHANGE; + MirrorNHKey *nh_key = new MirrorNHKey(data->vrf_name_, data->sip_, + data->sport_, data->dip_, data->dport_); + nh_req.key.reset(nh_key); + nh_req.data.reset(NULL); + agent()->nexthop_table()->Process(nh_req); + + VrfKey key(data->vrf_name_); + VrfEntry *vrf = + static_cast(agent()->vrf_table()->FindActiveEntry(&key)); + NextHop *nh = static_cast - (Agent::GetInstance()->nexthop_table()->FindActiveEntry(&nh_key)); - assert(nh); + (agent()->nexthop_table()->FindActiveEntry(nh_key)); + if (nh == NULL || vrf == NULL) { + //Make the mirror NH point to discard + //and change the nexthop once the VRF is + //available + AddUnresolved(mirror_entry); + DiscardNH key; + nh = static_cast + (agent()->nexthop_table()->FindActiveEntry(&key)); + } else { + AddResolvedVrfMirrorEntry(mirror_entry); + } if (mirror_entry->nh_ != nh) { mirror_entry->nh_ = nh; - mirror_entry->sip_ = data->sip_; - mirror_entry->sport_ = data->sport_; - mirror_entry->dip_ = data->dip_; - mirror_entry->dport_ = data->dport_; - mirror_entry->vrf_ = Agent::GetInstance()->vrf_table()->FindVrfFromName(data->vrf_name_); + mirror_entry->vrf_ = + agent()->vrf_table()->FindVrfFromName(data->vrf_name_); ret = true; } return ret; } +bool MirrorTable::Delete(DBEntry *entry, const DBRequest *request) { + MirrorEntry *mirror_entry = static_cast(entry); + RemoveUnresolved(mirror_entry); + DeleteResolvedVrfMirrorEntry(mirror_entry); + return true; +} + +void MirrorTable::Add(VrfMirrorEntryList &vrf_entry_map, MirrorEntry *entry) { + VrfMirrorEntryList::iterator it = vrf_entry_map.find(entry->vrf_name_); + + if (it != vrf_entry_map.end()) { + MirrorEntryList::const_iterator list_it = it->second.begin(); + for (; list_it != it->second.end(); list_it++) { + if (*list_it == entry) { + //Entry already present + return; + } + } + it->second.push_back(entry); + return; + } + + MirrorEntryList list; + list.push_back(entry); + vrf_entry_map.insert(VrfMirrorEntry(entry->vrf_name_, list)); +} + +void MirrorTable::Delete(VrfMirrorEntryList &list, MirrorEntry *entry) { + VrfMirrorEntryList::iterator it = list.find(entry->vrf_name_); + if (it == list.end()) { + return; + } + + MirrorEntryList::iterator list_it = it->second.begin(); + for(;list_it != it->second.end(); list_it++) { + if (*list_it == entry) { + it->second.erase(list_it); + break; + } + } +} + +void MirrorTable::ResyncMirrorEntry(VrfMirrorEntryList &list, + const VrfEntry *vrf) { + VrfMirrorEntryList::iterator it = list.find(vrf->GetName()); + if (it == list.end()) { + return; + } + + MirrorEntryList::iterator list_it = it->second.begin(); + for(;list_it != it->second.end(); list_it++) { + DBRequest req; + req.oper = DBRequest::DB_ENTRY_ADD_CHANGE; + + MirrorEntryKey *key = new MirrorEntryKey((*list_it)->GetAnalyzerName()); + key->sub_op_ = AgentKey::RESYNC; + MirrorEntryData *data = new MirrorEntryData((*list_it)->vrf_name(), + *((*list_it)->GetSip()), + (*list_it)->GetSPort(), + *((*list_it)->GetDip()), + (*list_it)->GetDPort()); + req.key.reset(key); + req.data.reset(data); + Enqueue(&req); + } + list.erase(it); +} + +void MirrorTable::AddUnresolved(MirrorEntry *entry) { + Add(unresolved_entry_list_, entry); +} + +void MirrorTable::RemoveUnresolved(MirrorEntry *entry) { + Delete(unresolved_entry_list_, entry); +} + +void MirrorTable::AddResolvedVrfMirrorEntry(MirrorEntry *entry) { + Add(resolved_entry_list_, entry); +} + +void MirrorTable::DeleteResolvedVrfMirrorEntry(MirrorEntry *entry) { + Delete(resolved_entry_list_, entry); +} + +void MirrorTable::ResyncResolvedMirrorEntry(const VrfEntry *vrf) { + ResyncMirrorEntry(resolved_entry_list_, vrf); +} + +void MirrorTable::ResyncUnresolvedMirrorEntry(const VrfEntry *vrf) { + ResyncMirrorEntry(unresolved_entry_list_, vrf); +} + void MirrorTable::AddMirrorEntry(const std::string &analyzer_name, const std::string &vrf_name, const Ip4Address &sip, uint16_t sport, @@ -93,7 +207,7 @@ void MirrorTable::AddMirrorEntry(const std::string &analyzer_name, MirrorNHKey *nh_key = new MirrorNHKey(vrf_name, sip, sport, dip, dport); req.key.reset(nh_key); req.data.reset(NULL); - Agent::GetInstance()->nexthop_table()->Enqueue(&req); + mirror_table_->agent()->nexthop_table()->Enqueue(&req); req.oper = DBRequest::DB_ENTRY_ADD_CHANGE; MirrorEntryKey *key = new MirrorEntryKey(analyzer_name); @@ -124,6 +238,27 @@ DBTableBase *MirrorTable::CreateTable(DB *db, const std::string &name) { return mirror_table_; }; +void MirrorTable::Initialize() { + VrfListenerInit(); +} + +void MirrorTable::VrfListenerInit() { + vrf_listener_id_ = agent()->vrf_table()-> + Register(boost::bind(&MirrorTable::VrfNotify, + this, _1, _2)); +} + +void MirrorTable::VrfNotify(DBTablePartBase *base, DBEntryBase *entry) { + const VrfEntry *vrf = static_cast(entry); + if (vrf->IsDeleted()) { + //VRF is getting deleted remove all the mirror nexthop + ResyncResolvedMirrorEntry(vrf); + return; + } + + ResyncUnresolvedMirrorEntry(vrf); +} + void MirrorTable::ReadHandler(const boost::system::error_code &ec, size_t bytes_transferred) { @@ -142,7 +277,7 @@ void MirrorTable::ReadHandler(const boost::system::error_code &ec, void MirrorTable::MirrorSockInit(void) { EventManager *event_mgr; - event_mgr = Agent::GetInstance()->event_manager(); + event_mgr = agent()->event_manager(); boost::asio::io_service &io = *event_mgr->io_service(); ip::udp::endpoint ep(ip::udp::v4(), 0); @@ -157,7 +292,7 @@ void MirrorTable::MirrorSockInit(void) { ip::udp::endpoint sock_ep = udp_sock_->local_endpoint(ec); assert(ec.value() == 0); - Agent::GetInstance()->set_mirror_port(sock_ep.port()); + agent()->set_mirror_port(sock_ep.port()); udp_sock_->async_receive(boost::asio::buffer(rx_buff_, sizeof(rx_buff_)), boost::bind(&MirrorTable::ReadHandler, this, @@ -166,7 +301,11 @@ void MirrorTable::MirrorSockInit(void) { } VrfEntry *MirrorTable::FindVrfEntry(const string &vrf_name) const { - return Agent::GetInstance()->vrf_table()->FindVrfFromName(vrf_name); + return agent()->vrf_table()->FindVrfFromName(vrf_name); +} + +void MirrorTable::Shutdown() { + agent()->vrf_table()->Unregister(vrf_listener_id_); } uint32_t MirrorEntry::vrf_id() const { diff --git a/src/vnsw/agent/oper/mirror_table.h b/src/vnsw/agent/oper/mirror_table.h index c6b92167e77..8c4f57aa0cf 100644 --- a/src/vnsw/agent/oper/mirror_table.h +++ b/src/vnsw/agent/oper/mirror_table.h @@ -31,7 +31,8 @@ struct MirrorEntryData : public AgentData { class MirrorEntry : AgentRefCount, public AgentDBEntry { public: MirrorEntry(std::string analyzer_name) : - analyzer_name_(analyzer_name), vrf_(NULL, this), nh_(NULL) { }; + analyzer_name_(analyzer_name), vrf_(NULL, this), nh_(NULL), + vrf_name_("") { }; virtual ~MirrorEntry() { }; virtual bool IsLess(const DBEntry &rhs) const; @@ -55,6 +56,7 @@ class MirrorEntry : AgentRefCount, public AgentDBEntry { const Ip4Address *GetDip() const {return &dip_;} uint16_t GetDPort() const {return dport_;} const NextHop *GetNH() const {return nh_.get();} + const std::string vrf_name() const {return vrf_name_;} private: std::string analyzer_name_; @@ -64,12 +66,17 @@ class MirrorEntry : AgentRefCount, public AgentDBEntry { Ip4Address dip_; uint16_t dport_; NextHopRef nh_; + std::string vrf_name_; friend class MirrorTable; }; class MirrorTable : public AgentDBTable { public: const static unsigned bufLen = 512; + typedef std::vector MirrorEntryList; + typedef std::map VrfMirrorEntryList; + typedef std::pair VrfMirrorEntry; + MirrorTable(DB *db, const std::string &name) : AgentDBTable(db, name) { } virtual ~MirrorTable(); @@ -79,7 +86,11 @@ class MirrorTable : public AgentDBTable { virtual DBEntry *Add(const DBRequest *req); virtual bool OnChange(DBEntry *entry, const DBRequest *req); - virtual bool Delete(DBEntry *entry, const DBRequest *request) { return true; } + virtual bool Delete(DBEntry *entry, const DBRequest *request); + virtual bool Resync(DBEntry *entry, const DBRequest *req) { + bool ret = OnChange(entry, req); + return ret; + } virtual AgentSandeshPtr GetAgentSandesh(const AgentSandeshArguments *args, const std::string &context); VrfEntry *FindVrfEntry(const std::string &vrf_name) const; @@ -93,10 +104,26 @@ class MirrorTable : public AgentDBTable { static MirrorTable *GetInstance() {return mirror_table_;} void MirrorSockInit(void); void ReadHandler(const boost::system::error_code& error, size_t bytes); + void AddUnresolved(MirrorEntry *entry); + void RemoveUnresolved(MirrorEntry *entry); + void AddResolvedVrfMirrorEntry(MirrorEntry *entry); + void DeleteResolvedVrfMirrorEntry(MirrorEntry *entry); + void ResyncMirrorEntry(VrfMirrorEntryList &list, const VrfEntry *vrf); + void ResyncResolvedMirrorEntry(const VrfEntry *vrf); + void ResyncUnresolvedMirrorEntry(const VrfEntry *vrf); + void Add(VrfMirrorEntryList &vrf_entry_map, MirrorEntry *entry); + void Delete(VrfMirrorEntryList &vrf_entry_map, MirrorEntry *entry); + void VrfListenerInit(); + void VrfNotify(DBTablePartBase *root, DBEntryBase *entry); + void Shutdown(); + void Initialize(); private: std::auto_ptr udp_sock_; static MirrorTable *mirror_table_; char rx_buff_[bufLen]; + VrfMirrorEntryList unresolved_entry_list_; + VrfMirrorEntryList resolved_entry_list_; + DBTableBase::ListenerId vrf_listener_id_; }; #endif diff --git a/src/vnsw/agent/oper/nexthop.cc b/src/vnsw/agent/oper/nexthop.cc index d177aa882f7..b37ecf1cb88 100644 --- a/src/vnsw/agent/oper/nexthop.cc +++ b/src/vnsw/agent/oper/nexthop.cc @@ -865,6 +865,11 @@ void TunnelNH::SendObjectLog(const NextHopTable *table, // Mirror NH routines ///////////////////////////////////////////////////////////////////////////// bool MirrorNH::CanAdd() const { + if (vrf_ == NULL) { + LOG(ERROR, "Invalid VRF in mirror NH"); + return false; + } + return true; } diff --git a/src/vnsw/agent/oper/operdb_init.cc b/src/vnsw/agent/oper/operdb_init.cc index 3bbcdca9434..10b162884f0 100644 --- a/src/vnsw/agent/oper/operdb_init.cc +++ b/src/vnsw/agent/oper/operdb_init.cc @@ -157,6 +157,7 @@ void OperDB::CreateDBTables(DB *db) { assert(mirror_table); agent_->set_mirror_table(mirror_table); mirror_table->set_agent(agent_); + mirror_table->Initialize(); VrfAssignTable *vassign_table = static_cast (db->CreateTable("db.vrf_assign.0")); @@ -302,6 +303,9 @@ void OperDB::Shutdown() { route_preference_module_->Shutdown(); domain_config_->Terminate(); vrouter_.reset(); + if (agent()->mirror_table()) { + agent()->mirror_table()->Shutdown(); + } } void OperDB::DeleteRoutes() { diff --git a/src/vnsw/agent/test/test_mirror.cc b/src/vnsw/agent/test/test_mirror.cc index 8bac71775f4..07800fdab1f 100644 --- a/src/vnsw/agent/test/test_mirror.cc +++ b/src/vnsw/agent/test/test_mirror.cc @@ -289,6 +289,150 @@ TEST_F(MirrorTableTest, MirrorEntryAddDel_4) { client->WaitForIdle(); } +TEST_F(MirrorTableTest, MirrorEntryAddDel_5) { + Ip4Address vhost_ip(agent_->router_id()); + Ip4Address remote_server = Ip4Address::from_string("1.1.1.1"); + //Add mirror entry pointing to same vhost IP + std::string ana = analyzer + "r"; + std::string analyzer1 = analyzer + "1"; + std::string analyzer2 = "analyzer2"; + MirrorTable::AddMirrorEntry(ana, "vrf3", + vhost_ip, 0x1, remote_server, 0x2); + MirrorTable::AddMirrorEntry(analyzer1, "vrf3", + vhost_ip, 0x1, remote_server, 0x2); + MirrorTable::AddMirrorEntry(analyzer2 , "vrf2", + vhost_ip, 0x1, remote_server, 0x2); + client->WaitForIdle(); + //Mirror NH would point to a gateway route + MirrorEntryKey key(ana); + const MirrorEntry *mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry != NULL); + const MirrorNH *mirr_nh = static_cast(mirr_entry->GetNH()); + EXPECT_TRUE(mirr_nh->GetType() == NextHop::DISCARD); + + MirrorEntryKey key1(analyzer1); + const MirrorEntry *mirr_entry1 = static_cast + (agent_->mirror_table()->FindActiveEntry(&key1)); + EXPECT_TRUE(mirr_entry1 != NULL); + const MirrorNH *mirr_nh1 = static_cast(mirr_entry1->GetNH()); + EXPECT_TRUE(mirr_nh1->GetType() == NextHop::DISCARD); + + + AddVrf("vrf3"); + client->WaitForIdle(); + + mirr_nh = static_cast(mirr_entry->GetNH()); + EXPECT_TRUE(mirr_nh->GetType() == NextHop::MIRROR); + + mirr_nh1 = static_cast(mirr_entry1->GetNH()); + EXPECT_TRUE(mirr_nh1->GetType() == NextHop::MIRROR); + + MirrorEntryKey key2(analyzer2); + const MirrorEntry *mirr_entry2 = static_cast + (agent_->mirror_table()->FindActiveEntry(&key2)); + EXPECT_TRUE(mirr_entry2 != NULL); + const MirrorNH *mirr_nh2 = static_cast(mirr_entry2->GetNH()); + EXPECT_TRUE(mirr_nh2->GetType() == NextHop::DISCARD); + + MirrorTable::DelMirrorEntry(ana); + MirrorTable::DelMirrorEntry(analyzer1); + MirrorTable::DelMirrorEntry(analyzer2); + client->WaitForIdle(); + mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry == NULL); + client->WaitForIdle(); + usleep(1000); + DelVrf("vrf3"); + client->WaitForIdle(); +} + +TEST_F(MirrorTableTest, MirrorInvalidVrf_1) { + Ip4Address vhost_ip(agent_->router_id()); + string analyser("analyzer-no-vrf"); + string vrf("invalid-vrf"); + + //Add mirror entry pointing to same vhost IP + MirrorTable::AddMirrorEntry(analyzer, vrf, vhost_ip, 0x1, vhost_ip, 0x2); + client->WaitForIdle(); + + //Mirror NH would point to a route, whose nexthop would be RCV NH + MirrorEntryKey key(analyzer); + const MirrorEntry *mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry != NULL); + + //Make sure mirror nh internally points to receive router + const MirrorNH *mirr_nh = static_cast(mirr_entry->GetNH()); + EXPECT_TRUE(mirr_nh->GetType() == NextHop::DISCARD); + + MirrorTable::DelMirrorEntry(analyzer); + client->WaitForIdle(); + mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry == NULL); +} + +TEST_F(MirrorTableTest, MirrorEntryAddDel_6) { + AddVrf("vrf3"); + client->WaitForIdle(); + + Ip4Address vhost_ip(agent_->router_id()); + Ip4Address remote_server = Ip4Address::from_string("1.1.1.1"); + //Add mirror entry pointing to same vhost IP + std::string ana = analyzer + "r"; + std::string analyzer1 = analyzer + "1"; + std::string analyzer2 = "analyzer2"; + MirrorTable::AddMirrorEntry(ana, "vrf3", + vhost_ip, 0x1, remote_server, 0x2); + MirrorTable::AddMirrorEntry(analyzer1, "vrf3", + vhost_ip, 0x1, remote_server, 0x2); + MirrorTable::AddMirrorEntry(analyzer2 , "vrf2", + vhost_ip, 0x1, remote_server, 0x2); + client->WaitForIdle(); + //Mirror NH would point to a gateway route + MirrorEntryKey key(ana); + const MirrorEntry *mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry != NULL); + const MirrorNH *mirr_nh = static_cast(mirr_entry->GetNH()); + EXPECT_TRUE(mirr_nh->GetType() == NextHop::MIRROR); + + MirrorEntryKey key1(analyzer1); + const MirrorEntry *mirr_entry1 = static_cast + (agent_->mirror_table()->FindActiveEntry(&key1)); + EXPECT_TRUE(mirr_entry1 != NULL); + const MirrorNH *mirr_nh1 = static_cast(mirr_entry1->GetNH()); + EXPECT_TRUE(mirr_nh1->GetType() == NextHop::MIRROR); + + + DelVrf("vrf3"); + client->WaitForIdle(); + + mirr_nh = static_cast(mirr_entry->GetNH()); + EXPECT_TRUE(mirr_nh->GetType() == NextHop::DISCARD); + + mirr_nh1 = static_cast(mirr_entry1->GetNH()); + EXPECT_TRUE(mirr_nh1->GetType() == NextHop::DISCARD); + + MirrorEntryKey key2(analyzer2); + const MirrorEntry *mirr_entry2 = static_cast + (agent_->mirror_table()->FindActiveEntry(&key2)); + EXPECT_TRUE(mirr_entry2 != NULL); + const MirrorNH *mirr_nh2 = static_cast(mirr_entry2->GetNH()); + EXPECT_TRUE(mirr_nh2->GetType() == NextHop::DISCARD); + + MirrorTable::DelMirrorEntry(ana); + MirrorTable::DelMirrorEntry(analyzer1); + MirrorTable::DelMirrorEntry(analyzer2); + client->WaitForIdle(); + mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry == NULL); + client->WaitForIdle(); +} + int main(int argc, char *argv[]) { GETUSERARGS();