From 6722f562d375771debe8b4705b247b38155494b9 Mon Sep 17 00:00:00 2001 From: Naveen N Date: Wed, 25 Nov 2015 12:16:05 +0530 Subject: [PATCH] * Agent changes to support fat flow * Maintain a list of protocol and port specified as fat flow Ignore flow matching protocol + port combination. Closes-bug:#1518234 Conflicts: src/vnsw/agent/oper/test/test_intf.cc src/vnsw/agent/oper/vm_interface.cc Change-Id: Iaa9b14f74276ca011906bff89b03841049acd4f4 --- src/vnsw/agent/cmn/agent.cc | 21 +++++++ src/vnsw/agent/cmn/agent.h | 2 + src/vnsw/agent/oper/test/test_intf.cc | 41 +++++++++++++ src/vnsw/agent/oper/vm_interface.cc | 61 ++++++++++++++++++- src/vnsw/agent/oper/vm_interface.h | 45 ++++++++++++++ src/vnsw/agent/pkt/pkt_flow_info.cc | 48 +++++++++++++++ src/vnsw/agent/pkt/pkt_flow_info.h | 3 +- src/vnsw/agent/pkt/test/egress-flow.xml | 40 ++++++++++++ src/vnsw/agent/pkt/test/ingress-flow.xml | 34 ++++++++++- src/vnsw/agent/test-xml/test_xml_oper.cc | 21 +++++++ src/vnsw/agent/test-xml/test_xml_oper.h | 3 + .../agent/vrouter/ksync/interface_ksync.cc | 6 ++ .../agent/vrouter/ksync/interface_ksync.h | 1 + 13 files changed, 320 insertions(+), 6 deletions(-) diff --git a/src/vnsw/agent/cmn/agent.cc b/src/vnsw/agent/cmn/agent.cc index 6d7d5facbe8..c364001c8bd 100644 --- a/src/vnsw/agent/cmn/agent.cc +++ b/src/vnsw/agent/cmn/agent.cc @@ -655,6 +655,27 @@ void Agent::SetAgentMcastLabelRange(uint8_t idx) { label_range_[idx] = str.str(); } +uint16_t +Agent::ProtocolStringToInt(const std::string &proto) { + if (proto == "tcp" || proto == "TCP") { + return IPPROTO_TCP; + } + + if (proto == "udp" || proto == "UDP") { + return IPPROTO_UDP; + } + + if (proto == "sctp" || proto == "SCTP") { + return IPPROTO_SCTP; + } + + if (proto =="icmp" || proto == "ICMP") { + return IPPROTO_ICMP; + } + + return atoi(proto.c_str()); +} + Agent::ForwardingMode Agent::TranslateForwardingMode (const std::string &mode) const { if (mode == "l2") diff --git a/src/vnsw/agent/cmn/agent.h b/src/vnsw/agent/cmn/agent.h index de8cd190ccf..f9283552740 100644 --- a/src/vnsw/agent/cmn/agent.h +++ b/src/vnsw/agent/cmn/agent.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -916,6 +917,7 @@ class Agent { flow_stats_req_handler_ = req; } + static uint16_t ProtocolStringToInt(const std::string &str); private: AgentParam *params_; diff --git a/src/vnsw/agent/oper/test/test_intf.cc b/src/vnsw/agent/oper/test/test_intf.cc index be50bd50ccb..2f844cdeb2f 100644 --- a/src/vnsw/agent/oper/test/test_intf.cc +++ b/src/vnsw/agent/oper/test/test_intf.cc @@ -165,6 +165,19 @@ class IntfTest : public ::testing::Test { client->WaitForIdle(); } + void AddFatFlow(struct PortInfo *input, std::string protocol, int port) { + + ostringstream str; + + str << "" + "" + "" << protocol << "" + "" << port << "" + "" + ""; + AddNode("virtual-machine-interface", input[0].name, input[0].intf_id, + str.str().c_str()); + } unsigned int intf_count; Agent *agent; @@ -3248,6 +3261,34 @@ TEST_F(IntfTest, IntfAddDel) { client->Reset(); } +TEST_F(IntfTest, FatFlow) { + struct PortInfo input[] = { + {"vnet1", 1, "1.1.1.1", "00:00:00:00:00:01", 1, 1}, + }; + + CreateVmportEnv(input, 1); + client->WaitForIdle(); + EXPECT_TRUE(VmPortFind(1)); + + const VmInterface *intf = static_cast(VmPortGet(1)); + EXPECT_TRUE(intf->fat_flow_list().list_.size() == 0); + + AddFatFlow(input, "udp", 53); + client->WaitForIdle(); + EXPECT_TRUE(intf->fat_flow_list().list_.size() == 1); + EXPECT_TRUE(intf->IsFatFlow(IPPROTO_UDP, 53) == true); + EXPECT_TRUE(intf->IsFatFlow(IPPROTO_UDP, 0) == false); + + DelNode("virtual-machine-interface", "vnet1"); + client->WaitForIdle(); + EXPECT_TRUE(intf->fat_flow_list().list_.size() == 0); + + DeleteVmportEnv(input, 1, true); + client->WaitForIdle(); + EXPECT_FALSE(VmPortFind(1)); + client->Reset(); +} + int main(int argc, char **argv) { GETUSERARGS(); diff --git a/src/vnsw/agent/oper/vm_interface.cc b/src/vnsw/agent/oper/vm_interface.cc index ee8e0522bc2..0248c685363 100644 --- a/src/vnsw/agent/oper/vm_interface.cc +++ b/src/vnsw/agent/oper/vm_interface.cc @@ -60,7 +60,7 @@ VmInterface::VmInterface(const boost::uuids::uuid &uuid) : tx_vlan_id_(kInvalidVlanId), rx_vlan_id_(kInvalidVlanId), parent_(NULL), local_preference_(VmInterface::INVALID), oper_dhcp_options_(), sg_list_(), floating_ip_list_(), service_vlan_list_(), static_route_list_(), - allowed_address_pair_list_(), vrf_assign_rule_list_(), + allowed_address_pair_list_(), fat_flow_list_(), vrf_assign_rule_list_(), vrf_assign_acl_(NULL), vm_ip_gw_addr_(0), vm_ip6_gw_addr_(), device_type_(VmInterface::DEVICE_TYPE_INVALID), vmi_type_(VmInterface::VMI_TYPE_INVALID), @@ -408,6 +408,18 @@ static void BuildVrfAndServiceVlanInfo(Agent *agent, return; } +static void BuildFatFlowTable(Agent *agent, VmInterfaceConfigData *data, + IFMapNode *node) { + VirtualMachineInterface *cfg = static_cast + (node->GetObject()); + for (FatFlowProtocols::const_iterator it = cfg->fat_flow_protocols().begin(); + it != cfg->fat_flow_protocols().end(); it++) { + uint16_t protocol = Agent::ProtocolStringToInt(it->protocol); + data->fat_flow_list_.list_.insert(VmInterface::FatFlowEntry(protocol, + it->port)); + } +} + static void BuildInstanceIp(Agent *agent, VmInterfaceConfigData *data, IFMapNode *node) { InstanceIp *ip = static_cast(node->GetObject()); @@ -798,6 +810,14 @@ bool VmInterface::IsConfigurerSet(VmInterface::Configurer type) { return ((configurer_ & (1 << type)) != 0); } +bool VmInterface::IsFatFlow(uint8_t protocol, uint16_t port) const { + if (fat_flow_list_.list_.find(FatFlowEntry(protocol, port)) != + fat_flow_list_.list_.end()) { + return true; + } + return false; +} + static bool DeleteVmi(InterfaceTable *table, const uuid &u, DBRequest *req) { int type = table->GetVmiToVmiType(u); if (type <= (int)VmInterface::VMI_TYPE_INVALID) @@ -903,6 +923,7 @@ bool InterfaceTable::VmiProcessConfig(IFMapNode *node, DBRequest &req) { } UpdateAttributes(agent_, data); + BuildFatFlowTable(agent_, data, node); // Get DHCP enable flag from subnet if (vn_node && data->addr_.to_ulong()) { @@ -1331,10 +1352,12 @@ void VmInterface::ApplyConfigCommon(const VrfEntry *old_vrf, //DHCP MAC IP binding ApplyMacVmBindingConfig(old_vrf, old_l2_active, old_dhcp_enable); //Security Group update - if (IsActive()) + if (IsActive()) { UpdateSecurityGroup(); - else + } else { DeleteSecurityGroup(); + DeleteFatFlow(); + } } @@ -1729,6 +1752,16 @@ bool VmInterface::CopyConfig(const InterfaceTable *table, ret = true; } + FatFlowEntrySet &old_fat_flow_entry_list = fat_flow_list_.list_; + const FatFlowEntrySet &new_fat_flow_entry_list = + data->fat_flow_list_.list_; + if (AuditList + (fat_flow_list_, old_fat_flow_entry_list.begin(), + old_fat_flow_entry_list.end(), new_fat_flow_entry_list.begin(), + new_fat_flow_entry_list.end())) { + ret = true; + } + InstanceIpSet &old_ipv4_list = instance_ipv4_list_.list_; InstanceIpSet new_ipv4_list = data->instance_ipv4_list_.list_; //Native ip of instance should be advertised even if @@ -2916,6 +2949,16 @@ void VmInterface::DeleteSecurityGroup() { } } +void VmInterface::DeleteFatFlow() { + FatFlowEntrySet::iterator it = fat_flow_list_.list_.begin(); + while (it != fat_flow_list_.list_.end()) { + FatFlowEntrySet::iterator prev = it++; + if (prev->del_pending_) { + fat_flow_list_.list_.erase(prev); + } + } +} + void VmInterface::UpdateL2TunnelId(bool force_update, bool policy_change) { AllocL2MplsLabel(force_update, policy_change); } @@ -3298,6 +3341,18 @@ void VmInterface::InstanceIpList::Remove(InstanceIpSet::iterator &it) { it->set_del_pending(true); } +void VmInterface::FatFlowList::Insert(const FatFlowEntry *rhs) { + list_.insert(*rhs); +} + +void VmInterface::FatFlowList::Update(const FatFlowEntry *lhs, + const FatFlowEntry *rhs) { +} + +void VmInterface::FatFlowList::Remove(FatFlowEntrySet::iterator &it) { + it->set_del_pending(true); +} + ///////////////////////////////////////////////////////////////////////////// // FloatingIp routines ///////////////////////////////////////////////////////////////////////////// diff --git a/src/vnsw/agent/oper/vm_interface.h b/src/vnsw/agent/oper/vm_interface.h index 677eb363900..851037d9b4d 100644 --- a/src/vnsw/agent/oper/vm_interface.h +++ b/src/vnsw/agent/oper/vm_interface.h @@ -354,6 +354,43 @@ class VmInterface : public Interface { InstanceIpSet list_; }; + struct FatFlowEntry : ListEntry { + FatFlowEntry(): protocol(0), port(0) {} + FatFlowEntry(const FatFlowEntry &rhs): + protocol(rhs.protocol), port(rhs.port) {} + FatFlowEntry(const uint8_t proto, const uint16_t p): + protocol(proto), port(p) {} + virtual ~FatFlowEntry(){} + bool operator == (const FatFlowEntry &rhs) const { + return (rhs.protocol == protocol && rhs.port == port); + } + + bool operator() (const FatFlowEntry &lhs, + const FatFlowEntry &rhs) const { + return lhs.IsLess(&rhs); + } + + bool IsLess(const FatFlowEntry *rhs) const { + if (protocol != rhs->protocol) { + return protocol < rhs->protocol; + } + return port < rhs->port; + } + uint8_t protocol; + uint16_t port; + }; + typedef std::set FatFlowEntrySet; + + struct FatFlowList { + FatFlowList(): list_() {} + ~FatFlowList() {}; + void Insert(const FatFlowEntry *rhs); + void Update(const FatFlowEntry *lhs, const FatFlowEntry *rhs); + void Remove(FatFlowEntrySet::iterator &it); + + FatFlowEntrySet list_; + }; + enum Trace { ADD, DELETE, @@ -471,6 +508,11 @@ class VmInterface : public Interface { return instance_ipv6_list_; } + const FatFlowList &fat_flow_list() const { + return fat_flow_list_; + } + + bool IsFatFlow(uint8_t protocol, uint16_t port) const; void set_vxlan_id(int vxlan_id) { vxlan_id_ = vxlan_id; } void set_subnet_bcast_addr(const Ip4Address &addr) { subnet_bcast_addr_ = addr; @@ -685,6 +727,7 @@ class VmInterface : public Interface { void DeleteAllowedAddressPair(bool l2); void UpdateSecurityGroup(); void DeleteSecurityGroup(); + void DeleteFatFlow(); void UpdateL2TunnelId(bool force_update, bool policy_change); void DeleteL2TunnelId(); void DeleteL2InterfaceRoute(bool old_l2_active, VrfEntry *old_vrf, @@ -754,6 +797,7 @@ class VmInterface : public Interface { AllowedAddressPairList allowed_address_pair_list_; InstanceIpList instance_ipv4_list_; InstanceIpList instance_ipv6_list_; + FatFlowList fat_flow_list_; // Peer for interface routes std::auto_ptr peer_; @@ -914,6 +958,7 @@ struct VmInterfaceConfigData : public VmInterfaceData { VmInterface::AllowedAddressPairList allowed_address_pair_list_; VmInterface::InstanceIpList instance_ipv4_list_; VmInterface::InstanceIpList instance_ipv6_list_; + VmInterface::FatFlowList fat_flow_list_; VmInterface::DeviceType device_type_; VmInterface::VmiType vmi_type_; // Parent physical-interface. Used in VMWare/ ToR logical-interface diff --git a/src/vnsw/agent/pkt/pkt_flow_info.cc b/src/vnsw/agent/pkt/pkt_flow_info.cc index 3d98a3d4f4e..41348bb8ccd 100644 --- a/src/vnsw/agent/pkt/pkt_flow_info.cc +++ b/src/vnsw/agent/pkt/pkt_flow_info.cc @@ -1018,6 +1018,7 @@ void PktFlowInfo::IngressProcess(const PktInfo *pkt, PktControlInfo *in, return; } + CalculatePort(pkt, in->intf_); // We always expect route for source-ip for ingress flows. // If route not present, return from here so that a short flow is added UpdateRoute(&in->rt_, in->vrf_, pkt->ip_saddr, pkt->smac, @@ -1150,6 +1151,7 @@ void PktFlowInfo::EgressProcess(const PktInfo *pkt, PktControlInfo *in, } if (out->intf_ && out->intf_->type() == Interface::VM_INTERFACE) { + CalculatePort(pkt, out->intf_); const VmInterface *vm_intf = static_cast(out->intf_); if (vm_intf->IsFloatingIp(pkt->ip_daddr)) { pkt->l3_forwarding = true; @@ -1338,11 +1340,57 @@ bool PktFlowInfo::Process(const PktInfo *pkt, PktControlInfo *in, return true; } +//Mask source port or destination port if a port has fat flow +//configuration. If both source port and destination port are +//present in configuration then the lowest of the port takes +//priority +void PktFlowInfo::CalculatePort(const PktInfo *cpkt, const Interface *in) { + const VmInterface *intf = NULL; + PktInfo *pkt = const_cast(cpkt); + + if (in == NULL || in->type() != Interface::VM_INTERFACE) { + return; + } + + intf = static_cast(in); + if (intf->fat_flow_list().list_.size() == 0) { + return; + } + + uint16_t sport = pkt->sport; + if (pkt->ip_proto == IPPROTO_ICMP) { + sport = 0; + } + if (pkt->sport < pkt->dport) { + if (intf->IsFatFlow(pkt->ip_proto, sport)) { + pkt->dport = 0; + return; + } + + if (intf->IsFatFlow(pkt->ip_proto, pkt->dport)) { + pkt->sport = 0; + return; + } + return; + } + + if (intf->IsFatFlow(pkt->ip_proto, pkt->dport)) { + pkt->sport = 0; + return; + } + + if (intf->IsFatFlow(pkt->ip_proto, sport)) { + pkt->dport = 0; + return; + } +} + void PktFlowInfo::Add(const PktInfo *pkt, PktControlInfo *in, PktControlInfo *out) { FlowKey key(in->nh_, pkt->ip_saddr, pkt->ip_daddr, pkt->ip_proto, pkt->sport, pkt->dport); FlowEntryPtr flow; + if (pkt->type != PktType::MESSAGE) { flow = Agent::GetInstance()->pkt()->flow_table()->Allocate(key); } else { diff --git a/src/vnsw/agent/pkt/pkt_flow_info.h b/src/vnsw/agent/pkt/pkt_flow_info.h index 284da953a96..18c4acd3426 100644 --- a/src/vnsw/agent/pkt/pkt_flow_info.h +++ b/src/vnsw/agent/pkt/pkt_flow_info.h @@ -90,13 +90,12 @@ class PktFlowInfo { bool UnknownUnicastFlow(const PktInfo *p, const PktControlInfo *in_info, const PktControlInfo *out_info); - public: void UpdateRoute(const AgentRoute **rt, const VrfEntry *vrf, const IpAddress &addr, const MacAddress &mac, FlowRouteRefMap &ref_map); uint8_t RouteToPrefixLen(const AgentRoute *route); - + void CalculatePort(const PktInfo *p, const Interface *intf); bool l3_flow; Address::Family family; boost::shared_ptr pkt; diff --git a/src/vnsw/agent/pkt/test/egress-flow.xml b/src/vnsw/agent/pkt/test/egress-flow.xml index ecec00ece97..555b54fba0e 100644 --- a/src/vnsw/agent/pkt/test/egress-flow.xml +++ b/src/vnsw/agent/pkt/test/egress-flow.xml @@ -84,6 +84,46 @@ dvn="vn1" action="pass"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /> diff --git a/src/vnsw/agent/test-xml/test_xml_oper.cc b/src/vnsw/agent/test-xml/test_xml_oper.cc index 02cfebc0373..55b386ca84d 100644 --- a/src/vnsw/agent/test-xml/test_xml_oper.cc +++ b/src/vnsw/agent/test-xml/test_xml_oper.cc @@ -336,6 +336,16 @@ bool AgentUtXmlVmInterface::ReadXml() { vlan_tag_ = 0; GetUintAttribute(node(), "vlan-tag", &vlan_tag_); GetStringAttribute(node(), "parent-vmi", &parent_vmi_); + + xml_attribute attr = node().attribute("fat-flow"); + for (;attr != xml_attribute(); attr = attr.next_attribute()) { + if (std::string(attr.name()) == "fat-flow") { + uint32_t value = attr.as_uint(); + fat_flow_port_.push_back(value); + } else { + break; + } + } return true; } @@ -354,6 +364,16 @@ bool AgentUtXmlVmInterface::ToXml(xml_node *parent) { sprintf(buff, "%d", vlan_tag_); AddXmlNodeWithValue(&n1, "sub-interface-vlan-tag", buff); } + + n1 = n.append_child("virtual-machine-interface-fat-flow-protocols"); + std::vector::const_iterator it = fat_flow_port_.begin(); + for(;it != fat_flow_port_.end(); it++) { + xml_node n2 = n1.append_child("fat-flow-protocol"); + AddXmlNodeWithValue(&n2, "protocol", "UDP"); + stringstream s; + s << *it; + AddXmlNodeWithValue(&n2, "port", s.str()); + } } if (add_nova_) { @@ -1048,6 +1068,7 @@ bool AgentUtXmlVmInterfaceValidate::ReadXml() { if (id) { vn_uuid_ = MakeUuid(id); } + return true; } diff --git a/src/vnsw/agent/test-xml/test_xml_oper.h b/src/vnsw/agent/test-xml/test_xml_oper.h index 5c0dccde4bf..3d8e46cb3f8 100644 --- a/src/vnsw/agent/test-xml/test_xml_oper.h +++ b/src/vnsw/agent/test-xml/test_xml_oper.h @@ -91,6 +91,7 @@ class AgentUtXmlVmInterface : public AgentUtXmlConfig { std::string ip_; bool add_nova_; uint16_t vlan_tag_; + std::vector fat_flow_port_; std::string parent_vmi_; }; @@ -316,6 +317,8 @@ class AgentUtXmlVmInterfaceValidate : public AgentUtXmlValidationNode { std::string device_type_; std::string vmi_type_; uint16_t vlan_tag_; + std::vector fat_flow_port_; + boost::uuids::uuid vn_uuid_; }; diff --git a/src/vnsw/agent/vrouter/ksync/interface_ksync.cc b/src/vnsw/agent/vrouter/ksync/interface_ksync.cc index c145d7cd8bc..63cb872a32e 100644 --- a/src/vnsw/agent/vrouter/ksync/interface_ksync.cc +++ b/src/vnsw/agent/vrouter/ksync/interface_ksync.cc @@ -340,6 +340,12 @@ bool InterfaceKSyncEntry::Sync(DBEntry *e) { switch (intf->type()) { case Interface::VM_INTERFACE: + { const VmInterface *vm_intf = static_cast(intf); + if (fat_flow_list_.list_ != vm_intf->fat_flow_list().list_) { + fat_flow_list_ = vm_intf->fat_flow_list(); + ret = true; + } + } case Interface::PACKET: dmac = table->agent()->vrrp_mac(); break; diff --git a/src/vnsw/agent/vrouter/ksync/interface_ksync.h b/src/vnsw/agent/vrouter/ksync/interface_ksync.h index 3e4bebab522..1c71bad3f88 100644 --- a/src/vnsw/agent/vrouter/ksync/interface_ksync.h +++ b/src/vnsw/agent/vrouter/ksync/interface_ksync.h @@ -108,6 +108,7 @@ class InterfaceKSyncEntry : public KSyncNetlinkDBEntry { std::string display_name_; Interface::Transport transport_; bool flood_unknown_unicast_; + VmInterface::FatFlowList fat_flow_list_; DISALLOW_COPY_AND_ASSIGN(InterfaceKSyncEntry); };