diff --git a/src/vnsw/agent/cfg/cfg_mirror.cc b/src/vnsw/agent/cfg/cfg_mirror.cc index fc3b51dd4c1..ac9c0d99c2f 100644 --- a/src/vnsw/agent/cfg/cfg_mirror.cc +++ b/src/vnsw/agent/cfg/cfg_mirror.cc @@ -98,12 +98,36 @@ const char *MirrorCfgTable::Add(const MirrorCreateReq &cfg) { return "Invalid mirror destination port "; } - IpAddress sip = agent_cfg_->agent()->GetMirrorSourceIp(dest_ip); - - MirrorTable::AddMirrorEntry(entry->key.handle, - entry->data.mirror_vrf, sip, - agent_cfg_->agent()->mirror_port(), - dest_ip, entry->data.udp_port); + IpAddress sip = agent_cfg_->agent()->GetMirrorSourceIp(dest_ip); + MirrorEntryData::MirrorEntryFlags mirror_flag = + MirrorTable::DecodeMirrorFlag(cfg.get_nhmode(), + cfg.get_juniperheader()); + if (mirror_flag == MirrorEntryData::DynamicNH_With_JuniperHdr) { + MirrorTable::AddMirrorEntry(entry->key.handle, + entry->data.mirror_vrf, sip, + agent_cfg_->agent()->mirror_port(), + dest_ip, entry->data.udp_port); + } else if (mirror_flag == MirrorEntryData::DynamicNH_Without_JuniperHdr) { + MacAddress mac = MacAddress::FromString(cfg.get_analyzer_vm_mac()); + MirrorTable::AddMirrorEntry(entry->key.handle,entry->data.mirror_vrf, + sip, agent_cfg_->agent()->mirror_port(), + dest_ip, entry->data.udp_port, 0, + mirror_flag, mac); + } else if (mirror_flag == MirrorEntryData::StaticNH_Without_JuniperHdr) { + + IpAddress dst_ip = IpAddress::from_string(cfg.get_vtep_dst_ip(), ec); + if (ec.value() != 0) { + delete entry; + return "Invalid mirror destination address "; + } + MirrorTable::AddMirrorEntry(entry->key.handle, entry->data.mirror_vrf, + sip, agent_cfg_->agent()->mirror_port(), + dst_ip, entry->data.udp_port, + cfg.get_vni(), mirror_flag, + MacAddress::ZeroMac()); + } else { + return "Mode not supported"; + } // Update ACL VnAclMap::iterator va_it; @@ -432,12 +456,42 @@ const char *IntfMirrorCfgTable::Add(const IntfMirrorCreateReq &intf_mirror) { entry->data.mirror_dest.time_period = intf_mirror.get_time_period(); entry->data.mirror_dest.mirror_vrf = intf_mirror.get_mirror_vrf(); - MirrorTable::AddMirrorEntry(entry->key.handle, - entry->data.mirror_dest.mirror_vrf, - entry->data.mirror_dest.sip, - entry->data.mirror_dest.sport, - entry->data.mirror_dest.dip, - entry->data.mirror_dest.dport); + MirrorEntryData::MirrorEntryFlags mirror_flag = + MirrorTable::DecodeMirrorFlag (intf_mirror.get_nhmode(), + intf_mirror.get_juniperheader()); + if (mirror_flag == MirrorEntryData::DynamicNH_With_JuniperHdr) { + MirrorTable::AddMirrorEntry(entry->key.handle, + entry->data.mirror_dest.mirror_vrf, + entry->data.mirror_dest.sip, + entry->data.mirror_dest.sport, + entry->data.mirror_dest.dip, + entry->data.mirror_dest.dport); + } else if (mirror_flag == MirrorEntryData::DynamicNH_Without_JuniperHdr) { + MacAddress mac = MacAddress::FromString(intf_mirror.get_analyzer_vm_mac()); + MirrorTable::AddMirrorEntry(entry->key.handle, + entry->data.mirror_dest.mirror_vrf, + entry->data.mirror_dest.sip, + entry->data.mirror_dest.sport, + entry->data.mirror_dest.dip, + entry->data.mirror_dest.dport, 0, + mirror_flag, mac); + } else if (mirror_flag == MirrorEntryData::StaticNH_Without_JuniperHdr) { + IpAddress dst_ip = IpAddress::from_string(intf_mirror.get_vtep_dst_ip(), ec); + if (ec.value() != 0) { + delete entry; + return "Invalid mirror destination address "; + } + MirrorTable::AddMirrorEntry(entry->key.handle, + entry->data.mirror_dest.mirror_vrf, + entry->data.mirror_dest.sip, + entry->data.mirror_dest.sport, + dst_ip, entry->data.mirror_dest.dport, + intf_mirror.get_vni(), mirror_flag, + MacAddress::ZeroMac()); + } else { + return "not supported"; + } + intf_mc_tree_.insert(std::pair(key, entry)); VmInterfaceKey *intf_key = new VmInterfaceKey(AgentKey::ADD_DEL_CHANGE, diff --git a/src/vnsw/agent/filter/acl.cc b/src/vnsw/agent/filter/acl.cc index 709db1d6eb2..26a76381ad2 100644 --- a/src/vnsw/agent/filter/acl.cc +++ b/src/vnsw/agent/filter/acl.cc @@ -944,9 +944,28 @@ void AclEntrySpec::AddMirrorEntry(Agent *agent) const { } IpAddress sip = agent->GetMirrorSourceIp(action.ma.ip); - agent->mirror_table()->AddMirrorEntry(action.ma.analyzer_name, - action.ma.vrf_name, sip, agent->mirror_port(), action.ma.ip, - action.ma.port); + MirrorEntryData::MirrorEntryFlags mirror_flag = + MirrorTable::DecodeMirrorFlag(action.ma.nh_mode, + action.ma.juniper_header); + if (mirror_flag == MirrorEntryData::DynamicNH_With_JuniperHdr) { + agent->mirror_table()->AddMirrorEntry(action.ma.analyzer_name, + action.ma.vrf_name, sip, agent->mirror_port(), action.ma.ip, + action.ma.port); + } else if (mirror_flag == MirrorEntryData::DynamicNH_Without_JuniperHdr) { + // remote_vm_analyzer mac provided from the config + agent->mirror_table()->AddMirrorEntry(action.ma.analyzer_name, + action.ma.vrf_name, sip, agent->mirror_port(), action.ma.ip, + action.ma.port, 0, mirror_flag, action.ma.mac); + } else if (mirror_flag == MirrorEntryData::StaticNH_Without_JuniperHdr) { + // Vtep dst ip & Vni will be provided from the config + agent->mirror_table()->AddMirrorEntry(action.ma.analyzer_name, + action.ma.vrf_name, sip, agent->mirror_port(), + action.ma.staticnhdata.vtep_dst_ip, action.ma.port, + action.ma.staticnhdata.vni, mirror_flag, + action.ma.staticnhdata.vtep_dst_mac); + } else { + LOG(ERROR, "Mirror nh mode not supported"); + } } } @@ -979,6 +998,25 @@ void AclEntrySpec::PopulateAction(const AclTable *acl_table, maction.ma.analyzer_name = action_list.mirror_to.analyzer_name; maction.ma.ip = IpAddress::from_string(action_list.mirror_to.analyzer_ip_address, ec); + maction.ma.juniper_header = action_list.mirror_to.juniper_header; + maction.ma.nh_mode = action_list.mirror_to.nh_mode; + MirrorEntryData::MirrorEntryFlags mirror_flag = + MirrorTable::DecodeMirrorFlag (maction.ma.nh_mode, + maction.ma.juniper_header); + if (mirror_flag == MirrorEntryData::StaticNH_Without_JuniperHdr) { + maction.ma.staticnhdata.vtep_dst_ip = + IpAddress::from_string( + action_list.mirror_to.static_nh_header.vtep_dst_ip_address, ec); + maction.ma.staticnhdata.vtep_dst_mac = + MacAddress::FromString(action_list.mirror_to.static_nh_header.vtep_dst_mac_address); + maction.ma.staticnhdata.vni = + action_list.mirror_to.static_nh_header.vni; + } else if(mirror_flag == MirrorEntryData::DynamicNH_Without_JuniperHdr) { + maction.ma.vrf_name = action_list.mirror_to.routing_instance; + maction.ma.mac = + MacAddress::FromString(action_list.mirror_to.analyzer_mac_address); + } + if (ec.value() == 0) { if (action_list.mirror_to.udp_port) { maction.ma.port = action_list.mirror_to.udp_port; diff --git a/src/vnsw/agent/filter/acl_entry_spec.h b/src/vnsw/agent/filter/acl_entry_spec.h index 93bf67607ff..59d7ea0dd3a 100644 --- a/src/vnsw/agent/filter/acl_entry_spec.h +++ b/src/vnsw/agent/filter/acl_entry_spec.h @@ -22,12 +22,22 @@ struct RangeSpec { uint16_t max; }; +struct StaticMirrorNhData { + IpAddress vtep_dst_ip; + uint32_t vni; + MacAddress vtep_dst_mac; +}; + struct MirrorActionSpec { std::string analyzer_name; std::string vrf_name; IpAddress ip; + MacAddress mac; uint16_t port; std::string encap; + bool juniper_header; + std::string nh_mode; + StaticMirrorNhData staticnhdata; bool operator == (const MirrorActionSpec &rhs) const { return analyzer_name == rhs.analyzer_name; } diff --git a/src/vnsw/agent/oper/agent.sandesh b/src/vnsw/agent/oper/agent.sandesh index 2ee81726b95..72b101b0af9 100644 --- a/src/vnsw/agent/oper/agent.sandesh +++ b/src/vnsw/agent/oper/agent.sandesh @@ -1075,6 +1075,11 @@ request sandesh MirrorCreateReq { // Time period for mirroring in seconds 16: i32 time_period; 17: string mirror_vrf; + 18: optional string nhmode; // dynamic or static NH + 19: optional bool juniperheader; + 20: optional string analyzer_vm_mac; // analyzer mac + 21: optional string vtep_dst_ip; + 22: optional i32 vni; } /** @@ -1251,6 +1256,11 @@ request sandesh IntfMirrorCreateReq { 5: optional i32 udp_port; 6: optional i32 time_period; 7: optional string mirror_vrf; + 8: optional string nhmode; // dynamic or static NH + 9: optional bool juniperheader; + 10: optional string analyzer_vm_mac; // analyzer mac + 11: optional string vtep_dst_ip; + 12: optional i32 vni; } /** diff --git a/src/vnsw/agent/oper/mirror_table.cc b/src/vnsw/agent/oper/mirror_table.cc index 9f276527147..135a6389c97 100644 --- a/src/vnsw/agent/oper/mirror_table.cc +++ b/src/vnsw/agent/oper/mirror_table.cc @@ -19,7 +19,6 @@ using namespace std; using namespace boost::asio; - MirrorTable *MirrorTable::mirror_table_; MirrorTable::~MirrorTable() { @@ -58,16 +57,96 @@ DBEntry *MirrorTable::Add(const DBRequest *req) { return mirror_entry; } +bool MirrorTable::OnChange(MirrorEntry *mirror_entry) { + bool ret = false; + NextHop *nh; + bool valid_nh = false; + if (mirror_entry->mirror_flags_ == + MirrorEntryData::DynamicNH_Without_JuniperHdr) { + VrfEntry *vrf = agent()->vrf_table()->FindVrfFromName(mirror_entry->vrf_name_); + // mirror vrf should have been created + if(vrf != NULL) { + BridgeRouteKey key(agent()->evpn_peer(), mirror_entry->vrf_name_, + mirror_entry->mac_); + BridgeRouteEntry *rt = static_cast + (static_cast + (vrf->GetBridgeRouteTable())->FindActiveEntry(&key)); + + // if route entry is preset assign the active nexthop to mirror entry + // if route entry & active apath is not preset create discard nh + // and add it unresolved entry + if (rt != NULL) { + const AgentPath *path = rt->GetActivePath(); + nh = path->nexthop(); + if (nh != NULL) { + mirror_entry->vni_ = rt->GetActiveLabel(); + AddResolvedVrfMirrorEntry(mirror_entry); + valid_nh = true; + } + } + } + } else { //StaticNH Without Juniper Hdr + InetUnicastRouteEntry *rt = + agent()->fabric_inet4_unicast_table()->FindLPM(mirror_entry->dip_); + //if route entry is preset add the active next hop else add discard nh + if (rt != NULL) { + DBRequest nh_req; + nh_req.oper = DBRequest::DB_ENTRY_ADD_CHANGE; + TunnelNHKey *nh_key = + new TunnelNHKey(agent()->fabric_vrf_name(), + mirror_entry->sip_.to_v4(), + mirror_entry->dip_.to_v4(), + false, TunnelType::VXLAN); + nh_req.key.reset(nh_key); + nh_req.data.reset(NULL); + agent()->nexthop_table()->Process(nh_req); + nh = static_cast + (agent()->nexthop_table()->FindActiveEntry(nh_key)); + if (nh != NULL) { + valid_nh = true; + AddResolvedVrfMirrorEntry(mirror_entry); + } + } + } + + // if there is no Valid nh point it to discard nh + if (!valid_nh) { + DiscardNH key; + nh = static_cast + (agent()->nexthop_table()->FindActiveEntry(&key)); + AddUnresolved(mirror_entry); + } + + if (mirror_entry->nh_ != nh) { + ret = true; + mirror_entry->nh_ = nh; + } + return ret; +} + bool MirrorTable::OnChange(DBEntry *entry, const DBRequest *req) { - bool ret = false; + bool ret = false; MirrorEntry *mirror_entry = static_cast(entry); MirrorEntryData *data = static_cast(req->data.get()); - - mirror_entry->vrf_name_ = data->vrf_name_; + if (mirror_entry->vrf_name_ != data->vrf_name_ || + mirror_entry->mirror_flags_ != data->mirror_flags_) { + DeleteMirrorVrf(mirror_entry); + 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_; + mirror_entry->mirror_flags_ = data->mirror_flags_; + mirror_entry->vni_ = data->vni_; + mirror_entry->mac_ = data->mac_; + mirror_entry->createdvrf_ = data->createdvrf_; + if (mirror_entry->mirror_flags_ == MirrorEntryData::DynamicNH_Without_JuniperHdr || + mirror_entry->mirror_flags_ == MirrorEntryData::StaticNH_Without_JuniperHdr) + { + ret = OnChange(mirror_entry); + return ret; + } DBRequest nh_req; nh_req.oper = DBRequest::DB_ENTRY_ADD_CHANGE; @@ -115,9 +194,26 @@ bool MirrorTable::Delete(DBEntry *entry, const DBRequest *request) { MirrorEntry *mirror_entry = static_cast(entry); RemoveUnresolved(mirror_entry); DeleteResolvedVrfMirrorEntry(mirror_entry); + DeleteMirrorVrf(mirror_entry); return true; } +void MirrorTable::DeleteMirrorVrf(MirrorEntry *entry) { + if (entry->mirror_flags_ == MirrorEntryData::DynamicNH_Without_JuniperHdr){ + RemoveUnresolved(entry); + DeleteResolvedVrfMirrorEntry(entry); + VrfEntry *vrf = + agent()->vrf_table()->FindVrfFromName(entry->vrf_name_); + if (vrf) { + bool confvrf = vrf->flags() & VrfData::ConfigVrf; + bool gwvrf = vrf->flags() & VrfData::GwVrf; + if (entry->createdvrf_ && !confvrf && !gwvrf) + agent()->vrf_table()->DeleteVrfReq(entry->vrf_name_, + VrfData::MirrorVrf); + } + } +} + void MirrorTable::Add(VrfMirrorEntryList &vrf_entry_map, MirrorEntry *entry) { VrfMirrorEntryList::iterator it = vrf_entry_map.find(entry->vrf_name_); @@ -171,7 +267,9 @@ void MirrorTable::ResyncMirrorEntry(VrfMirrorEntryList &list, *((*list_it)->GetSip()), (*list_it)->GetSPort(), *((*list_it)->GetDip()), - (*list_it)->GetDPort()); + (*list_it)->GetDPort(), (*list_it)->GetMirrorFlag(), + (*list_it)->GetVni(), *((*list_it)->GetMac()), + (*list_it)->GetCreatedVrf()); req.key.reset(key); req.data.reset(data); Enqueue(&req); @@ -203,6 +301,41 @@ 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 IpAddress &sip, uint16_t sport, + const IpAddress &dip, uint16_t dport, + uint32_t vni, uint8_t mirror_flag, + const MacAddress &mac) { + Agent *agent = Agent::GetInstance(); + bool createdvrf = false; + DBRequest req; + + if (dip.is_v6() && vrf_name == mirror_table_->agent()->fabric_vrf_name()) { + LOG(ERROR, "Ipv6 as destination not supported on Fabric VRF: " << + dip.to_string()); + return; + } + // if Mirror VRF is not preset create the vrf. + // creatation of VRF ensures all the routes will be dowloaded from control node. + if (mirror_flag == MirrorEntryData::DynamicNH_Without_JuniperHdr) { + VrfEntry *vrf = agent->vrf_table()->FindVrfFromName(vrf_name); + if (vrf == NULL) { + agent->vrf_table()->CreateVrfReq(vrf_name, VrfData::MirrorVrf); + createdvrf = true; + } + } + MirrorEntryKey *key = new MirrorEntryKey(analyzer_name); + MirrorEntryData *data = new MirrorEntryData(vrf_name, sip, sport, dip, + dport, mirror_flag, vni, mac, + createdvrf); + req.oper = DBRequest::DB_ENTRY_ADD_CHANGE; + req.key.reset(key); + req.data.reset(data); + mirror_table_->Enqueue(&req); +} + void MirrorTable::AddMirrorEntry(const std::string &analyzer_name, const std::string &vrf_name, const IpAddress &sip, uint16_t sport, @@ -226,7 +359,8 @@ void MirrorTable::AddMirrorEntry(const std::string &analyzer_name, req.oper = DBRequest::DB_ENTRY_ADD_CHANGE; MirrorEntryKey *key = new MirrorEntryKey(analyzer_name); MirrorEntryData *data = new MirrorEntryData(vrf_name, sip, - sport, dip, dport); + sport, dip, dport, 1, 0 , + MacAddress::ZeroMac(), false); req.key.reset(key); req.data.reset(data); mirror_table_->Enqueue(&req); @@ -263,16 +397,101 @@ void MirrorTable::VrfListenerInit() { } void MirrorTable::VrfNotify(DBTablePartBase *base, DBEntryBase *entry) { - const VrfEntry *vrf = static_cast(entry); + VrfEntry *vrf = static_cast(entry); + MirrorVrfState *state = static_cast + (vrf->GetState(base->parent(), vrf_listener_id_)); if (vrf->IsDeleted()) { + if (state) { + UnRegisterBridgeRouteTableListener(vrf, state); + vrf->ClearState(base->parent(), vrf_listener_id_); + delete state; + } //VRF is getting deleted remove all the mirror nexthop ResyncResolvedMirrorEntry(vrf); return; } + // This will be for dynamic witout juniper header, to resolve the + // bridge entry lookup + bool miror_vrf = UnresolvedMirrorVrf(vrf, unresolved_entry_list_); + if (state == NULL && miror_vrf) { + state = new MirrorVrfState(); + state->seen_ = true; + vrf->SetState(base->parent(), vrf_listener_id_, state); + if (state->bridge_rt_table_listener_id_ == DBTableBase::kInvalidId) { + BridgeAgentRouteTable *bridge_table = + static_cast + (vrf->GetBridgeRouteTable()); + state->bridge_rt_table_listener_id_ = + bridge_table->Register(boost::bind(&MirrorTable::BridgeRouteTableNotify, + this, _1, _2)); + } + } ResyncUnresolvedMirrorEntry(vrf); } +bool MirrorTable::UnresolvedMirrorVrf(const VrfEntry *vrf, + VrfMirrorEntryList &list){ + VrfMirrorEntryList::iterator it = list.find(vrf->GetName()); + if (it == list.end()) { + return false; + } + // Need to check if there are any entries with DynamicNH_Without_JuniperHdr + MirrorEntryList::iterator list_it = it->second.begin(); + for(;list_it != it->second.end(); list_it++) { + if ((*list_it)->GetMirrorFlag() == + MirrorEntryData::DynamicNH_Without_JuniperHdr) { + return true; + } + } + return false; +} +// if the Unresolved remote mac is present it will return the entry +MirrorEntry* +MirrorTable::UnresolvedMirrorEntry(VrfEntry *vrf, const MacAddress & mac, + VrfMirrorEntryList &list) { + VrfMirrorEntryList::iterator it = list.find(vrf->GetName()); + if (it == list.end()) { + return NULL; + } + MirrorEntryList::iterator list_it = it->second.begin(); + for(;list_it != it->second.end(); list_it++) { + const MacAddress &remote_vm_mac = *((*list_it)->GetMac()); + if (remote_vm_mac == mac) { + return (*list_it); + } + } + return NULL; +} + + +void MirrorTable::BridgeRouteTableNotify(DBTablePartBase *partition, + DBEntryBase *entry) { + const BridgeRouteEntry *bridge_rt = static_cast(entry); + if (bridge_rt->IsDeleted()) { + ResyncResolvedMirrorEntry(bridge_rt->vrf()); + } else { + MirrorEntry *mirror_entry = UnresolvedMirrorEntry(bridge_rt->vrf(), + bridge_rt->mac(), + unresolved_entry_list_); + if (mirror_entry && + mirror_entry->mirror_flags_ == + MirrorEntryData::DynamicNH_Without_JuniperHdr) { + ResyncUnresolvedMirrorEntry(bridge_rt->vrf()); + } + } +} + +void MirrorTable::UnRegisterBridgeRouteTableListener(const VrfEntry *vrf, + MirrorVrfState *state) { + if (state->bridge_rt_table_listener_id_ == DBTableBase::kInvalidId) + return; + BridgeAgentRouteTable *bridge_table = static_cast + (vrf->GetBridgeRouteTable()); + bridge_table->Unregister(state->bridge_rt_table_listener_id_); + state->bridge_rt_table_listener_id_ = DBTableBase::kInvalidId; +} + void MirrorTable::ReadHandler(const boost::system::error_code &ec, size_t bytes_transferred) { @@ -369,3 +588,17 @@ AgentSandeshPtr MirrorTable::GetAgentSandesh(const AgentSandeshArguments *args, return AgentSandeshPtr(new AgentMirrorSandesh(context, args->GetString("analyzer_name"))); } + +MirrorEntryData::MirrorEntryFlags +MirrorTable::DecodeMirrorFlag (const std::string &nh_mode, bool juniper_header) { + std::string str = "static"; + if (juniper_header) { + if (boost::iequals(nh_mode, str)) + return MirrorEntryData::StaticNH_With_JuniperHdr; + return MirrorEntryData::DynamicNH_With_JuniperHdr; + } else { + if (boost::iequals(nh_mode, str)) + return MirrorEntryData::StaticNH_Without_JuniperHdr; + return MirrorEntryData::DynamicNH_Without_JuniperHdr; + } +} diff --git a/src/vnsw/agent/oper/mirror_table.h b/src/vnsw/agent/oper/mirror_table.h index 3860e788041..9b0dd539b30 100644 --- a/src/vnsw/agent/oper/mirror_table.h +++ b/src/vnsw/agent/oper/mirror_table.h @@ -17,22 +17,38 @@ struct MirrorEntryKey : public AgentKey { }; struct MirrorEntryData : public AgentData { + enum MirrorEntryFlags { + DynamicNH_With_JuniperHdr = 1, + DynamicNH_Without_JuniperHdr = 2, + StaticNH_With_JuniperHdr = 3, + StaticNH_Without_JuniperHdr = 4 + + }; + MirrorEntryData(const std::string vrf_name, const IpAddress &sip, const uint16_t sport, const IpAddress &dip, - const uint16_t dport): vrf_name_(vrf_name), - sip_(sip), sport_(sport), dip_(dip), dport_(dport) { }; + const uint16_t dport, uint8_t mirror_flags, + uint32_t vni, const MacAddress &mac, bool createdvrf): + vrf_name_(vrf_name), sip_(sip), sport_(sport), dip_(dip), + dport_(dport), mirror_flags_(mirror_flags), vni_(vni), + mac_(mac), createdvrf_(createdvrf){ }; + std::string vrf_name_; IpAddress sip_; uint16_t sport_; IpAddress dip_; uint16_t dport_; + uint8_t mirror_flags_; + uint32_t vni_; + MacAddress mac_; // can be type of vtep mac or analyzer-mac based on type NH + bool createdvrf_; }; class MirrorEntry : AgentRefCount, public AgentDBEntry { public: MirrorEntry(std::string analyzer_name) : analyzer_name_(analyzer_name), vrf_(NULL, this), nh_(NULL), - vrf_name_("") { }; + vrf_name_(""), createdvrf_(false) { }; virtual ~MirrorEntry() { }; virtual bool IsLess(const DBEntry &rhs) const; @@ -57,7 +73,10 @@ class MirrorEntry : AgentRefCount, public AgentDBEntry { uint16_t GetDPort() const {return dport_;} const NextHop *GetNH() const {return nh_.get();} const std::string vrf_name() const {return vrf_name_;} - + uint32_t GetVni() const {return vni_;} + uint8_t GetMirrorFlag() const {return mirror_flags_;} + const MacAddress *GetMac() const { return &mac_;} + bool GetCreatedVrf() const {return createdvrf_;} private: std::string analyzer_name_; VrfEntryRef vrf_; @@ -67,6 +86,12 @@ class MirrorEntry : AgentRefCount, public AgentDBEntry { uint16_t dport_; NextHopRef nh_; std::string vrf_name_; + uint8_t mirror_flags_; + uint32_t vni_; + MacAddress mac_; // can be type of vtep mac or analyzer-mac based on type NH + // this vrf will be created if this mirror vrf is not known to compute node + // add it subscribes for the route information to get down loaded + bool createdvrf_; friend class MirrorTable; }; @@ -99,6 +124,12 @@ class MirrorTable : public AgentDBTable { const std::string &vrf_name, const IpAddress &sip, uint16_t sport, const IpAddress &dip, uint16_t dport); + static void AddMirrorEntry(const std::string &analyzer_name, + const std::string &vrf_name, + const IpAddress &sip, uint16_t sport, + const IpAddress &dip, uint16_t dport, + uint32_t vni, uint8_t mirror_flag, + const MacAddress &mac); static void DelMirrorEntry(const std::string &analyzer_name); virtual void OnZeroRefcount(AgentDBEntry *e); static DBTableBase *CreateTable(DB *db, const std::string &name); @@ -118,7 +149,22 @@ class MirrorTable : public AgentDBTable { void VrfNotify(DBTablePartBase *root, DBEntryBase *entry); void Shutdown(); void Initialize(); - + bool OnChange(MirrorEntry *mirror_entry); + struct MirrorVrfState : DBState { + MirrorVrfState() : DBState(), seen_(false), + bridge_rt_table_listener_id_(DBTableBase::kInvalidId) {} + bool seen_; + DBTableBase::ListenerId bridge_rt_table_listener_id_; + }; + void BridgeRouteTableNotify(DBTablePartBase *partition, DBEntryBase *e); + void UnRegisterBridgeRouteTableListener(const VrfEntry *entry, + MirrorVrfState *state); + bool UnresolvedMirrorVrf(const VrfEntry *vrf, VrfMirrorEntryList &list); + MirrorEntry* UnresolvedMirrorEntry(VrfEntry *vrf, const MacAddress & mac, + VrfMirrorEntryList &list); + static MirrorEntryData::MirrorEntryFlags + DecodeMirrorFlag (const std::string &nh_mode, bool juniper_header); + void DeleteMirrorVrf(MirrorEntry *entry); private: std::auto_ptr udp_sock_; static MirrorTable *mirror_table_; diff --git a/src/vnsw/agent/oper/vm_interface.cc b/src/vnsw/agent/oper/vm_interface.cc index c78252d9898..65b7d72f364 100644 --- a/src/vnsw/agent/oper/vm_interface.cc +++ b/src/vnsw/agent/oper/vm_interface.cc @@ -726,12 +726,35 @@ static void ReadAnalyzerNameAndCreate(Agent *agent, } else { dport = ContrailPorts::AnalyzerUdpPort(); } + MirrorEntryData::MirrorEntryFlags mirror_flag = + MirrorTable::DecodeMirrorFlag(mirror_to.nh_mode, + mirror_to.juniper_header); // not using the vrf coming in; by setting this to empty, -1 will be // configured so that current VRF will be used (for leaked routes). - agent->mirror_table()->AddMirrorEntry - (mirror_to.analyzer_name, std::string(), - agent->GetMirrorSourceIp(dip), - agent->mirror_port(), dip, dport); + if (mirror_flag == MirrorEntryData::DynamicNH_With_JuniperHdr) { + agent->mirror_table()->AddMirrorEntry + (mirror_to.analyzer_name, std::string(), + agent->GetMirrorSourceIp(dip), + agent->mirror_port(), dip, dport); + } else if (mirror_flag == MirrorEntryData::DynamicNH_Without_JuniperHdr) { + agent->mirror_table()->AddMirrorEntry(mirror_to.analyzer_name, + mirror_to.routing_instance, agent->GetMirrorSourceIp(dip), + agent->mirror_port(), dip, dport, 0, mirror_flag, + MacAddress::FromString(mirror_to.analyzer_mac_address)); + } else if (mirror_flag == MirrorEntryData::StaticNH_Without_JuniperHdr) { + IpAddress vtep_dip = + IpAddress::from_string(mirror_to.static_nh_header.vtep_dst_ip_address, ec); + if (ec.value() != 0) { + return; + } + agent->mirror_table()->AddMirrorEntry(mirror_to.analyzer_name, + mirror_to.routing_instance, agent->GetMirrorSourceIp(dip), + agent->mirror_port(), vtep_dip, dport, + mirror_to.static_nh_header.vni, mirror_flag, + MacAddress::FromString(mirror_to.static_nh_header.vtep_dst_mac_address)); + } else { + LOG(ERROR, "Mirror nh mode not supported"); + } data.analyzer_name_ = mirror_to.analyzer_name; string traffic_direction = cfg->properties().interface_mirror.traffic_direction; diff --git a/src/vnsw/agent/oper/vrf.h b/src/vnsw/agent/oper/vrf.h index 68a796b08cf..482a4c2fddb 100644 --- a/src/vnsw/agent/oper/vrf.h +++ b/src/vnsw/agent/oper/vrf.h @@ -41,6 +41,7 @@ struct VrfData : public AgentOperDBData { enum VrfEntryFlags { ConfigVrf = 1 << 0, // vrf is received from config GwVrf = 1 << 1, // GW configured for this VRF + MirrorVrf = 1 << 2, // internally Created VRF }; VrfData(Agent *agent, IFMapNode *node, uint32_t flags, diff --git a/src/vnsw/agent/test/test_mirror.cc b/src/vnsw/agent/test/test_mirror.cc index 07800fdab1f..ef39329b63a 100644 --- a/src/vnsw/agent/test/test_mirror.cc +++ b/src/vnsw/agent/test/test_mirror.cc @@ -57,7 +57,7 @@ class MirrorTableTest : public ::testing::Test { } virtual void TearDown() { - WAIT_FOR(1000, 1000, agent_->nexthop_table()->Size() == nh_count_); + WAIT_FOR(1002, 1000, agent_->nexthop_table()->Size() == nh_count_); WAIT_FOR(1000, 1000, agent_->vrf_table()->Size() == 1); } @@ -433,6 +433,297 @@ TEST_F(MirrorTableTest, MirrorEntryAddDel_6) { client->WaitForIdle(); } +//This test is to verify the Dynamic without Juniper header config +//Add Mirror Entry and check it is attached to the existing +//Tunnel NH created by BridgeTunnelRouteAdd +// Check that Static NH changed to MirrorNH after moving the nh mode +TEST_F(MirrorTableTest, StaticMirrorEntryAdd_6) { + Ip4Address vhost_ip(agent_->router_id()); + Ip4Address remote_server = Ip4Address::from_string("1.1.1.1"); + Ip4Address remote_vm_ip4_2 = Ip4Address::from_string("2.2.2.11"); + //Add mirror entry pointing to same vhost IP + std::string ana = analyzer + "r"; + std::string remote_vm_mac_str_; + MacAddress remote_vm_mac = MacAddress::FromString("00:00:01:01:01:11"); + TunnelType::TypeBmap bmap; + bmap = 1 << TunnelType::VXLAN; + AddVrf("vrf3"); + client->WaitForIdle(); + BridgeTunnelRouteAdd(agent_->local_peer(), "vrf3", bmap, remote_server, + 1, remote_vm_mac, remote_vm_ip4_2, 32); + client->WaitForIdle(); + MirrorTable::AddMirrorEntry(ana, "vrf3", vhost_ip, 0x1, remote_server, + 0x2, 0 , 2, remote_vm_mac); + client->WaitForIdle(); + + MirrorEntryKey key(ana); + const MirrorEntry *mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry != NULL); + const NextHop *mirr_nh = mirr_entry->GetNH(); + EXPECT_TRUE(mirr_nh->GetType() == NextHop::TUNNEL); + + EvpnAgentRouteTable::DeleteReq(agent_->local_peer(), "vrf3", remote_vm_mac, + remote_vm_ip4_2, 0, NULL); + MirrorTable::AddMirrorEntry(ana, "vrf3", + vhost_ip, 0x1, remote_server, 0x2); + client->WaitForIdle(); + + mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry != NULL); + mirr_nh = static_cast(mirr_entry->GetNH()); + mirr_nh = mirr_entry->GetNH(); + EXPECT_TRUE(mirr_nh->GetType() == NextHop::MIRROR); + DelVrf("vrf3"); + client->WaitForIdle(); + mirr_nh = mirr_entry->GetNH(); + EXPECT_TRUE(mirr_nh->GetType() == NextHop::DISCARD); + + MirrorTable::DelMirrorEntry(ana); + client->WaitForIdle(); + mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry == NULL); + client->WaitForIdle(); +} + +//This test is to verify the Dynamic without Juniper header config +//Add Mirror Entry without mirror VRF so that Mirror entry will create the +//vrf and attach to the Tunnel NH created by BridgeTunnelRouteAdd +// Change the VRF and check NH refrence is released +TEST_F(MirrorTableTest, StaticMirrorEntryAdd_7) { + Ip4Address vhost_ip(agent_->router_id()); + Ip4Address remote_server = Ip4Address::from_string("1.1.1.1"); + Ip4Address remote_vm_ip4_2 = Ip4Address::from_string("2.2.2.11"); + //Add mirror entry pointing to same vhost IP + std::string ana = analyzer + "r"; + MacAddress remote_vm_mac = MacAddress::FromString("00:00:01:01:01:11"); + TunnelType::TypeBmap bmap; + bmap = 1 << TunnelType::VXLAN; + client->WaitForIdle(); + + MirrorTable::AddMirrorEntry(ana, "vrf3", vhost_ip, 0x1, remote_server, + 0x2, 0 , 2, remote_vm_mac); + + BridgeTunnelRouteAdd(agent_->local_peer(), "vrf3", bmap, remote_server, + 1, remote_vm_mac, remote_vm_ip4_2, 32); + client->WaitForIdle(); + + MirrorEntryKey key(ana); + const MirrorEntry *mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry != NULL); + const NextHop *mirr_nh = mirr_entry->GetNH(); + EXPECT_TRUE(mirr_nh->GetType() == NextHop::TUNNEL); + + EvpnAgentRouteTable::DeleteReq(agent_->local_peer(), "vrf3", remote_vm_mac, + remote_vm_ip4_2, 0, NULL); + client->WaitForIdle(); + + MirrorTable::AddMirrorEntry(ana, "vrf4", vhost_ip, 0x1, remote_server, + 0x2, 0 , 2, remote_vm_mac); + BridgeTunnelRouteAdd(agent_->local_peer(), "vrf4", bmap, remote_server, + 1, remote_vm_mac, remote_vm_ip4_2, 32); + + client->WaitForIdle(); + mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry != NULL); + mirr_nh = mirr_entry->GetNH(); + EXPECT_TRUE(mirr_nh->GetType() == NextHop::TUNNEL); + EvpnAgentRouteTable::DeleteReq(agent_->local_peer(), "vrf4", remote_vm_mac, + remote_vm_ip4_2, 0, NULL); + client->WaitForIdle(); + MirrorTable::DelMirrorEntry(ana); + client->WaitForIdle(); + mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry == NULL); + client->WaitForIdle(); +} + +static void CreateTunnelNH(const string &vrf_name, const Ip4Address &sip, + const Ip4Address &dip, bool policy, + TunnelType::TypeBmap bmap){ + DBRequest req; + TunnelNHData *data = new TunnelNHData(); + + NextHopKey *key = new TunnelNHKey(vrf_name, sip, dip, policy, + TunnelType::ComputeType(bmap)); + req.oper = DBRequest::DB_ENTRY_ADD_CHANGE; + req.key.reset(key); + req.data.reset(data); + Agent::GetInstance()->nexthop_table()->Enqueue(&req); +} + +static void DeleteTunnelNH(const string &vrf_name, const Ip4Address &sip, + const Ip4Address &dip, bool policy, + TunnelType::TypeBmap bmap){ + DBRequest req; + TunnelNHData *data = new TunnelNHData(); + + NextHopKey *key = new TunnelNHKey(vrf_name, sip, dip, policy, + TunnelType::ComputeType(bmap)); + req.oper = DBRequest::DB_ENTRY_DELETE; + req.key.reset(key); + req.data.reset(data); + Agent::GetInstance()->nexthop_table()->Enqueue(&req); +} + +void AddResolveRoute(const Ip4Address &server_ip, uint32_t plen) { + Agent* agent = Agent::GetInstance(); + InetInterfaceKey vhost_key(agent->vhost_interface()->name()); + agent->fabric_inet4_unicast_table()->AddResolveRoute( + agent->local_peer(), + agent->fabric_vrf_name(), server_ip, plen, vhost_key, + 0, false, "", SecurityGroupList()); + client->WaitForIdle(); +} + +void DeleteRoute(const Peer *peer, const std::string &vrf_name, + const Ip4Address &addr, uint32_t plen) { + Agent::GetInstance()->fabric_inet4_unicast_table()->DeleteReq(peer, vrf_name, + addr, plen, NULL); + client->WaitForIdle(); + client->WaitForIdle(); +} + +//This test is to verify the Static without Juniper header config +//create a route & add tunnelnh through test +//check that Mirror entry attached to vxlan tunnel nh +TEST_F(MirrorTableTest, StaticMirrorEntryAdd_8) { + Ip4Address vhost_ip(agent_->router_id()); + //Add mirror entry pointing to same vhost IP + Ip4Address remote_server = Ip4Address::from_string("8.8.8.8"); + MacAddress remote_vm_mac = MacAddress::FromString("00:00:08:08:08:08"); + std::string ana = analyzer + "r"; + TunnelType::TypeBmap bmap; + bmap = 1 << TunnelType::VXLAN; + AddResolveRoute(remote_server, 32); + client->WaitForIdle(); + AddArp("8.8.8.8", "00:00:08:08:08:08", agent_->fabric_interface_name().c_str()); + client->WaitForIdle(); + CreateTunnelNH(agent_->fabric_vrf_name(), vhost_ip, remote_server, false, bmap); + client->WaitForIdle(); + + MirrorTable::AddMirrorEntry(ana, "vrf3", vhost_ip, 0x1, remote_server, + 0x2, 0 , 4, remote_vm_mac); + client->WaitForIdle(); + + MirrorEntryKey key(ana); + const MirrorEntry *mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry != NULL); + const NextHop *mirr_nh = mirr_entry->GetNH(); + EXPECT_TRUE(mirr_nh->GetType() == NextHop::TUNNEL); + + client->WaitForIdle(); + DeleteTunnelNH(agent_->fabric_vrf_name(), vhost_ip, remote_server, false, bmap); + client->WaitForIdle(); + DelArp("8.8.8.8", "00:00:08:08:08:08", agent_->fabric_interface_name().c_str()); + client->WaitForIdle(); + DeleteRoute(agent_->local_peer(), agent_->fabric_vrf_name(), remote_server, + 32); + client->WaitForIdle(); + MirrorTable::DelMirrorEntry(ana); + client->WaitForIdle(); + + mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry == NULL); + client->WaitForIdle(); +} +//This test is to verify the Static without Juniper header config +//create a route in resolved state and check that Mirror entry creates tunnel nh +//and attaches to it. +TEST_F(MirrorTableTest, StaticMirrorEntryAdd_9) { + Ip4Address vhost_ip(agent_->router_id()); + //Add mirror entry pointing to same vhost IP + Ip4Address remote_server = Ip4Address::from_string("8.8.8.8"); + MacAddress remote_vm_mac = MacAddress::FromString("00:00:08:08:08:08"); + std::string ana = analyzer + "r"; + AddResolveRoute(remote_server, 16); + + client->WaitForIdle(); + + MirrorTable::AddMirrorEntry(ana, "vrf3", vhost_ip, 0x1, remote_server, + 0x2, 2 , 4, remote_vm_mac); + client->WaitForIdle(); + AddArp("8.8.8.8", "00:00:08:08:08:08", agent_->fabric_interface_name().c_str()); + client->WaitForIdle(); + MirrorEntryKey key(ana); + const MirrorEntry *mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry != NULL); + const NextHop *mirr_nh = mirr_entry->GetNH(); + EXPECT_TRUE(mirr_nh->GetType() == NextHop::TUNNEL); + + DelArp("8.8.8.8", "00:00:08:08:08:08", agent_->fabric_interface_name().c_str()); + client->WaitForIdle(); + DeleteRoute(agent_->local_peer(), agent_->fabric_vrf_name(), remote_server, + 16); + DeleteRoute(agent_->local_peer(), agent_->fabric_vrf_name(), remote_server, + 32); + client->WaitForIdle(); + MirrorTable::DelMirrorEntry(ana); + client->WaitForIdle(); + + mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry == NULL); + client->WaitForIdle(); +} +// This test case is to move the mode from dynamic without juniper hdr +// to dynamic with juniper header after moving see that internally created +// VRF is deleted and check that MirrorNH points to discard NH +TEST_F(MirrorTableTest, StaticMirrorEntryAdd_10) { + Ip4Address vhost_ip(agent_->router_id()); + Ip4Address remote_server = Ip4Address::from_string("1.1.1.1"); + Ip4Address remote_vm_ip4_2 = Ip4Address::from_string("2.2.2.11"); + //Add mirror entry pointing to same vhost IP + std::string ana = analyzer + "r"; + MacAddress remote_vm_mac = MacAddress::FromString("00:00:01:01:01:11"); + TunnelType::TypeBmap bmap; + bmap = 1 << TunnelType::VXLAN; + client->WaitForIdle(); + + MirrorTable::AddMirrorEntry(ana, "vrf3", vhost_ip, 0x1, remote_server, + 0x2, 0 , 2, remote_vm_mac); + + BridgeTunnelRouteAdd(agent_->local_peer(), "vrf3", bmap, remote_server, + 1, remote_vm_mac, remote_vm_ip4_2, 32); + client->WaitForIdle(); + + MirrorEntryKey key(ana); + const MirrorEntry *mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry != NULL); + const NextHop *mirr_nh = mirr_entry->GetNH(); + EXPECT_TRUE(mirr_nh->GetType() == NextHop::TUNNEL); + + EvpnAgentRouteTable::DeleteReq(agent_->local_peer(), "vrf3", remote_vm_mac, + remote_vm_ip4_2, 0, NULL); + client->WaitForIdle(); + MirrorTable::AddMirrorEntry(ana, "vrf3", + vhost_ip, 0x1, remote_server, 0x2); + client->WaitForIdle(); + + mirr_entry = static_cast + (agent_->mirror_table()->FindActiveEntry(&key)); + EXPECT_TRUE(mirr_entry != NULL); + mirr_nh = static_cast(mirr_entry->GetNH()); + mirr_nh = mirr_entry->GetNH(); + EXPECT_TRUE(mirr_nh->GetType() == NextHop::DISCARD); + client->WaitForIdle(); + MirrorTable::DelMirrorEntry(ana); + 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(); diff --git a/src/vnsw/agent/vrouter/ksync/mirror_ksync.cc b/src/vnsw/agent/vrouter/ksync/mirror_ksync.cc index 71aa8519ab5..b0b340436df 100644 --- a/src/vnsw/agent/vrouter/ksync/mirror_ksync.cc +++ b/src/vnsw/agent/vrouter/ksync/mirror_ksync.cc @@ -13,7 +13,8 @@ MirrorKSyncEntry::MirrorKSyncEntry(MirrorKSyncObject *obj, uint32_t index) : KSyncNetlinkDBEntry(index), ksync_obj_(obj), vrf_id_(entry->vrf_id_), sip_(entry->sip_), sport_(entry->sport_), dip_(entry->dip_), - dport_(entry->dport_), analyzer_name_(entry->analyzer_name_) { + dport_(entry->dport_), analyzer_name_(entry->analyzer_name_), + mirror_flag_(entry->mirror_flag_), vni_(entry->vni_) { } MirrorKSyncEntry::MirrorKSyncEntry(MirrorKSyncObject *obj, @@ -29,7 +30,8 @@ MirrorKSyncEntry::MirrorKSyncEntry(MirrorKSyncObject *obj, vrf_id_(mirror_entry->vrf_id()), sip_(*mirror_entry->GetSip()), sport_(mirror_entry->GetSPort()), dip_(*mirror_entry->GetDip()), dport_(mirror_entry->GetDPort()), nh_(NULL), - analyzer_name_(mirror_entry->GetAnalyzerName()) { + analyzer_name_(mirror_entry->GetAnalyzerName()), + mirror_flag_(mirror_entry->GetMirrorFlag()), vni_(mirror_entry->GetVni()) { } MirrorKSyncEntry::MirrorKSyncEntry(MirrorKSyncObject *obj, @@ -90,6 +92,8 @@ int MirrorKSyncEntry::Encode(sandesh_op::type op, char *buf, int buf_len) { encoder.set_mirr_index(GetIndex()); encoder.set_mirr_rid(0); encoder.set_mirr_nhid(nh_entry->nh_id()); + encoder.set_mirr_vni(vni_); + encoder.set_mirr_flags(mirror_flag_); int error = 0; encode_len = encoder.WriteBinary((uint8_t *)buf, buf_len, &error); assert(error == 0); diff --git a/src/vnsw/agent/vrouter/ksync/mirror_ksync.h b/src/vnsw/agent/vrouter/ksync/mirror_ksync.h index 0c2f458163b..8fa0d977ecf 100644 --- a/src/vnsw/agent/vrouter/ksync/mirror_ksync.h +++ b/src/vnsw/agent/vrouter/ksync/mirror_ksync.h @@ -47,6 +47,8 @@ class MirrorKSyncEntry : public KSyncNetlinkDBEntry { uint16_t dport_; KSyncEntryPtr nh_; std::string analyzer_name_; + uint8_t mirror_flag_; + uint32_t vni_; DISALLOW_COPY_AND_ASSIGN(MirrorKSyncEntry); };