diff --git a/.gitignore b/.gitignore index 44f8eeab5c..2065bc635a 100644 --- a/.gitignore +++ b/.gitignore @@ -54,8 +54,10 @@ cfgmgr/vlanmgrd cfgmgr/vrfmgrd cfgmgr/nbrmgrd cfgmgr/vxlanmgrd +cfgmgr/natmgrd neighsyncd/neighsyncd portsyncd/portsyncd +natsyncd/natsyncd orchagent/orchagent orchagent/routeresync orchagent/orchagent_restart_check diff --git a/Makefile.am b/Makefile.am index 9f31144e40..4f1572daeb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = fpmsyncd neighsyncd portsyncd orchagent swssconfig cfgmgr tests +SUBDIRS = fpmsyncd neighsyncd portsyncd natsyncd orchagent swssconfig cfgmgr tests if HAVE_LIBTEAM SUBDIRS += teamsyncd diff --git a/README.md b/README.md index a484d1a5a5..3d1ac2fc05 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -Broadcom[![Broadcom](https://sonic-jenkins.westus2.cloudapp.azure.com/job/broadcom/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus2.cloudapp.azure.com/job/broadcom/job/sonic-swss-build/) -Mellanox[![Mellanox](https://sonic-jenkins.westus2.cloudapp.azure.com/job/mellanox/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus2.cloudapp.azure.com/job/mellanox/job/sonic-swss-build/) VS[![VS](https://sonic-jenkins.westus2.cloudapp.azure.com/job/vs/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus2.cloudapp.azure.com/job/vs/job/sonic-swss-build/) # SONiC - SWitch State Service - SWSS diff --git a/cfgmgr/Makefile.am b/cfgmgr/Makefile.am index c1e3f06996..7178cad1c6 100644 --- a/cfgmgr/Makefile.am +++ b/cfgmgr/Makefile.am @@ -1,9 +1,9 @@ -INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/orchagent -I $(top_srcdir)/warmrestart +INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/orchagent -I $(top_srcdir)/warmrestart -I $(top_srcdir)/orchagent/flex_counter CFLAGS_SAI = -I /usr/include/sai LIBNL_CFLAGS = -I/usr/include/libnl3 LIBNL_LIBS = -lnl-genl-3 -lnl-route-3 -lnl-3 -bin_PROGRAMS = vlanmgrd teammgrd portmgrd intfmgrd buffermgrd vrfmgrd nbrmgrd vxlanmgrd sflowmgrd +bin_PROGRAMS = vlanmgrd teammgrd portmgrd intfmgrd buffermgrd vrfmgrd nbrmgrd vxlanmgrd sflowmgrd natmgrd if DEBUG DBGFLAGS = -ggdb -DDEBUG @@ -55,3 +55,8 @@ sflowmgrd_SOURCES = sflowmgrd.cpp sflowmgr.cpp $(top_srcdir)/orchagent/orch.cpp sflowmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) sflowmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) sflowmgrd_LDADD = -lswsscommon + +natmgrd_SOURCES = natmgrd.cpp natmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h +natmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) +natmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) +natmgrd_LDADD = -lswsscommon diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 3d1915c1b8..7997f2e19b 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -7,6 +7,7 @@ #include "intfmgr.h" #include "exec.h" #include "shellcmd.h" +#include "warm_restart.h" using namespace std; using namespace swss; @@ -29,6 +30,10 @@ IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, c m_stateIntfTable(stateDb, STATE_INTERFACE_TABLE_NAME), m_appIntfTableProducer(appDb, APP_INTF_TABLE_NAME) { + if (!WarmStart::isWarmStart()) + { + flushLoopbackIntfs(); + } } void IntfMgr::setIntfIp(const string &alias, const string &opCmd, @@ -107,6 +112,28 @@ void IntfMgr::delLoopbackIntf(const string &alias) } } +void IntfMgr::flushLoopbackIntfs() +{ + stringstream cmd; + string res; + + cmd << IP_CMD << " link show type dummy | grep -o '" << LOOPBACK_PREFIX << "[^:]*'"; + + int ret = swss::exec(cmd.str(), res); + if (ret) + { + SWSS_LOG_DEBUG("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + return; + } + + auto aliases = tokenize(res, '\n'); + for (string &alias : aliases) + { + SWSS_LOG_NOTICE("Remove loopback device %s", alias.c_str()); + delLoopbackIntf(alias); + } +} + int IntfMgr::getIntfIpCount(const string &alias) { stringstream cmd; @@ -390,6 +417,7 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, string vrf_name = ""; string mtu = ""; string adminStatus = ""; + string nat_zone = ""; for (auto idx : data) { const auto &field = fvField(idx); @@ -404,6 +432,11 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, { adminStatus = value; } + + if (field == "nat_zone") + { + nat_zone = value; + } } if (op == SET_COMMAND) @@ -431,6 +464,15 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, { addLoopbackIntf(alias); } + else + { + /* Set nat zone */ + if (!nat_zone.empty()) + { + FieldValueTuple fvTuple("nat_zone", nat_zone); + data.push_back(fvTuple); + } + } if (!vrf_name.empty()) { diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index 7aab9fe447..73448b74c9 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -34,6 +34,7 @@ class IntfMgr : public Orch int getIntfIpCount(const std::string &alias); void addLoopbackIntf(const std::string &alias); void delLoopbackIntf(const std::string &alias); + void flushLoopbackIntfs(void); void addHostSubIntf(const std::string&intf, const std::string &subIntf, const std::string &vlan); void setHostSubIntfMtu(const std::string &subIntf, const std::string &mtu); diff --git a/cfgmgr/intfmgrd.cpp b/cfgmgr/intfmgrd.cpp index d184b66e4a..d92aff9ceb 100644 --- a/cfgmgr/intfmgrd.cpp +++ b/cfgmgr/intfmgrd.cpp @@ -8,6 +8,7 @@ #include "intfmgr.h" #include #include +#include "warm_restart.h" using namespace std; using namespace swss; @@ -52,6 +53,9 @@ int main(int argc, char **argv) DBConnector appDb("APPL_DB", 0); DBConnector stateDb("STATE_DB", 0); + WarmStart::initialize("intfmgrd", "swss"); + WarmStart::checkWarmStart("intfmgrd", "swss"); + IntfMgr intfmgr(&cfgDb, &appDb, &stateDb, cfg_intf_tables); // TODO: add tables in stateDB which interface depends on to monitor list diff --git a/cfgmgr/natmgr.cpp b/cfgmgr/natmgr.cpp new file mode 100644 index 0000000000..5df7a75084 --- /dev/null +++ b/cfgmgr/natmgr.cpp @@ -0,0 +1,7404 @@ +/* + * Copyright 2019 Broadcom Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "logger.h" +#include "producerstatetable.h" +#include "macaddress.h" +#include "natmgr.h" +#include "exec.h" +#include "tokenize.h" +#include "converter.h" +#include "shellcmd.h" +#include "warm_restart.h" +#include "ipaddress.h" +#include "ipprefix.h" + +using namespace std; +using namespace swss; + +/* NatMgr Constructor */ +NatMgr::NatMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames) : + Orch(cfgDb, tableNames), + m_statePortTable(stateDb, STATE_PORT_TABLE_NAME), + m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME), + m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME), + m_stateInterfaceTable(stateDb, STATE_INTERFACE_TABLE_NAME), + m_appNatTableProducer(appDb, APP_NAT_TABLE_NAME), + m_appNaptTableProducer(appDb, APP_NAPT_TABLE_NAME), + m_appTwiceNatTableProducer(appDb, APP_NAT_TWICE_TABLE_NAME), + m_appTwiceNaptTableProducer(appDb, APP_NAPT_TWICE_TABLE_NAME), + m_appNatGlobalTableProducer(appDb, APP_NAT_GLOBAL_TABLE_NAME), + m_appNaptPoolIpTable(appDb, APP_NAPT_POOL_IP_TABLE_NAME) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_INFO("NatMgr Constructor ..!"); + + /* Set the Admin mode to disabled */ + natAdminMode = DISABLED; + + /* Set NAT default timeout as 600 seconds */ + m_natTimeout = NAT_TIMEOUT_DEFAULT; + + /* Set NAT default tcp timeout as 86400 seconds (1 Day) */ + m_natTcpTimeout = NAT_TCP_TIMEOUT_DEFAULT; + + /* Set NAT default udp timeout as 300 seconds */ + m_natUdpTimeout = NAT_UDP_TIMEOUT_DEFAULT; + + /* Clean the NAT iptables */ + std::string res; + const std::string cmds = std::string("") + IPTABLES_CMD + " -F -t nat "; + if (swss::exec(cmds, res)) + { + SWSS_LOG_ERROR("Command '%s' failed", cmds.c_str()); + } + + flushNotifier = std::make_shared(appDb, "FLUSHNATREQUEST"); +} + +/* To check the port init id done or not */ +bool NatMgr::isPortInitDone(DBConnector *app_db) +{ + bool portInit = 0; + long cnt = 0; + + while(!portInit) { + Table portTable(app_db, APP_PORT_TABLE_NAME); + std::vector tuples; + portInit = portTable.get("PortInitDone", tuples); + + if(portInit) + break; + sleep(1); + cnt++; + } + SWSS_LOG_NOTICE("PORT_INIT_DONE : %d %ld", portInit, cnt); + return portInit; +} + +/* To check the given port is State Ok or not */ +bool NatMgr::isPortStateOk(const string &port) +{ + vector temp; + + if (!port.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX)) + { + if (m_stateVlanTable.get(port, temp)) + { + SWSS_LOG_INFO("Vlan %s is ready", port.c_str()); + return true; + } + SWSS_LOG_INFO("Vlan %s is not yet ready", port.c_str()); + } + else if (!port.compare(0, strlen(LAG_PREFIX), LAG_PREFIX)) + { + if (m_stateLagTable.get(port, temp)) + { + SWSS_LOG_INFO("Lag %s is ready", port.c_str()); + return true; + } + SWSS_LOG_INFO("Lag %s is not yet ready", port.c_str()); + } + else if (!port.compare(0, strlen(ETHERNET_PREFIX), ETHERNET_PREFIX)) + { + if (m_statePortTable.get(port, temp)) + { + SWSS_LOG_INFO("Port %s is ready", port.c_str()); + return true; + } + SWSS_LOG_INFO("Port %s is not yet ready", port.c_str()); + } + else + { + SWSS_LOG_ERROR("Invalid Port %s ", port.c_str()); + } + return false; +} + +/* To check the give interface is State Ok or not */ +bool NatMgr::isIntfStateOk(const string &interface) +{ + vector temp; + + if (m_stateInterfaceTable.get(interface, temp)) + { + SWSS_LOG_INFO("Interface %s is ready", interface.c_str()); + return true; + } + + SWSS_LOG_INFO("Interface %s is not yet ready", interface.c_str()); + return false; +} + +/* To check the nat fetaure is enabled or not */ +bool NatMgr::isNatEnabled(void) +{ + if (natAdminMode == ENABLED) + { + return true; + } + + return false; +} + +/* To check the give global_ip is withing the Prefix subnet or not */ +bool NatMgr::isGlobalIpMatching(const string &prefix, const string &global_ip) +{ + IpAddress externalAddr(global_ip); + IpPrefix ip_prefix(prefix); + + auto ea = externalAddr.getIp(); + auto ia = ip_prefix.getIp(); + auto ia2 = ia.getIp(); + auto ma = ip_prefix.getMask(); + auto ma2 = ma.getIp(); + + /* Check global ip is within the given subnet */ + if ((ia2.ip_addr.ipv4_addr & ma2.ip_addr.ipv4_addr) == (ea.ip_addr.ipv4_addr & ma2.ip_addr.ipv4_addr)) + { + return true; + } + + return false; +} + +/* To check the given pool_name is mapped to any binding or not */ +bool NatMgr::isPoolMappedtoBinding(const string &pool_name, string &binding_name) +{ + /* Get all binding Info */ + for (auto it = m_natBindingInfo.begin(); it != m_natBindingInfo.end(); it++) + { + /* Check is it matches with given pool name */ + if (pool_name == (*it).second.pool_name) + { + /* Pool is mapped to Binding, return now */ + binding_name = (*it).first; + return true; + } + } + + return false; +} + +/* To check the given Static NAT entry is matched with any Static NAPT entry */ +bool NatMgr::isMatchesWithStaticNapt(const string &global_ip, string &local_ip) +{ + /* Get all Static NAPT entries */ + for (auto it = m_staticNaptEntry.begin(); it != m_staticNaptEntry.end(); it++) + { + vector keys = tokenize((*it).first, config_db_key_delimiter); + + /* Ensure that interface is having some port, otherwise Entry is not yet added */ + if ((keys[0] == global_ip) && ((*it).second.local_ip == local_ip) && + ((*it).second.interface != NONE_STRING)) + { + /* Matches with the Static NAPT entry */ + return true; + } + } + return false; +} + +/* To check the given Static NAPT entry is matched with any Static NAT entry */ +bool NatMgr::isMatchesWithStaticNat(const string &global_ip, string &local_ip) +{ + /* Get all Static NAT entries */ + for (auto it = m_staticNatEntry.begin(); it != m_staticNatEntry.end(); it++) + { + /* Ensure that interface is having some port, otherwise Entry is not yet added */ + if (((*it).first == global_ip) && ((*it).second.local_ip == local_ip) && + ((*it).second.interface != NONE_STRING)) + { + /* Matches with the Static NAT entry */ + return true; + } + } + return false; +} + +/* To get the configured interface for the given global ip address */ +bool NatMgr::getIpEnabledIntf(const string &global_ip, string &interface) +{ + /* Get all Ports from Ip Interface Info */ + for (auto it = m_natIpInterfaceInfo.begin(); it != m_natIpInterfaceInfo.end(); it++) + { + /* Get all IpAddress from Ip Interface Values from the key (Port) */ + for (auto ipPrefix = m_natIpInterfaceInfo[(*it).first].begin(); ipPrefix != m_natIpInterfaceInfo[(*it).first].end(); ipPrefix++) + { + /* Check the global ip address is in subnet */ + if (isGlobalIpMatching(*ipPrefix, global_ip) == true) + { + /* Matched with this interface, return now */ + interface = (*it).first; + return true; + } + } + } + + return false; +} + +/* This is ideally called on docker stop */ +void NatMgr::cleanupPoolIpTable(void) +{ + SWSS_LOG_INFO("Cleaning the NAPT Pool IP table from APP_DB"); + for (auto it = m_natPoolInfo.begin(); it != m_natPoolInfo.end(); it++) + { + /* Delete pool ip from APPL_DB */ + setNaptPoolIpTable(DELETE, ((*it).second).ip_range, ((*it).second).port_range); + } +} + +/* This is ideally called on docker stop */ +void NatMgr::cleanupMangleIpTables(void) +{ + SWSS_LOG_INFO("Cleaning the Mangle IpTables"); + for (auto it = m_natZoneInterfaceInfo.begin(); it != m_natZoneInterfaceInfo.end(); it++) + { + /* Delete the mangle iptables rules for non-loopback interface */ + if (strncmp((*it).first.c_str(), LOOPBACK_PREFIX, strlen(LOOPBACK_PREFIX))) + { + setMangleIptablesRules(DELETE, (*it).first, (*it).second); + } + } +} + +/* To Add/Delete NAPT pool ip table to APPL_DB */ +void NatMgr::setNaptPoolIpTable(const string &opCmd, const string &ip_range, const string &port_range) +{ + uint32_t ipv4_addr_low, ipv4_addr_high, ip, setIp; + char ipAddr[INET_ADDRSTRLEN]; + std::vector values; + + if (!port_range.empty() and (port_range != "NULL")) + { + swss::FieldValueTuple p("port_range", port_range); + values.push_back(p); + vector nat_ip = tokenize(ip_range, range_specifier); + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool is not valid"); + return; + } + else if (nat_ip.size() == 2) + { + inet_pton(AF_INET, nat_ip[1].c_str(), &ipv4_addr_high); + ipv4_addr_high = ntohl(ipv4_addr_high); + inet_pton(AF_INET, nat_ip[0].c_str(), &ipv4_addr_low); + ipv4_addr_low = ntohl(ipv4_addr_low); + } + else + { + inet_pton(AF_INET, nat_ip[0].c_str(), &ipv4_addr_low); + ipv4_addr_high = ntohl(ipv4_addr_low); + ipv4_addr_low = ntohl(ipv4_addr_low); + } + + for (ip = ipv4_addr_low; ip <= ipv4_addr_high; ip++) + { + setIp = htonl(ip); + inet_ntop(AF_INET, &setIp, ipAddr, INET_ADDRSTRLEN); + if (opCmd == ADD) + { + m_appNaptPoolIpTable.set(ipAddr, values); + } + else + { + m_appNaptPoolIpTable.del(ipAddr); + } + } + } +} + +/* To Add a dummy conntrack entry for the Static Single NAT entry in the kernel */ +void NatMgr::addConntrackSingleNatEntry(const string &key) +{ + std::string res, cmds = std::string("") + CONNTRACK_CMD; + + if (m_staticNatEntry[key].nat_type == DNAT_NAT_TYPE) + { + SWSS_LOG_INFO("Add static NAT conntrack entry with src-ip %s, timeout %d", + m_staticNatEntry[key].local_ip.c_str(), m_natTimeout); + + cmds += (" -I -n " + key + ":1 -g 127.0.0.1:127" + " -p udp -t " + to_string(m_natTimeout) + + " --src " + m_staticNatEntry[key].local_ip + " --sport 1 --dst 127.0.0.1 --dport 127 -u ASSURED "); + } + else if (m_staticNatEntry[key].nat_type == SNAT_NAT_TYPE) + { + SWSS_LOG_INFO("Add static NAT conntrack entry with src-ip %s, timeout %d", + key.c_str(), m_natTimeout); + + cmds += (" -I -n " + m_staticNatEntry[key].local_ip + ":1 -g 127.0.0.1:127" + " -p udp -t " + to_string(m_natTimeout) + + " --src " + key + " --sport 1 --dst 127.0.0.1 --dport 127 -u ASSURED "); + } + + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Added the static NAT conntrack entry"); + } +} + +/* To Add a dummy conntrack entry for the Static Twice NAT entry in the kernel */ +void NatMgr::addConntrackTwiceNatEntry(const string &snatKey, const string &dnatKey) +{ + std::string res, cmds = std::string("") + CONNTRACK_CMD; + + SWSS_LOG_INFO("Add static Twice NAT conntrack entry with src-ip %s, dst-ip %s, timeout %u", + snatKey.c_str(), dnatKey.c_str(), m_natTimeout); + + cmds += (" -I -n " + m_staticNatEntry[snatKey].local_ip + ":1" + " -g " + m_staticNatEntry[dnatKey].local_ip + ":1" + + " -p udp" + " -t " + to_string(m_natTimeout) + " --src " + snatKey + " --sport 1" + " --dst " + dnatKey + + " --dport 1" + " -u ASSURED "); + + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Added the static Twice NAT conntrack entry"); + } +} + +/* To Add a dummy conntrack entry for the Static NAPT entry in the kernel, + * so that the port number is reserved and the same port is not allocated by the stack for any other dynamic entry */ +void NatMgr::addConntrackSingleNaptEntry(const string &key) +{ + int timeout = 0; + std::string res, prototype, state, cmds = std::string("") + CONNTRACK_CMD; + vector keys = tokenize(key, config_db_key_delimiter); + + if (keys[1] == to_upper(IP_PROTOCOL_UDP)) + { + prototype = IP_PROTOCOL_UDP; + timeout = m_natUdpTimeout; + state = ""; + } + else if (keys[1] == to_upper(IP_PROTOCOL_TCP)) + { + prototype = IP_PROTOCOL_TCP; + timeout = m_natTcpTimeout; + state = " --state ESTABLISHED "; + } + + if (m_staticNaptEntry[key].nat_type == DNAT_NAT_TYPE) + { + + SWSS_LOG_INFO("Add static NAPT conntrack entry with protocol %s, src-ip %s, src-port %s, timeout %d", + prototype.c_str(), m_staticNaptEntry[key].local_ip.c_str(), m_staticNaptEntry[key].local_port.c_str(), timeout); + + cmds += (" -I -n " + keys[0] + ":" + keys[2] + " -g 127.0.0.1:127" + " -p " + prototype + " -t " + to_string(timeout) + + " --src " + m_staticNaptEntry[key].local_ip + " --sport " + m_staticNaptEntry[key].local_port + " --dst 127.0.0.1 --dport 127 -u ASSURED " + state); + } + else if (m_staticNaptEntry[key].nat_type == SNAT_NAT_TYPE) + { + SWSS_LOG_INFO("Add static NAPT conntrack entry with protocol %s, src-ip %s, src-port %s, timeout %d", + prototype.c_str(), keys[0].c_str(), keys[2].c_str(), timeout); + + cmds += (" -I -n " + m_staticNaptEntry[key].local_ip + ":" + m_staticNaptEntry[key].local_port + " -g 127.0.0.1:127" + " -p " + prototype + " -t " + to_string(timeout) + + " --src " + keys[0] + " --sport " + keys[2] + " --dst 127.0.0.1 --dport 127 -u ASSURED " + state); + } + + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Added the static NAPT conntrack entry"); + } +} + +/* To Add a dummy conntrack entry for the Static Twice NAPT entry in the kernel */ +void NatMgr::addConntrackTwiceNaptEntry(const string &snatKey, const string &dnatKey) +{ + int timeout = 0; + std::string res, prototype, state, cmds = std::string("") + CONNTRACK_CMD; + vector snatKeys = tokenize(snatKey, config_db_key_delimiter); + vector dnatKeys = tokenize(dnatKey, config_db_key_delimiter); + + if (snatKeys[1] == to_upper(IP_PROTOCOL_UDP)) + { + prototype = IP_PROTOCOL_UDP; + timeout = m_natUdpTimeout; + state = ""; + } + else if (snatKeys[1] == to_upper(IP_PROTOCOL_TCP)) + { + prototype = IP_PROTOCOL_TCP; + timeout = m_natTcpTimeout; + state = " --state ESTABLISHED "; + } + + SWSS_LOG_DEBUG("Add static Twice NAPT conntrack entry with protocol %s, src-ip %s, src-port %s, dst-ip %s, dst-port %s, timeout %u", + prototype.c_str(), snatKeys[0].c_str(), snatKeys[2].c_str(), dnatKeys[0].c_str(), dnatKeys[2].c_str(), timeout); + + cmds += (" -I -n " + m_staticNaptEntry[snatKey].local_ip + ":" + m_staticNaptEntry[snatKey].local_port + " -g " + m_staticNaptEntry[dnatKey].local_ip + ":" + + m_staticNaptEntry[dnatKey].local_port + " -p " + prototype + " -t " + to_string(timeout) + + " --src " + snatKeys[0] + " --sport " + snatKeys[2] + " --dst " + dnatKeys[0] + " --dport " + dnatKeys[2] + " -u ASSURED " + state); + + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Added the static Twice NAPT conntrack entry"); + } +} + +/* To Delete conntrack entry for Static Single NAT entry */ +void NatMgr::deleteConntrackSingleNatEntry(const string &key) +{ + std::string res, cmds = std::string("") + CONNTRACK_CMD; + + if (m_staticNatEntry[key].nat_type == DNAT_NAT_TYPE) + { + SWSS_LOG_INFO("Delete static NAT conntrack entry with src-ip %s", m_staticNatEntry[key].local_ip.c_str()); + + cmds += (" -D -s " + m_staticNatEntry[key].local_ip + " -p udp" + " &> /dev/null"); + } + else if (m_staticNatEntry[key].nat_type == SNAT_NAT_TYPE) + { + SWSS_LOG_INFO("Delete static NAT conntrack entry with src-ip %s", key.c_str()); + + cmds += (" -D -s " + key + " -p udp" + " &> /dev/null"); + } + + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Deleted the Static NAT conntrack entry"); + } +} + +/* To Delete conntrack entry for Static Twice NAT entry */ +void NatMgr::deleteConntrackTwiceNatEntry(const string &snatKey, const string &dnatKey) +{ + std::string res, cmds = std::string("") + CONNTRACK_CMD; + + SWSS_LOG_INFO("Delete static Twice NAT conntrack entry with src-ip %s and dst-ip %s", snatKey.c_str(), dnatKey.c_str()); + + cmds += (" -D -s " + snatKey + " -d " + dnatKey + " &> /dev/null"); + + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Deleted the Static Twice NAT conntrack entry"); + } +} + +/* To Delete conntrack entry for Static Single NAPT entry */ +void NatMgr::deleteConntrackSingleNaptEntry(const string &key) +{ + std::string res, prototype, cmds = std::string("") + CONNTRACK_CMD; + vector keys = tokenize(key, config_db_key_delimiter); + + if (keys[1] == to_upper(IP_PROTOCOL_UDP)) + { + prototype = IP_PROTOCOL_UDP; + } + else if (keys[1] == to_upper(IP_PROTOCOL_TCP)) + { + prototype = IP_PROTOCOL_TCP; + } + + if (m_staticNaptEntry[key].nat_type == DNAT_NAT_TYPE) + { + SWSS_LOG_INFO("Delete static NAPT conntrack entry with protocol %s, src-ip %s, src-port %s", + prototype.c_str(), m_staticNaptEntry[key].local_ip.c_str(), m_staticNaptEntry[key].local_port.c_str()); + + cmds += (" -D -s " + m_staticNaptEntry[key].local_ip + " -p " + prototype + " --sport " + m_staticNaptEntry[key].local_port + " &> /dev/null"); + } + else if (m_staticNaptEntry[key].nat_type == SNAT_NAT_TYPE) + { + SWSS_LOG_INFO("Delete static NAPT conntrack entry with protocol %s, src-ip %s, src-port %s", + prototype.c_str(), keys[0].c_str(), keys[2].c_str()); + + cmds += (" -D -s " + keys[0] + " -p " + prototype + " --sport " + keys[2] + " &> /dev/null"); + } + + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Deleted the Static NAPT conntrack entry"); + } +} + +/* To Delete conntrack entry for Static Twice NAPT entry */ +void NatMgr::deleteConntrackTwiceNaptEntry(const string &snatKey, const string &dnatKey) +{ + std::string res, prototype, cmds = std::string("") + CONNTRACK_CMD; + vector snatKeys = tokenize(snatKey, config_db_key_delimiter); + vector dnatKeys = tokenize(dnatKey, config_db_key_delimiter); + + if (snatKeys[1] == to_upper(IP_PROTOCOL_UDP)) + { + prototype = IP_PROTOCOL_UDP; + } + else if (snatKeys[1] == to_upper(IP_PROTOCOL_TCP)) + { + prototype = IP_PROTOCOL_TCP; + } + + SWSS_LOG_INFO("Delete static Twice NAPT conntrack entry with protocol %s, src-ip %s, src-port %s, dst-ip %s, dst-port %s", + prototype.c_str(), snatKeys[0].c_str(), snatKeys[2].c_str(), dnatKeys[0].c_str(), dnatKeys[2].c_str()); + + cmds += (" -D -s " + snatKeys[0] + " -p " + prototype + " --orig-port-src " + snatKeys[2] + " -d " + dnatKeys[0] + " --orig-port-dst " + dnatKeys[2] + " &> /dev/null"); + + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Deleted the Static Twice NAPT conntrack entry"); + } +} + +/* To Delete conntrack entries for matching Pool ip address */ +void NatMgr::deleteConntrackDynamicEntries(const string &ip_range) +{ + std::string res, cmds; + + uint32_t ipv4_addr_low, ipv4_addr_high, ip, setIp; + char ipAddr[INET_ADDRSTRLEN]; + + vector nat_ip = tokenize(ip_range, range_specifier); + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool is not valid"); + return; + } + else if (nat_ip.size() == 2) + { + inet_pton(AF_INET, nat_ip[1].c_str(), &ipv4_addr_high); + ipv4_addr_high = ntohl(ipv4_addr_high); + inet_pton(AF_INET, nat_ip[0].c_str(), &ipv4_addr_low); + ipv4_addr_low = ntohl(ipv4_addr_low); + } + else + { + inet_pton(AF_INET, nat_ip[0].c_str(), &ipv4_addr_low); + ipv4_addr_high = ntohl(ipv4_addr_low); + ipv4_addr_low = ntohl(ipv4_addr_low); + } + + for (ip = ipv4_addr_low; ip <= ipv4_addr_high; ip++) + { + setIp = htonl(ip); + inet_ntop(AF_INET, &setIp, ipAddr, INET_ADDRSTRLEN); + std::string ipAddrString(ipAddr); + + SWSS_LOG_INFO("Delete dynamic conntrack entry with translated-src-ip %s", ipAddr); + + cmds = (std::string("") + CONNTRACK_CMD + " -D -q " + ipAddrString + " &> /dev/null"); + + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Deleted the dynamic conntrack entry"); + } + } +} + +/* Iptable rules are added in the mangles table, to support use of Loopback IP as NAT Public IP which is a typical use-case in DC scenarios. The way it works is that: + * + * * The mangle table rules are processed first before the nat table rules. + * * Assign mark field on the packet using the mangles rules in the PREROUTING (ingress) and POSTROUTING (egress) stages. + * * The mark field is derived from the configured zone (zone + 1). Using 'mark'value of 0 has issues as that is implicit value for any packet traversing the kernel. + * * Match against the 'mark' value in the nat rules happens after the 'mangle' rule sets it. + * * Since packet doesn't go out of a Loopback interface, we configure zone value on the public interfaces same as on the Loopback interface (whose IP is used as NAT public IP). + * * So matching against the zone value is done while allocating NAT IPs. + * * + * * */ +bool NatMgr::setMangleIptablesRules(const string &opCmd, const string &interface, const string &nat_zone) +{ + SWSS_LOG_ENTER(); + + /* The command should be generated as: + * iptables -t mangle -opCmd PREROUTING -i port -j MARK --set-mark nat_zone + * iptables -t mangle -opCmd POSTROUTING -o port -j MARK --set-mark nat_zone + */ + std::string res; + int ret; + + const std::string cmds = std::string("") + + IPTABLES_CMD + " -t mangle " + "-" + opCmd + " PREROUTING -i " + interface + " -j MARK --set-mark " + nat_zone + " && " + + IPTABLES_CMD + " -t mangle " + "-" + opCmd + " POSTROUTING -o " + interface + " -j MARK --set-mark " + nat_zone ; + + ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + return false; + } + + return true; +} + +/* To Add arbitrary value for DNAT rule incase of fullcone */ +bool NatMgr::setFullConeDnatIptablesRule(const string &opCmd) +{ + /* This rule in the PREROUTING chain should be the default rule at the end of the list + * iptables -t nat -[A/D] PREROUTING -j DNAT --fullcone + */ + std::string res; + int ret; + + /* In case of fullcone, the --to-destination is ignored by the stack, giving an aribitrary value so that + * iptables doesn't fail for PREROUTING/DNAT rule */ + const std::string cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " PREROUTING " + " -j DNAT --to-destination 1.1.1.1 --fullcone"; + + ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + return false; + } + return true; +} + +/* To Add or Delete the Iptables rules for Static NAT entry */ +bool NatMgr::setStaticNatIptablesRules(const string &opCmd, const string &interface, const string &external_ip, const string &internal_ip, const string &nat_type) +{ + SWSS_LOG_ENTER(); + + /* The command should be generated as: + * iptables -t nat -opCmd PREROUTING -m mark --mark zone-value -j DNAT -d external_ip --to-destination internal_ip + * iptables -t nat -opCmd POSTROUTING -m mark --mark zone-value -j SNAT -s internal_ip --to-source external_ip + */ + std::string res; + std::string markStr = std::string(""); + int ret; + + markStr = " -m mark --mark " + m_natZoneInterfaceInfo[interface]; + + if (nat_type == DNAT_NAT_TYPE) + { + const std::string cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " PREROUTING " + markStr + " -j DNAT -d " + external_ip + " --to-destination " + internal_ip + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING " + markStr + " -j SNAT -s " + internal_ip + " --to-source " + external_ip ; + + ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + return false; + } + } + else + { + const std::string cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " PREROUTING" + " -j DNAT -d " + internal_ip + " --to-destination " + external_ip + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING" + " -j SNAT -s " + external_ip + " --to-source " + internal_ip ; + + ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + return false; + } + } + + return true; +} + +/* To Add or Delete the Iptables rules for Static NAPT entry */ +bool NatMgr::setStaticNaptIptablesRules(const string &opCmd, const string &interface, const string &prototype, const string &external_ip, + const string &external_port, const string &internal_ip, const string &internal_port, const string &nat_type) +{ + SWSS_LOG_ENTER(); + + /* The command should be generated as: + * iptables -t nat -opCmd PREROUTING -m mark --mark zone-value -p prototype -j DNAT -d external_ip --dport external_port --to-destination internal_ip:internal_port + * iptables -t nat -opCmd POSTROUTING -m mark --mark zone-value -p prototype -j SNAT -s internal_ip --sport internal_port --to-source external_ip:external_port + */ + std::string res; + std::string markStr = std::string(""); + int ret; + + markStr = " -m mark --mark " + m_natZoneInterfaceInfo[interface]; + + if (nat_type == DNAT_NAT_TYPE) + { + const std::string cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " PREROUTING " + markStr + " -p " + prototype + " -j DNAT -d " + external_ip + " --dport " + external_port + " --to-destination " + + internal_ip + ":" + internal_port + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING " + markStr + " -p " + prototype + " -j SNAT -s " + internal_ip + " --sport " + internal_port + " --to-source " + + external_ip + ":" + external_port; + + ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + return false; + } + } + else + { + const std::string cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " PREROUTING" + " -p " + prototype + " -j DNAT -d " + internal_ip + " --dport " + internal_port + " --to-destination " + + external_ip + ":" + external_port + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING" + " -p " + prototype + " -j SNAT -s " + external_ip + " --sport " + external_port + " --to-source " + + internal_ip + ":" + internal_port; + + ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + return false; + } + } + + return true; +} + +/* To Add or Delete the Iptables rules for Static Twice NAT entry */ +bool NatMgr::setStaticTwiceNatIptablesRules(const string &opCmd, const string &interface, const string &src_ip, const string &translated_src_ip, + const string &dest_ip, const string &translated_dest_ip) +{ + SWSS_LOG_ENTER(); + + /* The command should be generated as: + * iptables -t nat -opCmd PREROUTING -j DNAT -d translated_src --to-destination src -s translated_dst + * iptables -t nat -opCmd PREROUTING -m mark --mark zone-value -j DNAT -d dst --to-destination translated_dst -s src + * + * iptables -t nat -opCmd POSTROUTING -j SNAT -s src --to-source translated_src -d translated_dst + * iptables -t nat -opCmd POSTROUTING -m mark --mark zone-value -j SNAT -s translated_dst --to-source dst -d src + */ + + std::string res; + std::string markStr = std::string(""); + int ret; + + markStr = " -m mark --mark " + m_natZoneInterfaceInfo[interface]; + + const std::string cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " PREROUTING -j DNAT -d " + translated_src_ip + + " --to-destination " + src_ip + " -s " + translated_dest_ip + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " PREROUTING " + markStr + " -j DNAT -d " + dest_ip + + " --to-destination " + translated_dest_ip + " -s " + src_ip + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -j SNAT -s " + src_ip + + " --to-source " + translated_src_ip + " -d " + translated_dest_ip + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING " + markStr + " -j SNAT -s " + translated_dest_ip + + " --to-source " + dest_ip + " -d " + src_ip; + + ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + return false; + } + + return true; +} + +/* To Add or Delete the Iptables rules for Static Twice NAPT entry */ +bool NatMgr::setStaticTwiceNaptIptablesRules(const string &opCmd, const string &interface, const string &prototype, const string &src_ip, const string &src_port, + const string &translated_src_ip, const string &translated_src_port, const string &dest_ip, const string &dest_port, + const string &translated_dest_ip, const string &translated_dest_port) +{ + SWSS_LOG_ENTER(); + + /* The command should be generated as: + * iptables -t nat -opCmd PREROUTING -j DNAT -p udp -d translated_src --dport translated_src_l4_port --to-destination src:src_l4_port + * -s translated_dst --sport translated_dst_l4_port + * iptables -t nat -opCmd PREROUTING -m mark --mark zone-value -j DNAT -p udp -d dst --dport dst_l4_port --to-destination translated_dst:translated_dst_l4_port + * -s src --sport src_l4_port + * + * iptables -t nat -opCmd POSTROUTING -j SNAT -p udp -s src --sport src_l4_port --to-source translated_src:translated_src_l4_port + * -d translated_dst --dport translated_dst_l4_port + * iptables -t nat -opCmd POSTROUTING -m mark --mark zone-value -j SNAT -p udp -s translated_dst --sport translated_dst_l4_port --to-source dst:dst_l4_port + * -d src --dport src_l4_port + */ + + std::string res; + std::string markStr = std::string(""); + int ret; + + markStr = " -m mark --mark " + m_natZoneInterfaceInfo[interface]; + + const std::string cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " PREROUTING -p " + prototype + " -j DNAT -d " + translated_src_ip + " --dport " + translated_src_port + + " --to-destination " + src_ip + ":" + src_port + " -s " + translated_dest_ip + " --sport " + translated_dest_port + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " PREROUTING " + markStr + " -p " + prototype + " -j DNAT -d " + dest_ip + " --dport " + dest_port + + " --to-destination " + translated_dest_ip + ":" + translated_dest_port + " -s " + src_ip + " --sport " + src_port + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p " + prototype + " -j SNAT -s " + src_ip + " --sport " + src_port + + " --to-source " + translated_src_ip + ":" + translated_src_port + " -d " + translated_dest_ip + " --dport " + translated_dest_port + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING " + markStr + " -p " + prototype + " -j SNAT -s " + translated_dest_ip + " --sport " + translated_dest_port + + " --to-source " + dest_ip + ":" + dest_port + " -d " + src_ip + " --dport " +src_port; + + ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + return false; + } + + return true; +} + +/* To Add or Delete the Iptables rules for Dynamic NAT/NAPT without ACLs */ +bool NatMgr::setDynamicNatIptablesRulesWithoutAcl(const string &opCmd, const string &interface, const string &external_ip, + const string &external_port_range, const string &key) +{ + SWSS_LOG_ENTER(); + + /* The command should be generated as: + * + * iptables -t nat -opCmd POSTROUTING -p tcp -j SNAT -m mark --mark zone-value --to-source external_ip:external_port_range --fullcone + * iptables -t nat -opCmd POSTROUTING -p udp -j SNAT -m mark --mark zone-value --to-source external_ip:external_port_range --fullcone + * iptables -t nat -opCmd POSTROUTING -p icmp -j SNAT -m mark --mark zone-value --to-source external_ip:external_port_range --fullcone + */ + std::string res, cmd; + std::string externalString = EMPTY_STRING; + std::string fullcone = EMPTY_STRING; + std::string prototype = EMPTY_STRING; + std::string cmds = std::string(""); + std::string markStr = std::string(""); + + markStr = " -m mark --mark " + m_natZoneInterfaceInfo[interface]; + + if (external_port_range.empty()) + { + externalString = external_ip; + } + else + { + externalString = external_ip + ":" + external_port_range; + fullcone = " --fullcone"; + } + + /* Static Key empty means Single NAT */ + if (key.empty()) + { + /* Rules for Single NAT */ + cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p tcp -j SNAT " + markStr + " --to-source " + + externalString + fullcone + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p udp -j SNAT " + markStr + " --to-source " + + externalString + fullcone + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p icmp -j SNAT " + markStr + " --to-source " + + externalString + fullcone; + } + else + { + if (opCmd == ADD) + { + cmd = INSERT; + } + else + { + cmd = opCmd; + } + + vector keys = tokenize(key, config_db_key_delimiter); + if (keys.size() > 1) + { + if (keys[1] == to_upper(IP_PROTOCOL_UDP)) + { + prototype = " -p udp "; + } + else if (keys[1] == to_upper(IP_PROTOCOL_TCP)) + { + prototype = " -p tcp "; + } + + /* Rules for Double NAT */ + cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING " + prototype + " -j SNAT " + markStr + " --to-source " + + externalString + " -d " + keys[0] + " --dport " + keys[2] + fullcone + " && " + + IPTABLES_CMD + " -t nat " + "-" + cmd + " PREROUTING " + prototype + " -j DNAT -d " + m_staticNaptEntry[key].local_ip + " --dport " + + m_staticNaptEntry[key].local_port + " --to-destination " + keys[0] + ":" + keys[2] + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING " + prototype + " -j SNAT -s " + keys[0] + " --sport " + + keys[2] + " --to-source " + m_staticNaptEntry[key].local_ip + ":" + m_staticNaptEntry[key].local_port; + } + else + { + /* Rules for Double NAT */ + cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING " + prototype + " -j SNAT " + markStr + " --to-source " + + externalString + " -d " + key + fullcone + " && " + + IPTABLES_CMD + " -t nat " + "-" + cmd + " PREROUTING" + " -j DNAT -d " + m_staticNatEntry[key].local_ip + " --to-destination " + key + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING" + " -j SNAT -s " + key + " --to-source " + m_staticNatEntry[key].local_ip ; + } + } + + int ret = swss::exec(cmds, res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + return false; + } + + return true; +} + +/* To Add or Delete the Iptables rules for Dynamic NAT/NAPT with ACLs */ +bool NatMgr::setDynamicNatIptablesRulesWithAcl(const string &opCmd, const string &interface, const string &external_ip, + const string &external_port_range, natAclRule_t &natAclRuleId, + const string &key) +{ + SWSS_LOG_ENTER(); + + /* The command should be generated as: for example + * + * iptables -t nat -opCmd POSTROUTING -p tcp -s srcIpAddress -j RETURN + * iptables -t nat -opCmd POSTROUTING -p udp -s srcIpAddress -j RETURN + * iptables -t nat -opCmd POSTROUTING -p icmp -s srcIpAddress -j RETURN + * + * iptables -t nat -opCmd POSTROUTING -p tcp srcIpAddressString -j SNAT -m mark --mark zone-value --to-source external_ip:external_port_range --fullcone + * iptables -t nat -opCmd POSTROUTING -p udp srcIpAddressString -j SNAT -m mark --mark zone-value --to-source external_ip:external_port_range --fullcone + * iptables -t nat -opCmd POSTROUTING -p icmp srcIpAddressString -j SNAT -m mark --mark zone-value --to-source external_ip:external_port_range --fullcone + */ + + std::string res, cmd; + std::string srcIpAddressString = EMPTY_STRING, dstIpAddressString = EMPTY_STRING; + std::string srcPortString = EMPTY_STRING, dstPortString = EMPTY_STRING; + std::string externalString = EMPTY_STRING, fullcone = EMPTY_STRING; + std::string prototype = EMPTY_STRING; + std::string cmds = std::string(""); + vector keys; + std::string markStr = std::string(""); + + markStr = " -m mark --mark " + m_natZoneInterfaceInfo[interface]; + + if (external_port_range.empty()) + { + externalString = external_ip; + } + else + { + externalString = external_ip + ":" + external_port_range; + fullcone = " --fullcone"; + } + + if (natAclRuleId.src_ip_range != "None") + { + srcIpAddressString = " -s " + natAclRuleId.src_ip_range; + } + + if (natAclRuleId.dst_ip_range != "None") + { + dstIpAddressString = " -d " + natAclRuleId.dst_ip_range; + } + + if (natAclRuleId.src_l4_port_range != "None") + { + srcPortString = " --sport " + natAclRuleId.src_l4_port_range; + } + + if (natAclRuleId.dst_l4_port_range != "None") + { + dstPortString = " --dport " + natAclRuleId.dst_l4_port_range; + } + + /* Static Key not empty means Double NAT */ + if (!key.empty()) + { + /* Destination IP/Port address from ACL Rule not valid case for Double NAT */ + if (!dstIpAddressString.empty() or !dstPortString.empty()) + { + SWSS_LOG_WARN("Destination IP/Port is not valid for Twice NAT, skipped adding the ACL Rule"); + return true; + } + + keys = tokenize(key, config_db_key_delimiter); + if (keys.size() > 1) + { + /* Protocol from ACL Rule is not matching with static entry for Double NAT */ + if ((natAclRuleId.ip_protocol != "None") and (natAclRuleId.ip_protocol != keys[1])) + { + SWSS_LOG_WARN("Rule protocol %s is not matching with Static entry, skipped adding the ACL Rule", natAclRuleId.ip_protocol.c_str()); + return true; + } + + if (keys[1] == to_upper(IP_PROTOCOL_UDP)) + { + prototype = " -p udp "; + } + else if (keys[1] == to_upper(IP_PROTOCOL_TCP)) + { + prototype = " -p tcp "; + } + } + if (opCmd == ADD) + { + cmd = INSERT; + } + else + { + cmd = opCmd; + } + } + + if (natAclRuleId.packet_action == PACKET_ACTION_DO_NOT_NAT) + { + /* Rule are for all ip protocols */ + if (natAclRuleId.ip_protocol == "None") + { + /* Static Key empty means Single NAT */ + if (key.empty()) + { + /* Rules for Single NAT */ + cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p tcp" + srcIpAddressString + dstIpAddressString + + srcPortString + dstPortString + " -j RETURN" + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p udp" + srcIpAddressString + dstIpAddressString + + srcPortString + dstPortString + " -j RETURN" + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p icmp" + srcIpAddressString + dstIpAddressString + + srcPortString + dstPortString + " -j RETURN"; + } + else + { + /* Rules for Double NAT */ + if (keys.size() > 1) + { + cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p tcp" + srcIpAddressString + " -d " + keys[0] + + srcPortString + " --dport " + keys[2] + " -j RETURN" + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p udp" + srcIpAddressString + " -d " + keys[0] + + srcPortString + " --dport " + keys[2] + " -j RETURN" + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p icmp" + srcIpAddressString + " -d " + keys[0] + + srcPortString + " --dport " + keys[2] + " -j RETURN"; + } + else + { + cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p tcp" + srcIpAddressString + " -d " + keys[0] + + srcPortString + " -j RETURN" + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p udp" + srcIpAddressString + " -d " + keys[0] + + srcPortString + " -j RETURN" + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p icmp" + srcIpAddressString + " -d " + keys[0] + + srcPortString + " -j RETURN"; + } + + } + } + else + { + /* Static Key empty means Single NAT */ + if (key.empty()) + { + /* Rule for Single NAT */ + cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p " + natAclRuleId.ip_protocol + srcIpAddressString + + dstIpAddressString + srcPortString + dstPortString + " -j RETURN"; + } + else + { + if (keys.size() > 1) + { + /* Rules for Double NAT */ + cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p " + natAclRuleId.ip_protocol + srcIpAddressString + + " -d " + keys[0] + srcPortString + " --dport " + keys[2] + " -j RETURN"; + } + else + { + /* Rules for Double NAT */ + cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p " + natAclRuleId.ip_protocol + srcIpAddressString + + " -d " + keys[0] + srcPortString + " -j RETURN"; + } + } + } + } + else + { + /* Static Key empty means Single NAT */ + if (key.empty()) + { + /* Rules for all ip protocols */ + if (natAclRuleId.ip_protocol == "None") + { + cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p tcp" + srcIpAddressString + dstIpAddressString + srcPortString + dstPortString + + " -j SNAT " + markStr + " --to-source " + externalString + fullcone + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p udp" + srcIpAddressString + dstIpAddressString + srcPortString + dstPortString + + " -j SNAT " + markStr + " --to-source " + externalString + fullcone + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p icmp" + srcIpAddressString + dstIpAddressString + srcPortString + dstPortString + + " -j SNAT " + markStr + " --to-source " + externalString + fullcone; + } + else + { + cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING -p " + natAclRuleId.ip_protocol + srcIpAddressString + + dstIpAddressString + srcPortString + dstPortString + " -j SNAT " + markStr + " --to-source " + externalString + fullcone; + } + } + else + { + if (keys.size() > 1) + { + /* Rules for Double NAT */ + cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING " + prototype + " -j SNAT " + markStr + srcIpAddressString + srcPortString + + " --to-source " + externalString + " -d " + keys[0] + " --dport " + keys[2] + fullcone + " && " + + IPTABLES_CMD + " -t nat " + "-" + cmd + " PREROUTING " + prototype + " -j DNAT -d " + m_staticNaptEntry[key].local_ip + " --dport " + + m_staticNaptEntry[key].local_port + srcIpAddressString + srcPortString + " --to-destination " + keys[0] + ":" + keys[2] + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING " + prototype + " -j SNAT -s " + key[0] + " --sport " + + keys[2] + " --to-source " + m_staticNaptEntry[key].local_ip + ":" + m_staticNaptEntry[key].local_port; + } + else + { + /* Rules for Double NAT */ + cmds = std::string("") + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING " + prototype + " -j SNAT " + markStr + srcIpAddressString + + " --to-source " + externalString + " -d " + key + fullcone + " && " + + IPTABLES_CMD + " -t nat " + "-" + cmd + " PREROUTING" + " -j DNAT -d " + m_staticNatEntry[key].local_ip + srcIpAddressString + + " --to-destination " + key + " && " + + IPTABLES_CMD + " -t nat " + "-" + opCmd + " POSTROUTING" + " -j SNAT -s " + key + " --to-source " + m_staticNatEntry[key].local_ip ; + } + } + } + + int ret = swss::exec(cmds, res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + return false; + } + + return true; +} + +/* To add Static NAT entry based on Static Key if all valid conditions are met */ +void NatMgr::addStaticNatEntry(const string &key) +{ + /* Example: + * Entry is STATIC_NAT|65.55.42.1 and key is 65.55.42.1 + */ + + string interface = EMPTY_STRING; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s NAT entry addition to APPL_DB", key.c_str()); + return; + } + + /* Get the matching Ip interface for dnat type, otherwise return */ + if ((m_staticNatEntry[key].nat_type == DNAT_NAT_TYPE) and (!getIpEnabledIntf(key, interface))) + { + SWSS_LOG_INFO("L3 Interface is not yet enabled for %s, skipping NAT entry addition to APPL_DB", key.c_str()); + return; + } + + /* Check the Static NAT is conflicts with Static NAPT */ + if (isMatchesWithStaticNapt(key, m_staticNatEntry[key].local_ip)) + { + SWSS_LOG_ERROR("Invalid : %s conflicts with Static NAPT, skipping NAT entry addition to APPL_DB", key.c_str()); + return; + } + + m_staticNatEntry[key].interface = interface; + + if (m_staticNatEntry[key].twice_nat_id.empty()) + { + /* Add the new Static Single NAT entry */ + SWSS_LOG_INFO("Adding the Static Single NAT entry for %s", key.c_str()); + addStaticSingleNatEntry(key); + } + else + { + /* Add the new Static Twice NAT entry */ + SWSS_LOG_INFO("Adding the Static Twice NAT entry for %s", key.c_str()); + addStaticTwiceNatEntry(key); + } +} + +/* To add Static NAPT entry based on Static Key if all valid conditions are met */ +void NatMgr::addStaticNaptEntry(const string &key) +{ + /* Example: + * Entry is STATIC_NAPT|65.55.42.1|TCP|1024 and key is 65.55.42.1|TCP|1024 + */ + + string interface = EMPTY_STRING, prototype = EMPTY_STRING; + vector keys = tokenize(key, config_db_key_delimiter); + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s NAPT entry addition to APPL_DB", key.c_str()); + return; + } + + /* Get the matching Ip interface for dnat type, otherwise return */ + if ((m_staticNaptEntry[key].nat_type == DNAT_NAT_TYPE) and (!getIpEnabledIntf(keys[0], interface))) + { + SWSS_LOG_INFO("L3 Interface is not yet enabled for %s, skipping NAPT entry addition to APPL_DB", key.c_str()); + return; + } + + /* Check the Static NAPT is conflicts with Static NAT */ + if (isMatchesWithStaticNat(keys[0], m_staticNaptEntry[key].local_ip)) + { + SWSS_LOG_ERROR("Invalid : %s conflicts with Static NAT, skipping NAPT entry addition to APPL_DB", key.c_str()); + return; + } + + m_staticNaptEntry[key].interface = interface; + + if (m_staticNaptEntry[key].twice_nat_id.empty()) + { + /* Add the new Static Single NAPT entry */ + SWSS_LOG_INFO("Adding the Static Single NAPT entry for %s", key.c_str()); + addStaticSingleNaptEntry(key); + } + else + { + /* Add the new Static Twice NAPT entry */ + SWSS_LOG_INFO("Adding the Static Twice NAPT entry for %s", key.c_str()); + addStaticTwiceNaptEntry(key); + } +} + +/* To delete Static NAT entry based on Static Key if all valid conditions are met */ +void NatMgr::removeStaticNatEntry(const string &key) +{ + /* Example: + * Entry is STATIC_NAT|65.55.42.1 and key is 65.55.42.1 + */ + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s NAT entry deletion", key.c_str()); + return; + } + + if (m_staticNatEntry[key].twice_nat_id.empty()) + { + /* Remove the Static Single NAT Entry */ + SWSS_LOG_INFO("Deleting the Static Single NAT entry for %s", key.c_str()); + removeStaticSingleNatEntry(key); + } + else if ((m_staticNatEntry[key].twice_nat_added == true)) + { + /* Remove the Static Twice NAT entry */ + SWSS_LOG_INFO("Deleting the Static Twice NAT entry for %s", key.c_str()); + removeStaticTwiceNatEntry(key); + } + else + { + SWSS_LOG_INFO("No Static Twice NAT entry to delete for %s", key.c_str()); + m_staticNatEntry[key].interface = NONE_STRING; + } +} + +/* To delete Static NAPT entry based on Static Key if all valid conditions are met */ +void NatMgr::removeStaticNaptEntry(const string &key) +{ + /* Example: + * Entry is STATIC_NAPT|65.55.42.1|TCP|1024 and key is 65.55.42.1|TCP|1024 + */ + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s NAPT entry deletion", key.c_str()); + return; + } + + if (m_staticNaptEntry[key].twice_nat_id.empty()) + { + /* Remove the Static Single NAPT Entry */ + SWSS_LOG_INFO("Deleting the Static Single NAPT entry for %s", key.c_str()); + removeStaticSingleNaptEntry(key); + } + else if ((m_staticNaptEntry[key].twice_nat_added == true)) + { + /* Remove the Static Twice NAPT entry */ + SWSS_LOG_INFO("Deleting the Static Twice NAPT entry for %s", key.c_str()); + removeStaticTwiceNaptEntry(key); + } + else + { + SWSS_LOG_INFO("No Static Twice NAPT entry to delete for %s", key.c_str()); + m_staticNaptEntry[key].interface = NONE_STRING; + } +} + +/* To add Static NAT entries based on L3 Interface if all valid conditions are met */ +void NatMgr::addStaticNatEntries(const string port, const string ipPrefix) +{ + /* Example: + * Port is Ethernet1 and ipPrefix is 10.0.0.1/24 + */ + + string prototype, interface; + bool isEntryAdded = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping NAT entry addition to APPL_DB"); + return; + } + + /* Get all the Static NAT entries */ + for (auto it = m_staticNatEntry.begin(); it != m_staticNatEntry.end(); it++) + { + prototype = EMPTY_STRING, interface = EMPTY_STRING; + + /* Check interface is assigned, means Entry is already added, otherwise continue */ + if ((*it).second.interface != NONE_STRING) + { + continue; + } + + if ((port == NONE_STRING) and (ipPrefix == NONE_STRING)) + { + /* Get the matching Ip interface for dnat type, otherwise return */ + if (((*it).second.nat_type == DNAT_NAT_TYPE) and (!getIpEnabledIntf((*it).first, interface))) + { + continue; + } + } + else + { + /* Check Global ip is matching, otherwise continue */ + if (isGlobalIpMatching(ipPrefix, (*it).first) == false) + { + continue; + } + interface = port; + } + + /* Check the Static NAT is conflicts with Static NAPT */ + if (isMatchesWithStaticNapt((*it).first, (*it).second.local_ip)) + { + continue; + } + + (*it).second.interface = interface; + + isEntryAdded = true; + + if ((*it).second.twice_nat_id.empty()) + { + /* Add the new Static Single NAT entry */ + SWSS_LOG_INFO("Adding the Static Single NAT entry for %s", (*it).first.c_str()); + addStaticSingleNatEntry((*it).first); + } + else + { + /* Add the new Static Twice NAT entry */ + SWSS_LOG_INFO("Adding the Static Twice NAT entry for %s", (*it).first.c_str()); + addStaticTwiceNatEntry((*it).first); + } + } + + if (!isEntryAdded) + { + SWSS_LOG_INFO("No Static NAT entries to add"); + } +} + +/* To add Static NAPT entries based on L3 Interface if all valid conditions are met */ +void NatMgr::addStaticNaptEntries(const string port, const string ipPrefix) +{ + /* Example: + * Port is Ethernet1 and ipPrefix is 10.0.0.1/24 + */ + + string prototype, interface; + bool isEntryAdded = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping NAPT entry addition to APPL_DB"); + return; + } + + /* Get all the Static NAPT entries */ + for (auto it = m_staticNaptEntry.begin(); it != m_staticNaptEntry.end(); it++) + { + vector keys = tokenize((*it).first, config_db_key_delimiter); + vector fvVectorSnat, fvVectorDnat; + prototype = EMPTY_STRING, interface = EMPTY_STRING; + + /* Check interface is assigned, means Entry is already added otherwise continue*/ + if ((*it).second.interface != NONE_STRING) + { + continue; + } + + if ((port == NONE_STRING) and (ipPrefix == NONE_STRING)) + { + /* Get the matching Ip interface for dnat type, otherwise return */ + if (((*it).second.nat_type == DNAT_NAT_TYPE) and (!getIpEnabledIntf(keys[0], interface))) + { + continue; + } + } + else + { + /* Check Global ip is matching, otherwise continue */ + if (isGlobalIpMatching(ipPrefix, keys[0]) == false) + { + continue; + } + interface = port; + } + + /* Check the Static NAPT is conflicts with Static NAT */ + if (isMatchesWithStaticNat(keys[0], (*it).second.local_ip)) + { + continue; + } + + (*it).second.interface = interface; + + isEntryAdded = true; + + if ((*it).second.twice_nat_id.empty()) + { + /* Add the new Static Single NAPT entry */ + SWSS_LOG_INFO("Adding the Static Single NAPT entry for %s", (*it).first.c_str()); + addStaticSingleNaptEntry((*it).first); + } + else + { + /* Add the new Static Twice NAPT entry */ + SWSS_LOG_INFO("Adding the Static Twice NAPT entry for %s", (*it).first.c_str()); + addStaticTwiceNaptEntry((*it).first); + } + } + + if (!isEntryAdded) + { + SWSS_LOG_INFO("No Static NAPT entries to add"); + } +} + +/* To delete Static NAT entries based on L3 Interface if all valid conditions are met */ +void NatMgr::removeStaticNatEntries(const string port, const string ipPrefix) +{ + /* Example: + * Port is Ethernet1 and ipPrefix is 10.0.0.1/24 + */ + + string prototype, interface; + bool isEntryDeleted = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping NAT entry deletion from APPL_DB"); + return; + } + + /* Get all the Static NAT entries */ + for (auto it = m_staticNatEntry.begin(); it != m_staticNatEntry.end(); it++) + { + prototype = EMPTY_STRING, interface = EMPTY_STRING; + + if ((port == NONE_STRING) and (ipPrefix == NONE_STRING)) + { + /* Get the matching Ip interface for dnat type, otherwise return */ + if (((*it).second.nat_type == DNAT_NAT_TYPE) and (!getIpEnabledIntf((*it).first, interface))) + { + continue; + } + + /* Check interface is matching, otherwise continue */ + if ((*it).second.interface != interface) + { + continue; + } + } + else + { + /* Check interface is matching, otherwise continue */ + if ((*it).second.interface != port) + { + continue; + } + + /* Check Global ip is matching, otherwise continue */ + if (isGlobalIpMatching(ipPrefix, (*it).first) == false) + { + continue; + } + interface = port; + } + + isEntryDeleted = true; + + /* Remove the Static NAT Entry */ + SWSS_LOG_INFO("Deleting the Static NAT entry for %s", (*it).first.c_str()); + removeStaticNatEntry((*it).first); + } + + if (!isEntryDeleted) + { + SWSS_LOG_INFO("No Static NAT entries to delete"); + } +} + +/* To delete Static NAPT entries based on L3 Interface if all valid conditions are met */ +void NatMgr::removeStaticNaptEntries(const string port, const string ipPrefix) +{ + /* Example: + * Port is Ethernet1 and ipPrefix is 10.0.0.1/24 + */ + + string prototype, interface; + bool isEntryDeleted = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping NAPT entry deletion from APPL_DB"); + return; + } + + /* Get all the Static NAPT entries */ + for (auto it = m_staticNaptEntry.begin(); it != m_staticNaptEntry.end(); it++) + { + vector keys = tokenize((*it).first, config_db_key_delimiter); + prototype = EMPTY_STRING, interface = EMPTY_STRING; + + if ((port == NONE_STRING) and (ipPrefix == NONE_STRING)) + { + /* Get the matching Ip interface for dnat type, otherwise return */ + if (((*it).second.nat_type == DNAT_NAT_TYPE) and (!getIpEnabledIntf(keys[0], interface))) + { + continue; + } + + /* Check interface is matching, otherwise continue */ + if ((*it).second.interface != interface) + { + continue; + } + } + else + { + /* Check interface is matching, otherwise continue */ + if ((*it).second.interface != port) + { + continue; + } + + /* Check Global ip is matching, otherwise continue */ + if (isGlobalIpMatching(ipPrefix, keys[0]) == false) + { + continue; + } + interface = port; + } + + isEntryDeleted = true; + + /* Remove the Static NAPT Entry */ + SWSS_LOG_INFO("Deleting the Static NAPT entry for %s", (*it).first.c_str()); + removeStaticNaptEntry((*it).first); + } + + if (!isEntryDeleted) + { + SWSS_LOG_INFO("No Static NAPT entries to delete"); + } +} + +/* To add Static Single NAT entry based on Static Key */ +void NatMgr::addStaticSingleNatEntry(const string &key) +{ + /* Example: + * Entry is STATIC_NAT|65.55.42.1 and key is 65.55.42.1 + */ + + string appKeyDnat = EMPTY_STRING, appKeySnat = EMPTY_STRING; + string interface = m_staticNatEntry[key].interface; + vector fvVectorDnat, fvVectorSnat; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s Single NAT entry addition to APPL_DB", key.c_str()); + return; + } + + /* Create APPL_DB key and it's values */ + if (m_staticNatEntry[key].nat_type == DNAT_NAT_TYPE) + { + appKeyDnat += key; + FieldValueTuple p(TRANSLATED_IP, m_staticNatEntry[key].local_ip); + fvVectorDnat.push_back(p); + + appKeySnat += m_staticNatEntry[key].local_ip; + FieldValueTuple q(TRANSLATED_IP, key); + fvVectorSnat.push_back(q); + } + else if (m_staticNatEntry[key].nat_type == SNAT_NAT_TYPE) + { + appKeySnat += key; + FieldValueTuple p(TRANSLATED_IP, m_staticNatEntry[key].local_ip); + fvVectorSnat.push_back(p); + + appKeyDnat += m_staticNatEntry[key].local_ip; + FieldValueTuple q(TRANSLATED_IP, key); + fvVectorDnat.push_back(q); + } + + FieldValueTuple r(NAT_TYPE, DNAT_NAT_TYPE); + fvVectorDnat.push_back(r); + FieldValueTuple s(NAT_TYPE, SNAT_NAT_TYPE); + fvVectorSnat.push_back(s); + + FieldValueTuple t(ENTRY_TYPE, STATIC_ENTRY_TYPE); + fvVectorDnat.push_back(t); + fvVectorSnat.push_back(t); + + if (m_staticNatEntry[key].twice_nat_id != EMPTY_STRING) + { + FieldValueTuple t(TWICE_NAT_ID, m_staticNatEntry[key].twice_nat_id); + fvVectorDnat.push_back(t); + fvVectorSnat.push_back(t); + } + + /* Add it to APPL_DB */ + m_appNatTableProducer.set(appKeyDnat, fvVectorDnat); + m_appNatTableProducer.set(appKeySnat, fvVectorSnat); + + SWSS_LOG_INFO("Added Static NAT %s to APPL_DB", key.c_str()); + + /* Add a dummy conntrack entry for Static NAT entry */ + addConntrackSingleNatEntry(key); + + /* Add Static NAT iptables rule */ + if (!setStaticNatIptablesRules(INSERT, interface, key, m_staticNatEntry[key].local_ip, m_staticNatEntry[key].nat_type)) + { + SWSS_LOG_ERROR("Failed to add Static NAT iptables rules for %s", key.c_str()); + } + else + { + SWSS_LOG_INFO("Added Static NAT iptables rules for %s", key.c_str()); + } +} + +/* To add Static Twice NAT entry based on Static Key if all valid conditions are met */ +void NatMgr::addStaticTwiceNatEntry(const string &key) +{ + /* Example: + * Entry is STATIC_NAT|65.55.42.1 and key is 65.55.42.1 + */ + + string interface = EMPTY_STRING; + string src, translated_src, dest, translated_dest; + bool isEntryAdded = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s Twice NAT entry addition to APPL_DB", key.c_str()); + return; + } + + /* Get all the Static NAT entries */ + for (auto it = m_staticNatEntry.begin(); it != m_staticNatEntry.end(); it++) + { + vector fvVector, reversefvVector; + + /* Check for other entries, otherwise continue */ + if ((*it).first == key) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNatEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is not added, otherwise continue */ + if ((*it).second.twice_nat_added or m_staticNatEntry[key].twice_nat_added) + { + continue; + } + + /* Check interface is assigned, otherwise continue */ + if (((*it).second.interface == NONE_STRING) or (m_staticNatEntry[key].interface == NONE_STRING)) + { + continue; + } + + /* Check the nat type is different, otherwise continue */ + if ((*it).second.nat_type == m_staticNatEntry[key].nat_type) + { + continue; + } + + if (((*it).second.nat_type == DNAT_NAT_TYPE) and + (m_staticNatEntry[key].nat_type == SNAT_NAT_TYPE)) + { + src = key; + dest = (*it).first; + translated_src = m_staticNatEntry[key].local_ip; + translated_dest = (*it).second.local_ip; + interface = (*it).second.interface; + } + else if (((*it).second.nat_type == SNAT_NAT_TYPE) and + (m_staticNatEntry[key].nat_type == DNAT_NAT_TYPE)) + { + src = (*it).first; + dest = key; + translated_src = (*it).second.local_ip; + translated_dest = m_staticNatEntry[key].local_ip; + interface = m_staticNatEntry[key].interface; + } + + /* Create APPL_DB key and it's values */ + string appKey = src + ":" + dest; + string reverseAppKey = translated_dest + ":" + translated_src; + + FieldValueTuple p(TRANSLATED_SRC_IP, translated_src); + fvVector.push_back(p); + FieldValueTuple q(TRANSLATED_DST_IP, translated_dest); + fvVector.push_back(q); + FieldValueTuple r(ENTRY_TYPE, STATIC_ENTRY_TYPE); + fvVector.push_back(r); + + FieldValueTuple p1(TRANSLATED_SRC_IP, dest); + reversefvVector.push_back(p1); + FieldValueTuple q1(TRANSLATED_DST_IP, src); + reversefvVector.push_back(q1); + reversefvVector.push_back(r); + + (*it).second.twice_nat_added = true; + m_staticNatEntry[key].twice_nat_added = true; + + /* Add it to APPL_DB */ + m_appTwiceNatTableProducer.set(appKey, fvVector); + m_appTwiceNatTableProducer.set(reverseAppKey, reversefvVector); + SWSS_LOG_INFO("Added Static Twice NAT for %s and %s to APPL_DB", key.c_str(), (*it).first.c_str()); + + /* Add a dummy conntrack entry for Static Twice NAT entry */ + if (((*it).second.nat_type == DNAT_NAT_TYPE) and + (m_staticNatEntry[key].nat_type == SNAT_NAT_TYPE)) + { + addConntrackTwiceNatEntry(key, (*it).first); + } + else if (((*it).second.nat_type == SNAT_NAT_TYPE) and + (m_staticNatEntry[key].nat_type == DNAT_NAT_TYPE)) + { + addConntrackTwiceNatEntry((*it).first, key); + } + + /* Add Static NAT iptables rule */ + if (!setStaticTwiceNatIptablesRules(INSERT, interface, src, translated_src, dest, translated_dest)) + { + SWSS_LOG_ERROR("Failed to add Static Twice NAT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + } + else + { + isEntryAdded = true; + SWSS_LOG_INFO("Added Static Twice NAT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + } + break; + } + + if (!isEntryAdded) + { + SWSS_LOG_INFO("No Static Twice NAT entries to add"); + } + else + { + return; + } + + /* Get all Binding Info */ + for (auto it = m_natBindingInfo.begin(); it != m_natBindingInfo.end(); it++) + { + string pool_name = (*it).second.pool_name; + string port_range = EMPTY_STRING; + + /* Check the pool is present in cache, otherwise continue */ + if (m_natPoolInfo.find(pool_name) == m_natPoolInfo.end()) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNatEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is not added, otherwise continue */ + if ((*it).second.twice_nat_added or m_staticNatEntry[key].twice_nat_added) + { + continue; + } + + /* Check interface is assigned, means Entry is already added, otherwise continue */ + if (((*it).second.pool_interface == NONE_STRING) or (m_staticNatEntry[key].interface == NONE_STRING)) + { + continue; + } + + /* Check the nat type is same, otherwise continue */ + if ((*it).second.nat_type != m_staticNatEntry[key].nat_type) + { + continue; + } + + /* Check the port_range is not present, otherwise continue */ + port_range = m_natPoolInfo[pool_name].port_range; + if (!port_range.empty() and (port_range != "NULL")) + { + continue; + } + + vector nat_ip = tokenize(m_natPoolInfo[pool_name].ip_range, range_specifier); + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool %s is not valid, skipping dynamic twice nat iptables rules addition for %s", pool_name.c_str(), (*it).first.c_str()); + continue; + } + + (*it).second.twice_nat_added = true; + (*it).second.static_key = key; + m_staticNatEntry[key].twice_nat_added = true; + m_staticNatEntry[key].binding_key = (*it).first; + isEntryAdded = true; + + setDynamicAllForwardOrAclbasedRules(ADD, (*it).second.pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, (*it).second.acl_name, + (*it).first); + + break; + } + + if (!isEntryAdded) + { + SWSS_LOG_INFO("No Static-Dynamic Twice NAT entries to add"); + } + +} + +/* To add Static Single NAPT entry based on Static Key */ +void NatMgr::addStaticSingleNaptEntry(const string &key) +{ + /* Example: + * Entry is STATIC_NAPT|65.55.42.1|TCP|1024 and key is 65.55.42.1|TCP|1024 + */ + + string prototype = EMPTY_STRING, interface = m_staticNaptEntry[key].interface;; + vector keys = tokenize(key, config_db_key_delimiter); + string appKeyDnat = EMPTY_STRING, appKeySnat = EMPTY_STRING; + vector fvVectorDnat, fvVectorSnat; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s Twice NAT entry addition to APPL_DB", key.c_str()); + return; + } + + if (keys[1] == to_upper(IP_PROTOCOL_UDP)) + { + prototype = IP_PROTOCOL_UDP; + } + else if (keys[1] == to_upper(IP_PROTOCOL_TCP)) + { + prototype = IP_PROTOCOL_TCP; + } + + /* Create the APPL_DB key and it's values */ + if (m_staticNaptEntry[key].nat_type == DNAT_NAT_TYPE) + { + appKeyDnat += (keys[1] + DEFAULT_KEY_SEPARATOR + keys[0] + DEFAULT_KEY_SEPARATOR + keys[2]); + FieldValueTuple p(TRANSLATED_IP, m_staticNaptEntry[key].local_ip); + FieldValueTuple q(TRANSLATED_L4_PORT, m_staticNaptEntry[key].local_port); + fvVectorDnat.push_back(p); + fvVectorDnat.push_back(q); + + appKeySnat += (keys[1] + DEFAULT_KEY_SEPARATOR + m_staticNaptEntry[key].local_ip + DEFAULT_KEY_SEPARATOR + m_staticNaptEntry[key].local_port); + FieldValueTuple r(TRANSLATED_IP, keys[0]); + FieldValueTuple s(TRANSLATED_L4_PORT, keys[2]); + fvVectorSnat.push_back(r); + fvVectorSnat.push_back(s); + } + else if (m_staticNaptEntry[key].nat_type == SNAT_NAT_TYPE) + { + appKeySnat += (keys[1] + DEFAULT_KEY_SEPARATOR + keys[0] + DEFAULT_KEY_SEPARATOR + keys[2]); + FieldValueTuple p(TRANSLATED_IP, m_staticNaptEntry[key].local_ip); + FieldValueTuple q(TRANSLATED_L4_PORT, m_staticNaptEntry[key].local_port); + fvVectorSnat.push_back(p); + fvVectorSnat.push_back(q); + + appKeyDnat += (keys[1] + DEFAULT_KEY_SEPARATOR + m_staticNaptEntry[key].local_ip + DEFAULT_KEY_SEPARATOR + m_staticNaptEntry[key].local_port); + FieldValueTuple r(TRANSLATED_IP, keys[0]); + FieldValueTuple s(TRANSLATED_L4_PORT, keys[2]); + fvVectorDnat.push_back(r); + fvVectorDnat.push_back(s); + } + + FieldValueTuple t(NAT_TYPE, DNAT_NAT_TYPE); + fvVectorDnat.push_back(t); + FieldValueTuple u(NAT_TYPE, SNAT_NAT_TYPE); + fvVectorSnat.push_back(u); + + FieldValueTuple v(ENTRY_TYPE, STATIC_ENTRY_TYPE); + fvVectorDnat.push_back(v); + fvVectorSnat.push_back(v); + + /* Add it to APPL_DB */ + m_appNaptTableProducer.set(appKeyDnat, fvVectorDnat); + m_appNaptTableProducer.set(appKeySnat, fvVectorSnat); + + SWSS_LOG_INFO("Added Static NAPT %s to APPL_DB", key.c_str()); + + /* Delete any conntrack entry if exists */ + deleteConntrackSingleNaptEntry(key); + + /* Add a dummy conntrack entry for Static NAPT entry */ + addConntrackSingleNaptEntry(key); + + /* Add Static NAPT iptables rule */ + if (!setStaticNaptIptablesRules(INSERT, interface, prototype, keys[0], keys[2], + m_staticNaptEntry[key].local_ip, m_staticNaptEntry[key].local_port, + m_staticNaptEntry[key].nat_type)) + { + SWSS_LOG_ERROR("Failed to add Static NAPT iptables rules for %s", key.c_str()); + } + else + { + SWSS_LOG_INFO("Added Static NAPT iptables rules for %s", key.c_str()); + } +} + +/* To add Static Twice NAPT entry based on Static Key if all valid conditions are met */ +void NatMgr::addStaticTwiceNaptEntry(const string &key) +{ + /* Example: + * Entry is STATIC_NAPT|65.55.42.1|TCP|1024 and key is 65.55.42.1|TCP|1024 + */ + + string interface = EMPTY_STRING, prototype = EMPTY_STRING; + vector keys = tokenize(key, config_db_key_delimiter); + string src, translated_src, dest, translated_dest; + string src_port, translated_src_port, dest_port, translated_dest_port; + bool isEntryAdded = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s Twice NAT entry addition to APPL_DB", key.c_str()); + return; + } + + if (keys[1] == to_upper(IP_PROTOCOL_UDP)) + { + prototype = IP_PROTOCOL_UDP; + } + else if (keys[1] == to_upper(IP_PROTOCOL_TCP)) + { + prototype = IP_PROTOCOL_TCP; + } + + /* Get all the Static NAPT entries */ + for (auto it = m_staticNaptEntry.begin(); it != m_staticNaptEntry.end(); it++) + { + vector fvVector, reversefvVector; + vector entry_keys = tokenize((*it).first, config_db_key_delimiter); + + /* Check for other entries, otherwise continue */ + if ((*it).first == key) + { + continue; + } + + /* Check for both protocols are same, otherwise continue */ + if (entry_keys[1] != keys[1]) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNaptEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is not added, otherwise continue */ + if ((*it).second.twice_nat_added or m_staticNaptEntry[key].twice_nat_added) + { + continue; + } + + /* Check interface is assigned, means Entry is already added, otherwise continue */ + if (((*it).second.interface == NONE_STRING) or (m_staticNaptEntry[key].interface == NONE_STRING)) + { + continue; + } + + /* Check the nat type is different, otherwise continue */ + if ((*it).second.nat_type == m_staticNaptEntry[key].nat_type) + { + continue; + } + + if (((*it).second.nat_type == DNAT_NAT_TYPE) and + (m_staticNaptEntry[key].nat_type == SNAT_NAT_TYPE)) + { + src = keys[0]; + src_port = keys[2]; + dest = entry_keys[0]; + dest_port = entry_keys[2]; + translated_src = m_staticNaptEntry[key].local_ip; + translated_src_port = m_staticNaptEntry[key].local_port; + translated_dest = (*it).second.local_ip; + translated_dest_port = (*it).second.local_port; + interface = (*it).second.interface; + } + else if (((*it).second.nat_type == SNAT_NAT_TYPE) and + (m_staticNaptEntry[key].nat_type == DNAT_NAT_TYPE)) + { + src = entry_keys[0]; + src_port = entry_keys[2]; + dest = keys[0]; + dest_port = keys[2]; + translated_src = (*it).second.local_ip; + translated_src_port = (*it).second.local_port; + translated_dest = m_staticNaptEntry[key].local_ip; + translated_dest_port = m_staticNaptEntry[key].local_port; + interface = m_staticNaptEntry[key].interface; + } + + /* Create APPL_DB key and it's values */ + string appKey = keys[1] + ":" + src + ":" + src_port + ":" + dest + ":" + dest_port; + string reverseAppKey = keys[1] + ":" + translated_dest + ":" + translated_dest_port + ":" + translated_src + ":" + translated_src_port; + + FieldValueTuple p(TRANSLATED_SRC_IP, translated_src); + fvVector.push_back(p); + FieldValueTuple q(TRANSLATED_SRC_L4_PORT, translated_src_port); + fvVector.push_back(q); + FieldValueTuple r(TRANSLATED_DST_IP, translated_dest); + fvVector.push_back(r); + FieldValueTuple s(TRANSLATED_DST_L4_PORT, translated_dest_port); + fvVector.push_back(s); + FieldValueTuple t(ENTRY_TYPE, STATIC_ENTRY_TYPE); + fvVector.push_back(t); + + FieldValueTuple p1(TRANSLATED_SRC_IP, dest); + reversefvVector.push_back(p1); + FieldValueTuple q1(TRANSLATED_SRC_L4_PORT, dest_port); + reversefvVector.push_back(q1); + FieldValueTuple r1(TRANSLATED_DST_IP, src); + reversefvVector.push_back(r1); + FieldValueTuple s1(TRANSLATED_DST_L4_PORT, src_port); + reversefvVector.push_back(s1); + reversefvVector.push_back(t); + + (*it).second.twice_nat_added = true; + m_staticNaptEntry[key].twice_nat_added = true; + + /* Add it to APPL_DB */ + m_appTwiceNaptTableProducer.set(appKey, fvVector); + m_appTwiceNaptTableProducer.set(reverseAppKey, reversefvVector); + + SWSS_LOG_INFO("Added Static Twice NAPT for %s and %s to APPL_DB", key.c_str(), (*it).first.c_str()); + + if (((*it).second.nat_type == DNAT_NAT_TYPE) and + (m_staticNaptEntry[key].nat_type == SNAT_NAT_TYPE)) + { + /* Delete any conntrack entry if exists */ + deleteConntrackTwiceNaptEntry(key, (*it).first); + + /* Add a dummy conntrack entry for Static Twice NAPT entry */ + addConntrackTwiceNaptEntry(key, (*it).first); + } + else if (((*it).second.nat_type == SNAT_NAT_TYPE) and + (m_staticNaptEntry[key].nat_type == DNAT_NAT_TYPE)) + { + /* Delete any conntrack entry if exists */ + deleteConntrackTwiceNaptEntry((*it).first, key); + + /* Add a dummy conntrack entry for Static Twice NAPT entry */ + addConntrackTwiceNaptEntry((*it).first, key); + } + + /* Add Static NAPT iptables rule */ + if (!setStaticTwiceNaptIptablesRules(INSERT, interface, prototype, src, src_port, translated_src, translated_src_port, + dest, dest_port, translated_dest, translated_dest_port)) + { + SWSS_LOG_ERROR("Failed to add Static Twice NAT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + } + else + { + isEntryAdded = true; + SWSS_LOG_INFO("Added Static Twice NAT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + } + break; + } + + if (!isEntryAdded) + { + SWSS_LOG_INFO("No Static Twice NAPT entries to add"); + } + else + { + return; + } + + /* Get all Binding Info */ + for (auto it = m_natBindingInfo.begin(); it != m_natBindingInfo.end(); it++) + { + string pool_name = (*it).second.pool_name; + string port_range = EMPTY_STRING; + + /* Check the pool is present in cache, otherwise continue */ + if (m_natPoolInfo.find(pool_name) == m_natPoolInfo.end()) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNaptEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is not added, otherwise continue */ + if ((*it).second.twice_nat_added or m_staticNaptEntry[key].twice_nat_added) + { + continue; + } + + /* Check interface is assigned, means Entry is already added, otherwise continue */ + if (((*it).second.pool_interface == NONE_STRING) or (m_staticNaptEntry[key].interface == NONE_STRING)) + { + continue; + } + + /* Check the nat type is same, otherwise continue */ + if ((*it).second.nat_type != m_staticNaptEntry[key].nat_type) + { + continue; + } + + /* Check the port_range is present, otherwise continue */ + port_range = m_natPoolInfo[pool_name].port_range; + if (port_range.empty() or (port_range == "NULL")) + { + continue; + } + + vector nat_ip = tokenize(m_natPoolInfo[pool_name].ip_range, range_specifier); + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool %s is not valid, skipping dynamic twice nat iptables rules addition for %s", pool_name.c_str(), (*it).first.c_str()); + continue; + } + + (*it).second.twice_nat_added = true; + (*it).second.static_key = key; + m_staticNaptEntry[key].twice_nat_added = true; + m_staticNaptEntry[key].binding_key = (*it).first; + isEntryAdded = true; + + setDynamicAllForwardOrAclbasedRules(ADD, (*it).second.pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, (*it).second.acl_name, + (*it).first); + + break; + } + + if (!isEntryAdded) + { + SWSS_LOG_INFO("No Static-Dynamic Twice NAPT entries to add"); + } +} + +/* To delete Static Single NAT entry based on Static Key if all valid conditions are met */ +void NatMgr::removeStaticSingleNatEntry(const string &key) +{ + /* Example: + * Entry is STATIC_NAT|65.55.42.1 and key is 65.55.42.1 + */ + + string interface = EMPTY_STRING; + vector fvVectorDnat, fvVectorSnat; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s NAT entry deletion", key.c_str()); + return; + } + + /* Get the matching Ip interface for dnat type, otherwise return */ + if ((m_staticNatEntry[key].nat_type == DNAT_NAT_TYPE) and (!getIpEnabledIntf(key, interface))) + { + SWSS_LOG_INFO("L3 Interface is not yet enabled for %s, skipping NAT entry deletion", key.c_str()); + return; + } + + /* Check for the key interface is matching with the saved one, otherwise return */ + if (m_staticNatEntry[key].interface != interface) + { + SWSS_LOG_INFO("Interface is not matching for %s, skipping NAT entry deletion", key.c_str()); + m_staticNatEntry.erase(key); + return; + } + + /* Create the APPL_DB key and it's values */ + string appKeyDnat = EMPTY_STRING, appKeySnat = EMPTY_STRING; + + if (m_staticNatEntry[key].nat_type == DNAT_NAT_TYPE) + { + appKeyDnat += key; + appKeySnat += m_staticNatEntry[key].local_ip; + } + else if (m_staticNatEntry[key].nat_type == SNAT_NAT_TYPE) + { + appKeySnat += key; + appKeyDnat += m_staticNatEntry[key].local_ip; + } + + /* Delete conntrack entry */ + deleteConntrackSingleNatEntry(key); + + /* Delete it from APPL_DB */ + m_appNatTableProducer.del(appKeyDnat); + m_appNatTableProducer.del(appKeySnat); + + SWSS_LOG_INFO("Deleted Static NAT %s from APPL_DB", key.c_str()); + + /* Remove Static NAT iptables rule */ + if (!setStaticNatIptablesRules(DELETE, interface, key, m_staticNatEntry[key].local_ip, m_staticNatEntry[key].nat_type)) + { + SWSS_LOG_ERROR("Failed to delete Static NAT iptables rules for %s", key.c_str()); + } + else + { + SWSS_LOG_INFO("Deleted Static NAT iptables rules for %s", key.c_str()); + } + + m_staticNatEntry[key].interface = NONE_STRING; + + /* Add any static NAPT conflict entry if present */ + for (auto it = m_staticNaptEntry.begin(); it != m_staticNaptEntry.end(); it++) + { + vector keys = tokenize((*it).first, config_db_key_delimiter); + if ((keys[0] == key) and ((*it).second.local_ip == m_staticNatEntry[key].local_ip) and + ((*it).second.interface == NONE_STRING)) + { + /* Add the new Static NAPT entry */ + SWSS_LOG_INFO("Adding the Static NAPT entry for %s", key.c_str()); + addStaticNaptEntry((*it).first); + break; + } + } +} + +/* To delete Static Twice NAT entry based on Static Key if all valid conditions are met */ +void NatMgr::removeStaticTwiceNatEntry(const string &key) +{ + /* Example: + * Entry is STATIC_NAT|65.55.42.1 and key is 65.55.42.1 + */ + + string interface = EMPTY_STRING; + string src, translated_src, dest, translated_dest; + bool isEntryDeleted = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s Twice NAT entry deletion from APPL_DB", key.c_str()); + return; + } + + /* Get all the Static NAT entries */ + for (auto it = m_staticNatEntry.begin(); it != m_staticNatEntry.end(); it++) + { + /* Check for other entries, otherwise continue */ + if ((*it).first == key) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNatEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is not added, otherwise continue */ + if ((!(*it).second.twice_nat_added) or (!m_staticNatEntry[key].twice_nat_added)) + { + continue; + } + + /* Check interface is not assigned, means Entry is not added, otherwise continue */ + if (((*it).second.interface == NONE_STRING) or (m_staticNatEntry[key].interface == NONE_STRING)) + { + continue; + } + + /* Check the nat type is different, otherwise continue */ + if ((*it).second.nat_type == m_staticNatEntry[key].nat_type) + { + continue; + } + + if (((*it).second.nat_type == DNAT_NAT_TYPE) and + (m_staticNatEntry[key].nat_type == SNAT_NAT_TYPE)) + { + src = key; + dest = (*it).first; + translated_src = m_staticNatEntry[key].local_ip; + translated_dest = (*it).second.local_ip; + interface = (*it).second.interface; + } + else if (((*it).second.nat_type == SNAT_NAT_TYPE) and + (m_staticNatEntry[key].nat_type == DNAT_NAT_TYPE)) + { + src = (*it).first; + dest = key; + translated_src = (*it).second.local_ip; + translated_dest = m_staticNatEntry[key].local_ip; + interface = m_staticNatEntry[key].interface; + } + + string appKey = src + ":" + dest; + string reverseAppKey = translated_dest + ":" + translated_src; + + if (((*it).second.nat_type == DNAT_NAT_TYPE) and + (m_staticNatEntry[key].nat_type == SNAT_NAT_TYPE)) + { + /* Delete any conntrack entry */ + deleteConntrackTwiceNatEntry(key, (*it).first); + } + else if (((*it).second.nat_type == SNAT_NAT_TYPE) and + (m_staticNatEntry[key].nat_type == DNAT_NAT_TYPE)) + { + /* Delete any conntrack entry */ + deleteConntrackTwiceNatEntry((*it).first, key); + } + + /* Delete it from APPL_DB */ + m_appTwiceNatTableProducer.del(appKey); + m_appTwiceNatTableProducer.del(reverseAppKey); + + (*it).second.twice_nat_added = false; + m_staticNatEntry[key].twice_nat_added = false; + + SWSS_LOG_INFO("Deleted Static Twice NAT for %s and %s from APPL_DB", key.c_str(), (*it).first.c_str()); + + /* Delete Static NAT iptables rule */ + if (!setStaticTwiceNatIptablesRules(DELETE, interface, src, translated_src, dest, translated_dest)) + { + SWSS_LOG_ERROR("Failed to delete Static Twice NAT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + } + else + { + SWSS_LOG_INFO("Deleted Static Twice NAT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + isEntryDeleted = true; + } + + m_staticNatEntry[key].interface = NONE_STRING; + + return; + } + + if (!isEntryDeleted) + { + SWSS_LOG_INFO("No Static Twice NAT entries to delete"); + } + else + { + return; + } + + /* Get all Binding Info */ + for (auto it = m_natBindingInfo.begin(); it != m_natBindingInfo.end(); it++) + { + string port_range = EMPTY_STRING; + string pool_name = (*it).second.pool_name; + string acls_name = (*it).second.acl_name; + + /* Check the pool is present in cache, otherwise continue */ + if (m_natPoolInfo.find(pool_name) == m_natPoolInfo.end()) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNatEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is added, otherwise continue */ + if ((!(*it).second.twice_nat_added) or (!m_staticNatEntry[key].twice_nat_added)) + { + continue; + } + + /* Check interface is assigned, otherwise continue */ + if (((*it).second.pool_interface == NONE_STRING) or (m_staticNatEntry[key].interface == NONE_STRING)) + { + continue; + } + + /* Check the nat type is same, otherwise continue */ + if ((*it).second.nat_type != m_staticNatEntry[key].nat_type) + { + continue; + } + + /* Check the port_range is not present, otherwise continue */ + port_range = m_natPoolInfo[pool_name].port_range; + if (!port_range.empty() and (port_range != "NULL")) + { + continue; + } + + /* Check the key is matching, otherwise continue */ + if (m_staticNatEntry[key].binding_key != (*it).first) + { + continue; + } + + vector nat_ip = tokenize(m_natPoolInfo[pool_name].ip_range, range_specifier); + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool %s is not valid, skipping dynamic twice nat iptables rules deletion for %s", pool_name.c_str(), (*it).first.c_str()); + continue; + } + + /* Delete Dynamic rules */ + setDynamicAllForwardOrAclbasedRules(DELETE, (*it).second.pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, acls_name, (*it).first); + + (*it).second.twice_nat_added = false; + (*it).second.static_key = EMPTY_STRING; + m_staticNatEntry[key].twice_nat_added = false; + m_staticNatEntry[key].binding_key = EMPTY_STRING; + isEntryDeleted = true; + break; + } + + if (!isEntryDeleted) + { + SWSS_LOG_INFO("No Static-Dynamic Twice NAT entries to delete"); + } +} + +/* To delete Static Single NAPT entry based on Static Key if all valid conditions are met */ +void NatMgr::removeStaticSingleNaptEntry(const string &key) +{ + /* Example: + * Entry is STATIC_NAPT|65.55.42.1|TCP|1024 and key is 65.55.42.1|TCP|1024 + */ + + string interface = EMPTY_STRING, prototype = EMPTY_STRING; + vector keys = tokenize(key, config_db_key_delimiter); + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s NAPT entry deletion", key.c_str()); + return; + } + + /* Get the matching Ip interface for dnat type, otherwise return */ + if ((m_staticNaptEntry[key].nat_type == DNAT_NAT_TYPE) and (!getIpEnabledIntf(keys[0], interface))) + { + SWSS_LOG_INFO("L3 Interface is not yet enabled for %s, skipping NAPT entry deletion", key.c_str()); + return; + } + + /* Check for the key interface is matching with the save one, otherwise return */ + if (m_staticNaptEntry[key].interface != interface) + { + SWSS_LOG_INFO("Interface is not matching for %s, skipping NAPT entry deletion", key.c_str()); + return; + } + + if (keys[1] == to_upper(IP_PROTOCOL_UDP)) + { + prototype = IP_PROTOCOL_UDP; + } + else if (keys[1] == to_upper(IP_PROTOCOL_TCP)) + { + prototype = IP_PROTOCOL_TCP; + } + + /* Create the APPL_DB key and it's values */ + string appKeyDnat = EMPTY_STRING, appKeySnat = EMPTY_STRING; + + if (m_staticNaptEntry[key].nat_type == DNAT_NAT_TYPE) + { + appKeyDnat += (keys[1] + DEFAULT_KEY_SEPARATOR + keys[0] + DEFAULT_KEY_SEPARATOR + keys[2]); + appKeySnat += (keys[1] + DEFAULT_KEY_SEPARATOR + m_staticNaptEntry[key].local_ip + DEFAULT_KEY_SEPARATOR + m_staticNaptEntry[key].local_port); + } + else if (m_staticNaptEntry[key].nat_type == SNAT_NAT_TYPE) + { + appKeySnat += (keys[1] + DEFAULT_KEY_SEPARATOR + keys[0] + DEFAULT_KEY_SEPARATOR + keys[2]); + appKeyDnat += (keys[1] + DEFAULT_KEY_SEPARATOR + m_staticNaptEntry[key].local_ip + DEFAULT_KEY_SEPARATOR + m_staticNaptEntry[key].local_port); + } + + /* Delete conntrack entry */ + deleteConntrackSingleNaptEntry(key); + + /* Delete it from APPL_DB */ + m_appNaptTableProducer.del(appKeyDnat); + m_appNaptTableProducer.del(appKeySnat); + + SWSS_LOG_INFO("Deleted Static NAPT %s from APPL_DB", key.c_str()); + + /* Remove Static NAPT iptables rule */ + if (!setStaticNaptIptablesRules(DELETE, interface, prototype, keys[0], keys[2], + m_staticNaptEntry[key].local_ip, m_staticNaptEntry[key].local_port, + m_staticNaptEntry[key].nat_type)) + { + SWSS_LOG_ERROR("Failed to delete Static NAPT iptables rules for %s", key.c_str()); + } + else + { + SWSS_LOG_INFO("Deleted Static NAPT iptables rules for %s", key.c_str()); + } + + m_staticNaptEntry[key].interface = NONE_STRING; + + /* Add any static NAT conflict entry if present */ + for (auto it = m_staticNatEntry.begin(); it != m_staticNatEntry.end(); it++) + { + if (((*it).first == keys[0]) and ((*it).second.local_ip == m_staticNaptEntry[key].local_ip) and + ((*it).second.interface == NONE_STRING)) + { + /* Add the new Static NAT entry */ + SWSS_LOG_INFO("Adding the Static NAT entry for %s", key.c_str()); + addStaticNatEntry(keys[0]); + break; + } + } +} + +/* To delete Static Twice NAPT entry based on Static Key if all valid conditions are met */ +void NatMgr::removeStaticTwiceNaptEntry(const string &key) +{ + /* Example: + * Entry is STATIC_NAPT|65.55.42.1|TCP|1024 and key is 65.55.42.1|TCP|1024 + */ + + string interface = EMPTY_STRING, prototype = EMPTY_STRING; + vector keys = tokenize(key, config_db_key_delimiter); + string src, translated_src, dest, translated_dest; + string src_port, translated_src_port, dest_port, translated_dest_port; + bool isEntryDeleted = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s Twice NAPT entry deletion from APPL_DB", key.c_str()); + return; + } + + if (keys[1] == to_upper(IP_PROTOCOL_UDP)) + { + prototype = IP_PROTOCOL_UDP; + } + else if (keys[1] == to_upper(IP_PROTOCOL_TCP)) + { + prototype = IP_PROTOCOL_TCP; + } + + /* Get all the Static NAPT entries */ + for (auto it = m_staticNaptEntry.begin(); it != m_staticNaptEntry.end(); it++) + { + vector entry_keys = tokenize((*it).first, config_db_key_delimiter); + + /* Check for other entries, otherwise continue */ + if ((*it).first == key) + { + continue; + } + + /* Check for both protocols are same, otherwise continue */ + if (entry_keys[1] != keys[1]) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNaptEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is not added, otherwise continue */ + if ((!(*it).second.twice_nat_added) or (!m_staticNaptEntry[key].twice_nat_added)) + { + continue; + } + + /* Check interface is not assigned, means Entry is not added, otherwise continue */ + if (((*it).second.interface == NONE_STRING) or (m_staticNaptEntry[key].interface == NONE_STRING)) + { + continue; + } + + /* Check the nat type is different, otherwise continue */ + if ((*it).second.nat_type == m_staticNaptEntry[key].nat_type) + { + continue; + } + + if (((*it).second.nat_type == DNAT_NAT_TYPE) and + (m_staticNaptEntry[key].nat_type == SNAT_NAT_TYPE)) + { + src = keys[0]; + src_port = keys[2]; + dest = entry_keys[0]; + dest_port = entry_keys[2]; + translated_src = m_staticNaptEntry[key].local_ip; + translated_src_port = m_staticNaptEntry[key].local_port; + translated_dest = (*it).second.local_ip; + translated_dest_port = (*it).second.local_port; + interface = (*it).second.interface; + } + else if (((*it).second.nat_type == SNAT_NAT_TYPE) and + (m_staticNaptEntry[key].nat_type == DNAT_NAT_TYPE)) + { + src = entry_keys[0]; + src_port = entry_keys[2]; + dest = keys[0]; + dest_port = keys[2]; + translated_src = (*it).second.local_ip; + translated_src_port = (*it).second.local_port; + translated_dest = m_staticNaptEntry[key].local_ip; + translated_dest_port = m_staticNaptEntry[key].local_port; + interface = m_staticNaptEntry[key].interface; + } + + string appKey = keys[1] + ":" + src + ":" + src_port + ":" + dest + ":" + dest_port; + string reverseAppKey = keys[1] + ":" + translated_dest + ":" + translated_dest_port + ":" + translated_src + ":" + translated_src_port; + + if (((*it).second.nat_type == DNAT_NAT_TYPE) and + (m_staticNaptEntry[key].nat_type == SNAT_NAT_TYPE)) + { + /* Delete any conntrack entry */ + deleteConntrackTwiceNaptEntry(key, (*it).first); + } + else if (((*it).second.nat_type == SNAT_NAT_TYPE) and + (m_staticNaptEntry[key].nat_type == DNAT_NAT_TYPE)) + { + /* Delete any conntrack entry */ + deleteConntrackTwiceNaptEntry((*it).first, key); + } + + /* Delete it from APPL_DB */ + m_appTwiceNaptTableProducer.del(appKey); + m_appTwiceNaptTableProducer.del(reverseAppKey); + + (*it).second.twice_nat_added = false; + m_staticNaptEntry[key].twice_nat_added = false; + + SWSS_LOG_INFO("Deleted Static Twice NAPT for %s and %s from APPL_DB", key.c_str(), (*it).first.c_str()); + + /* Delete Static NAPT iptables rule */ + if (!setStaticTwiceNaptIptablesRules(DELETE, interface, prototype, src, src_port, translated_src, translated_src_port, + dest, dest_port, translated_dest, translated_dest_port)) + { + SWSS_LOG_ERROR("Failed to delete Static Twice NAPT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + } + else + { + SWSS_LOG_INFO("Deleted Static Twice NAPT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + isEntryDeleted = true; + } + + m_staticNaptEntry[key].interface = NONE_STRING; + + return; + } + + if (!isEntryDeleted) + { + SWSS_LOG_INFO("No Static Twice NAPT entries to delete"); + } + else + { + return; + } + + /* Get all Binding Info */ + for (auto it = m_natBindingInfo.begin(); it != m_natBindingInfo.end(); it++) + { + string port_range = EMPTY_STRING; + string pool_name = (*it).second.pool_name; + string acls_name = (*it).second.acl_name; + + /* Check the pool is present in cache, otherwise continue */ + if (m_natPoolInfo.find(pool_name) == m_natPoolInfo.end()) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNaptEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is added, otherwise continue */ + if ((!(*it).second.twice_nat_added) or (!m_staticNaptEntry[key].twice_nat_added)) + { + continue; + } + + /* Check interface is assigned, otherwise continue */ + if (((*it).second.pool_interface == NONE_STRING) or (m_staticNaptEntry[key].interface == NONE_STRING)) + { + continue; + } + + /* Check the nat type is same, otherwise continue */ + if ((*it).second.nat_type != m_staticNaptEntry[key].nat_type) + { + continue; + } + + /* Check the port_range is present, otherwise continue */ + port_range = m_natPoolInfo[pool_name].port_range; + if (port_range.empty() or (port_range == "NULL")) + { + continue; + } + + /* Check the key is matching, otherwise continue */ + if (m_staticNaptEntry[key].binding_key != (*it).first) + { + continue; + } + + vector nat_ip = tokenize(m_natPoolInfo[pool_name].ip_range, range_specifier); + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool %s is not valid, skipping dynamic twice nat iptables rules deletion for %s", pool_name.c_str(), (*it).first.c_str()); + continue; + } + + /* Delete Dynamic rules */ + setDynamicAllForwardOrAclbasedRules(DELETE, (*it).second.pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, acls_name, (*it).first); + + (*it).second.twice_nat_added = false; + (*it).second.static_key = EMPTY_STRING; + m_staticNatEntry[key].twice_nat_added = false; + m_staticNatEntry[key].binding_key = EMPTY_STRING; + isEntryDeleted = true; + break; + } + + if (!isEntryDeleted) + { + SWSS_LOG_INFO("No Static-Dynamic Twice NAPT entries to delete"); + } +} + +/* To add Static NAT Iptables based on L3 Interface if all valid conditions are met */ +void NatMgr::addStaticNatIptables(const string port) +{ + /* Example: + * Port is Ethernet1 + */ + bool isRulesAdded = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping NAT Iptables addition"); + return; + } + + /* Get all the Static NAT entries */ + for (auto it = m_staticNatEntry.begin(); it != m_staticNatEntry.end(); it++) + { + /* Check interface is same as given Port, otherwise continue */ + if ((*it).second.interface != port) + { + continue; + } + + isRulesAdded = true; + + if ((*it).second.twice_nat_id.empty()) + { + /* Add the new Static Single NAT Iptables */ + SWSS_LOG_INFO("Adding the Static Single NAT Iptables for %s", (*it).first.c_str()); + addStaticSingleNatIptables((*it).first); + } + else + { + /* Add the new Static Twice NAT Iptables */ + SWSS_LOG_INFO("Adding the Static Twice NAT Iptables for %s", (*it).first.c_str()); + addStaticTwiceNatIptables((*it).first); + } + } + + if (!isRulesAdded) + { + SWSS_LOG_INFO("No Static NAT iptables rules to add"); + } +} + +/* To add Static Single NAT iptables based on Static Key */ +void NatMgr::addStaticSingleNatIptables(const string &key) +{ + /* Example: + * Entry is STATIC_NAT|65.55.42.1 and key is 65.55.42.1 + */ + + string interface = m_staticNatEntry[key].interface; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s Single NAT Iptables addition", key.c_str()); + return; + } + + /* Add Static NAT iptables rule */ + if (!setStaticNatIptablesRules(INSERT, interface, key, m_staticNatEntry[key].local_ip, m_staticNatEntry[key].nat_type)) + { + SWSS_LOG_ERROR("Failed to add Static NAT iptables rules for %s", key.c_str()); + } + else + { + SWSS_LOG_INFO("Added Static NAT iptables rules for %s", key.c_str()); + } +} + +/* To add Static Twice NAT Iptables based on Static Key if all valid conditions are met */ +void NatMgr::addStaticTwiceNatIptables(const string &key) +{ + /* Example: + * Entry is STATIC_NAT|65.55.42.1 and key is 65.55.42.1 + */ + + string interface = EMPTY_STRING; + string src, translated_src, dest, translated_dest; + string src_port, translated_src_port, dest_port, translated_dest_port; + bool isRulesAdded = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s Twice NAT entry addition to APPL_DB", key.c_str()); + return; + } + + /* Get all the Static NAT entries */ + for (auto it = m_staticNatEntry.begin(); it != m_staticNatEntry.end(); it++) + { + /* Check for other entries, otherwise continue */ + if ((*it).first == key) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNatEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is added, otherwise continue */ + if (!(*it).second.twice_nat_added or !m_staticNatEntry[key].twice_nat_added) + { + continue; + } + + /* Check the nat type is different, otherwise continue */ + if ((*it).second.nat_type == m_staticNatEntry[key].nat_type) + { + continue; + } + + if (((*it).second.nat_type == DNAT_NAT_TYPE) and + (m_staticNatEntry[key].nat_type == SNAT_NAT_TYPE)) + { + src = key; + dest = (*it).first; + translated_src = m_staticNatEntry[key].local_ip; + translated_dest = (*it).second.local_ip; + interface = (*it).second.interface; + } + else if (((*it).second.nat_type == SNAT_NAT_TYPE) and + (m_staticNatEntry[key].nat_type == DNAT_NAT_TYPE)) + { + src = (*it).first; + dest = key; + translated_src = (*it).second.local_ip; + translated_dest = m_staticNatEntry[key].local_ip; + interface = m_staticNatEntry[key].interface; + } + + /* Add Static NAT iptables rule */ + if (!setStaticTwiceNatIptablesRules(INSERT, interface, src, translated_src, dest, translated_dest)) + { + SWSS_LOG_ERROR("Failed to add Static Twice NAT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + } + else + { + isRulesAdded = true; + SWSS_LOG_INFO("Added Static Twice NAT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + } + break; + } + + if (!isRulesAdded) + { + SWSS_LOG_INFO("No Static Twice NAT iptables rules to add"); + } + else + { + return; + } + + /* Get all Binding Info */ + for (auto it = m_natBindingInfo.begin(); it != m_natBindingInfo.end(); it++) + { + string pool_name = (*it).second.pool_name; + string port_range = EMPTY_STRING; + + /* Check the pool is present in cache, otherwise continue */ + if (m_natPoolInfo.find(pool_name) == m_natPoolInfo.end()) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNatEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is added, otherwise continue */ + if (!(*it).second.twice_nat_added or !m_staticNatEntry[key].twice_nat_added) + { + continue; + } + + /* Check the nat type is same, otherwise continue */ + if ((*it).second.nat_type != m_staticNatEntry[key].nat_type) + { + continue; + } + + /* Check the port_range is not present, otherwise continue */ + port_range = m_natPoolInfo[pool_name].port_range; + if (!port_range.empty() and (port_range != "NULL")) + { + continue; + } + + vector nat_ip = tokenize(m_natPoolInfo[pool_name].ip_range, range_specifier); + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool %s is not valid, skipping dynamic twice nat iptables rules addition for %s", pool_name.c_str(), (*it).first.c_str()); + continue; + } + + isRulesAdded = true; + setDynamicAllForwardOrAclbasedRules(ADD, (*it).second.pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, (*it).second.acl_name, + (*it).first); + break; + } + + if (!isRulesAdded) + { + SWSS_LOG_INFO("No Static-Dynamic Twice NAT iptables rules to add"); + } +} + +/* To add Static NAPT Iptables based on L3 Interface if all valid conditions are met */ +void NatMgr::addStaticNaptIptables(const string port) +{ + /* Example: + * Port is Ethernet1 + */ + + bool isRulesAdded = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping NAPT Iptables addition"); + return; + } + + /* Get all the Static NAPT entries */ + for (auto it = m_staticNaptEntry.begin(); it != m_staticNaptEntry.end(); it++) + { + /* Check interface is same as given Port, otherwise continue */ + if ((*it).second.interface != port) + { + continue; + } + + isRulesAdded = true; + + if ((*it).second.twice_nat_id.empty()) + { + /* Add the new Static Single NAPT iptables */ + SWSS_LOG_INFO("Adding the Static Single NAPT iptables for %s", (*it).first.c_str()); + addStaticSingleNaptIptables((*it).first); + } + else + { + /* Add the new Static Twice NAPT iptables */ + SWSS_LOG_INFO("Adding the Static Twice NAPT iptables for %s", (*it).first.c_str()); + addStaticTwiceNaptIptables((*it).first); + } + } + + if (!isRulesAdded) + { + SWSS_LOG_INFO("No Static NAPT iptables rules to add"); + } +} + +/* To add Static Single NAPT Iptables based on Static Key */ +void NatMgr::addStaticSingleNaptIptables(const string &key) +{ + /* Example: + * Entry is STATIC_NAPT|65.55.42.1|TCP|1024 and key is 65.55.42.1|TCP|1024 + */ + + string prototype = EMPTY_STRING, interface = m_staticNaptEntry[key].interface;; + vector keys = tokenize(key, config_db_key_delimiter); + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s Twice NAT Iptables addition ", key.c_str()); + return; + } + + if (keys[1] == to_upper(IP_PROTOCOL_UDP)) + { + prototype = IP_PROTOCOL_UDP; + } + else if (keys[1] == to_upper(IP_PROTOCOL_TCP)) + { + prototype = IP_PROTOCOL_TCP; + } + + /* Add Static NAPT iptables rule */ + if (!setStaticNaptIptablesRules(INSERT, interface, prototype, keys[0], keys[2], + m_staticNaptEntry[key].local_ip, m_staticNaptEntry[key].local_port, + m_staticNaptEntry[key].nat_type)) + { + SWSS_LOG_ERROR("Failed to add Static NAPT iptables rules for %s", key.c_str()); + } + else + { + SWSS_LOG_INFO("Added Static NAPT iptables rules for %s", key.c_str()); + } +} + +/* To add Static Twice NAPT Iptables based on Static Key if all valid conditions are met */ +void NatMgr::addStaticTwiceNaptIptables(const string &key) +{ + /* Example: + * Entry is STATIC_NAPT|65.55.42.1|TCP|1024 and key is 65.55.42.1|TCP|1024 + */ + + string interface = EMPTY_STRING, prototype = EMPTY_STRING; + vector keys = tokenize(key, config_db_key_delimiter); + string src, translated_src, dest, translated_dest; + string src_port, translated_src_port, dest_port, translated_dest_port; + bool isRulesAdded = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s Twice NAT entry addition to APPL_DB", key.c_str()); + return; + } + + if (keys[1] == to_upper(IP_PROTOCOL_UDP)) + { + prototype = IP_PROTOCOL_UDP; + } + else if (keys[1] == to_upper(IP_PROTOCOL_TCP)) + { + prototype = IP_PROTOCOL_TCP; + } + + /* Get all the Static NAPT entries */ + for (auto it = m_staticNaptEntry.begin(); it != m_staticNaptEntry.end(); it++) + { + vector entry_keys = tokenize((*it).first, config_db_key_delimiter); + + /* Check for other entries, otherwise continue */ + if ((*it).first == key) + { + continue; + } + + /* Check for both protocols are same, otherwise continue */ + if (entry_keys[1] != keys[1]) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNaptEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is added, otherwise continue */ + if (!(*it).second.twice_nat_added or !m_staticNaptEntry[key].twice_nat_added) + { + continue; + } + + /* Check the nat type is different, otherwise continue */ + if ((*it).second.nat_type == m_staticNaptEntry[key].nat_type) + { + continue; + } + + if (((*it).second.nat_type == DNAT_NAT_TYPE) and + (m_staticNaptEntry[key].nat_type == SNAT_NAT_TYPE)) + { + src = keys[0]; + src_port = keys[2]; + dest = entry_keys[0]; + dest_port = entry_keys[2]; + translated_src = m_staticNaptEntry[key].local_ip; + translated_src_port = m_staticNaptEntry[key].local_port; + translated_dest = (*it).second.local_ip; + translated_dest_port = (*it).second.local_port; + interface = (*it).second.interface; + } + else if (((*it).second.nat_type == SNAT_NAT_TYPE) and + (m_staticNaptEntry[key].nat_type == DNAT_NAT_TYPE)) + { + src = entry_keys[0]; + src_port = entry_keys[2]; + dest = keys[0]; + dest_port = keys[2]; + translated_src = (*it).second.local_ip; + translated_src_port = (*it).second.local_port; + translated_dest = m_staticNaptEntry[key].local_ip; + translated_dest_port = m_staticNaptEntry[key].local_port; + interface = m_staticNaptEntry[key].interface; + } + + /* Add Static NAPT iptables rule */ + if (!setStaticTwiceNaptIptablesRules(INSERT, interface, prototype, src, src_port, translated_src, translated_src_port, + dest, dest_port, translated_dest, translated_dest_port)) + { + SWSS_LOG_ERROR("Failed to add Static Twice NAT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + } + else + { + isRulesAdded = true; + SWSS_LOG_INFO("Added Static Twice NAT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + } + break; + } + + if (!isRulesAdded) + { + SWSS_LOG_INFO("No Static Twice NAPT iptables rules to add"); + } + else + { + return; + } + + /* Get all Binding Info */ + for (auto it = m_natBindingInfo.begin(); it != m_natBindingInfo.end(); it++) + { + string pool_name = (*it).second.pool_name; + string port_range = EMPTY_STRING; + + /* Check the pool is present in cache, otherwise continue */ + if (m_natPoolInfo.find(pool_name) == m_natPoolInfo.end()) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNaptEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is added, otherwise continue */ + if (!(*it).second.twice_nat_added or !m_staticNaptEntry[key].twice_nat_added) + { + continue; + } + + /* Check the nat type is same, otherwise continue */ + if ((*it).second.nat_type != m_staticNaptEntry[key].nat_type) + { + continue; + } + + /* Check the port_range is present, otherwise continue */ + port_range = m_natPoolInfo[pool_name].port_range; + if (port_range.empty() or (port_range == "NULL")) + { + continue; + } + + vector nat_ip = tokenize(m_natPoolInfo[pool_name].ip_range, range_specifier); + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool %s is not valid, skipping dynamic twice nat iptables rules addition for %s", pool_name.c_str(), (*it).first.c_str()); + continue; + } + + isRulesAdded = true; + setDynamicAllForwardOrAclbasedRules(ADD, (*it).second.pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, (*it).second.acl_name, + (*it).first); + break; + } + + if (!isRulesAdded) + { + SWSS_LOG_INFO("No Static-Dynamic Twice NAPT iptables rules to add"); + } +} + +/* To delete Static NAT Iptables based on L3 Interface if all valid conditions are met */ +void NatMgr::removeStaticNatIptables(const string port) +{ + /* Example: + * Port is Ethernet1 + */ + + bool isRulesDeleted = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping NAT iptables deletion"); + return; + } + + /* Get all the Static NAT entries */ + for (auto it = m_staticNatEntry.begin(); it != m_staticNatEntry.end(); it++) + { + /* Check interface is matching, otherwise continue */ + if ((*it).second.interface != port) + { + continue; + } + + isRulesDeleted = true; + + if ((*it).second.twice_nat_id.empty()) + { + /* Remove the Static Single NAT Iptables */ + SWSS_LOG_INFO("Deleting the Static Single NAT Iptables for %s", (*it).first.c_str()); + removeStaticSingleNatIptables((*it).first); + } + else if (((*it).second.twice_nat_added == true)) + { + /* Remove the Static Twice NAT Iptables */ + SWSS_LOG_INFO("Deleting the Static Twice NAT Iptables for %s", (*it).first.c_str()); + removeStaticTwiceNatIptables((*it).first); + } + } + + if (!isRulesDeleted) + { + SWSS_LOG_INFO("No Static NAT iptables rules to delete"); + } +} + +/* To delete Static Single NAT Iptables based on Static Key if all valid conditions are met */ +void NatMgr::removeStaticSingleNatIptables(const string &key) +{ + /* Example: + * Entry is STATIC_NAT|65.55.42.1 and key is 65.55.42.1 + */ + + string interface = m_staticNatEntry[key].interface; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s Static NAT iptables deletion", key.c_str()); + return; + } + + /* Remove Static NAT iptables rule */ + if (!setStaticNatIptablesRules(DELETE, interface, key, m_staticNatEntry[key].local_ip, m_staticNatEntry[key].nat_type)) + { + SWSS_LOG_ERROR("Failed to delete Static NAT iptables rules for %s", key.c_str()); + } + else + { + SWSS_LOG_INFO("Deleted Static NAT iptables rules for %s", key.c_str()); + } +} + +/* To delete Static Twice NAT Iptables based on Static Key if all valid conditions are met */ +void NatMgr::removeStaticTwiceNatIptables(const string &key) +{ + /* Example: + * Entry is STATIC_NAT|65.55.42.1 and key is 65.55.42.1 + */ + + string interface = EMPTY_STRING; + string src, translated_src, dest, translated_dest; + bool isRulesDeleted = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s Twice NAT iptables deletion", key.c_str()); + return; + } + + /* Get all the Static NAT entries */ + for (auto it = m_staticNatEntry.begin(); it != m_staticNatEntry.end(); it++) + { + /* Check for other entries, otherwise continue */ + if ((*it).first == key) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNatEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is added, otherwise continue */ + if ((!(*it).second.twice_nat_added) or (!m_staticNatEntry[key].twice_nat_added)) + { + continue; + } + + /* Check the nat type is different, otherwise continue */ + if ((*it).second.nat_type == m_staticNatEntry[key].nat_type) + { + continue; + } + + if (((*it).second.nat_type == DNAT_NAT_TYPE) and + (m_staticNatEntry[key].nat_type == SNAT_NAT_TYPE)) + { + src = key; + dest = (*it).first; + translated_src = m_staticNatEntry[key].local_ip; + translated_dest = (*it).second.local_ip; + interface = (*it).second.interface; + } + else if (((*it).second.nat_type == SNAT_NAT_TYPE) and + (m_staticNatEntry[key].nat_type == DNAT_NAT_TYPE)) + { + src = (*it).first; + dest = key; + translated_src = (*it).second.local_ip; + translated_dest = m_staticNatEntry[key].local_ip; + interface = m_staticNatEntry[key].interface; + } + + /* Delete Static NAT iptables rule */ + if (!setStaticTwiceNatIptablesRules(DELETE, interface, src, translated_src, dest, translated_dest)) + { + SWSS_LOG_ERROR("Failed to delete Static Twice NAT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + } + else + { + isRulesDeleted = true; + SWSS_LOG_INFO("Deleted Static Twice NAT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + } + break; + } + + if (!isRulesDeleted) + { + SWSS_LOG_INFO("No Static Twice NAT iptables rules to delete"); + } + else + { + return; + } + + /* Get all Binding Info */ + for (auto it = m_natBindingInfo.begin(); it != m_natBindingInfo.end(); it++) + { + string port_range = EMPTY_STRING; + string pool_name = (*it).second.pool_name; + string acls_name = (*it).second.acl_name; + + /* Check the pool is present in cache, otherwise continue */ + if (m_natPoolInfo.find(pool_name) == m_natPoolInfo.end()) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNatEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is added, otherwise continue */ + if ((!(*it).second.twice_nat_added) or (!m_staticNatEntry[key].twice_nat_added)) + { + continue; + } + + /* Check the nat type is same, otherwise continue */ + if ((*it).second.nat_type != m_staticNatEntry[key].nat_type) + { + continue; + } + + /* Check the port_range is not present, otherwise continue */ + port_range = m_natPoolInfo[pool_name].port_range; + if (!port_range.empty() and (port_range != "NULL")) + { + continue; + } + + /* Check the key is matching, otherwise continue */ + if (m_staticNatEntry[key].binding_key != (*it).first) + { + continue; + } + + vector nat_ip = tokenize(m_natPoolInfo[pool_name].ip_range, range_specifier); + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool %s is not valid, skipping dynamic twice nat iptables rules deletion for %s", pool_name.c_str(), (*it).first.c_str()); + continue; + } + + /* Delete Dynamic rules */ + isRulesDeleted = true; + setDynamicAllForwardOrAclbasedRules(DELETE, (*it).second.pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, acls_name, (*it).first); + break; + } + + if (!isRulesDeleted) + { + SWSS_LOG_INFO("No Static-Dynamic Twice NAT iptables rules to delete"); + } +} + +/* To delete Static NAPT iptables based on L3 Interface if all valid conditions are met */ +void NatMgr::removeStaticNaptIptables(const string port) +{ + /* Example: + * Port is Ethernet1 + */ + + bool isRulesDeleted = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping NAPT iptables deletion"); + return; + } + + /* Get all the Static NAPT entries */ + for (auto it = m_staticNaptEntry.begin(); it != m_staticNaptEntry.end(); it++) + { + /* Check interface is matching, otherwise continue */ + if ((*it).second.interface != port) + { + continue; + } + + isRulesDeleted = true; + + if ((*it).second.twice_nat_id.empty()) + { + /* Remove the Static Single NAPT Iptables */ + SWSS_LOG_INFO("Deleting the Static Single NAPT Iptables for %s", (*it).first.c_str()); + removeStaticSingleNaptIptables((*it).first); + } + else if (((*it).second.twice_nat_added == true)) + { + /* Remove the Static Twice NAPT Iptables */ + SWSS_LOG_INFO("Deleting the Static Twice NAPT iptables for %s", (*it).first.c_str()); + removeStaticTwiceNaptIptables((*it).first); + } + } + + if (!isRulesDeleted) + { + SWSS_LOG_INFO("No Static NAPT iptables rules to delete"); + } +} + +/* To delete Static Single NAPT Iptables based on Static Key if all valid conditions are met */ +void NatMgr::removeStaticSingleNaptIptables(const string &key) +{ + /* Example: + * Entry is STATIC_NAPT|65.55.42.1|TCP|1024 and key is 65.55.42.1|TCP|1024 + */ + + string interface = EMPTY_STRING, prototype = EMPTY_STRING; + vector keys = tokenize(key, config_db_key_delimiter); + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s Single NAPT iptables deletion", key.c_str()); + return; + } + + if (keys[1] == to_upper(IP_PROTOCOL_UDP)) + { + prototype = IP_PROTOCOL_UDP; + } + else if (keys[1] == to_upper(IP_PROTOCOL_TCP)) + { + prototype = IP_PROTOCOL_TCP; + } + + interface = m_staticNaptEntry[key].interface; + + /* Remove Static NAPT iptables rule */ + if (!setStaticNaptIptablesRules(DELETE, interface, prototype, keys[0], keys[2], + m_staticNaptEntry[key].local_ip, m_staticNaptEntry[key].local_port, + m_staticNaptEntry[key].nat_type)) + { + SWSS_LOG_ERROR("Failed to delete Static NAPT iptables rules for %s", key.c_str()); + } + else + { + SWSS_LOG_INFO("Deleted Static NAPT iptables rules for %s", key.c_str()); + } +} + +/* To delete Static Twice NAPT Iptables based on Static Key if all valid conditions are met */ +void NatMgr::removeStaticTwiceNaptIptables(const string &key) +{ + /* Example: + * Entry is STATIC_NAPT|65.55.42.1|TCP|1024 and key is 65.55.42.1|TCP|1024 + */ + + string interface = EMPTY_STRING, prototype = EMPTY_STRING; + vector keys = tokenize(key, config_db_key_delimiter); + string src, translated_src, dest, translated_dest; + string src_port, translated_src_port, dest_port, translated_dest_port; + bool isRulesDeleted = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping %s Twice NAPT iptables deletion", key.c_str()); + return; + } + + if (keys[1] == to_upper(IP_PROTOCOL_UDP)) + { + prototype = IP_PROTOCOL_UDP; + } + else if (keys[1] == to_upper(IP_PROTOCOL_TCP)) + { + prototype = IP_PROTOCOL_TCP; + } + + /* Get all the Static NAPT entries */ + for (auto it = m_staticNaptEntry.begin(); it != m_staticNaptEntry.end(); it++) + { + vector entry_keys = tokenize((*it).first, config_db_key_delimiter); + + /* Check for other entries, otherwise continue */ + if ((*it).first == key) + { + continue; + } + + /* Check for both protocols are same, otherwise continue */ + if (entry_keys[1] != keys[1]) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNaptEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is added, otherwise continue */ + if ((!(*it).second.twice_nat_added) or (!m_staticNaptEntry[key].twice_nat_added)) + { + continue; + } + + /* Check the nat type is different, otherwise continue */ + if ((*it).second.nat_type == m_staticNaptEntry[key].nat_type) + { + continue; + } + + if (((*it).second.nat_type == DNAT_NAT_TYPE) and + (m_staticNaptEntry[key].nat_type == SNAT_NAT_TYPE)) + { + src = keys[0]; + src_port = keys[2]; + dest = entry_keys[0]; + dest_port = entry_keys[2]; + translated_src = m_staticNaptEntry[key].local_ip; + translated_src_port = m_staticNaptEntry[key].local_port; + translated_dest = (*it).second.local_ip; + translated_dest_port = (*it).second.local_port; + interface = (*it).second.interface; + } + else if (((*it).second.nat_type == SNAT_NAT_TYPE) and + (m_staticNaptEntry[key].nat_type == DNAT_NAT_TYPE)) + { + src = entry_keys[0]; + src_port = entry_keys[2]; + dest = keys[0]; + dest_port = keys[2]; + translated_src = (*it).second.local_ip; + translated_src_port = (*it).second.local_port; + translated_dest = m_staticNaptEntry[key].local_ip; + translated_dest_port = m_staticNaptEntry[key].local_port; + interface = m_staticNaptEntry[key].interface; + } + + /* Delete Static NAPT iptables rule */ + if (!setStaticTwiceNaptIptablesRules(DELETE, interface, prototype, src, src_port, translated_src, translated_src_port, + dest, dest_port, translated_dest, translated_dest_port)) + { + SWSS_LOG_ERROR("Failed to delete Static Twice NAPT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + } + else + { + isRulesDeleted = true; + SWSS_LOG_INFO("Deleted Static Twice NAPT iptables rules for %s and %s", key.c_str(), (*it).first.c_str()); + } + break; + } + + if (!isRulesDeleted) + { + SWSS_LOG_INFO("No Static Twice NAPT iptables rules to delete"); + } + else + { + return; + } + + /* Get all Binding Info */ + for (auto it = m_natBindingInfo.begin(); it != m_natBindingInfo.end(); it++) + { + string port_range = EMPTY_STRING; + string pool_name = (*it).second.pool_name; + string acls_name = (*it).second.acl_name; + + /* Check the pool is present in cache, otherwise continue */ + if (m_natPoolInfo.find(pool_name) == m_natPoolInfo.end()) + { + continue; + } + + /* Check the twice_nat_id is matched, otherwise continue */ + if ((*it).second.twice_nat_id != m_staticNaptEntry[key].twice_nat_id) + { + continue; + } + + /* Check the twice NAT is added, otherwise continue */ + if ((!(*it).second.twice_nat_added) or (!m_staticNaptEntry[key].twice_nat_added)) + { + continue; + } + + /* Check the nat type is same, otherwise continue */ + if ((*it).second.nat_type != m_staticNaptEntry[key].nat_type) + { + continue; + } + + /* Check the port_range is present, otherwise continue */ + port_range = m_natPoolInfo[pool_name].port_range; + if (port_range.empty() or (port_range == "NULL")) + { + continue; + } + + /* Check the key is matching, otherwise continue */ + if (m_staticNaptEntry[key].binding_key != (*it).first) + { + continue; + } + vector nat_ip = tokenize(m_natPoolInfo[pool_name].ip_range, range_specifier); + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool %s is not valid, skipping dynamic twice nat iptables rules deletion for %s", pool_name.c_str(), (*it).first.c_str()); + continue; + } + + /* Delete Dynamic rules */ + isRulesDeleted = true; + setDynamicAllForwardOrAclbasedRules(DELETE, (*it).second.pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, acls_name, (*it).first); + break; + } + + if (!isRulesDeleted) + { + SWSS_LOG_INFO("No Static-Dynamic Twice NAPT iptables rules to delete"); + } +} + +/* To Add or Delete Dynamic NAT/NAPT iptables rules if all valid conditions are met */ +void NatMgr::setDynamicAllForwardOrAclbasedRules(const string &opCmd, const string &pool_interface, const string &ip_range, + const string &port_range, const string &aclsName, + const string &dynamicKey) +{ + vector access_list; + bool setAllForwardRules = true; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping Dynamic Iptables setting"); + return; + } + + /* Add/Delete ACLs based only when aclName string is not empty */ + if (((opCmd == DELETE) and (m_natBindingInfo[dynamicKey].acl_interface != NONE_STRING) and (aclsName != EMPTY_STRING)) or + ((opCmd == ADD) and (aclsName != EMPTY_STRING))) + { + access_list = tokenize(aclsName, comma); + + /* Loop for all ACL Ids */ + for (string aclId : access_list) + { + /* Check the Acl-id is enabled otherwise continue */ + if (m_natAclTableInfo.find(aclId) == m_natAclTableInfo.end()) + { + SWSS_LOG_INFO("Acl-id %s is not yet enabled, skipping it", aclId.c_str()); + continue; + } + + SWSS_LOG_INFO("Acl-id %s is enabled", aclId.c_str()); + + bool isRuleSet = false; + + /* Get all ACL Rule Info */ + for (auto it = m_natAclRuleInfo.begin(); it != m_natAclRuleInfo.end(); it++) + { + vector aclRuleKeys = tokenize((*it).first, config_db_key_delimiter); + + /* Check aclId is matching, otherwise continue */ + if (aclRuleKeys[0] != aclId) + { + continue; + } + + SWSS_LOG_INFO("Rule-id %s is mapped to Acl-id %s", aclRuleKeys[1].c_str(), aclId.c_str()); + + /* Set pool ip to APPL_DB */ + setNaptPoolIpTable(opCmd, ip_range, port_range); + + /* Set dynamic iptables rule with acls*/ + if (!setDynamicNatIptablesRulesWithAcl(opCmd, pool_interface, ip_range, port_range, (*it).second, m_natBindingInfo[dynamicKey].static_key)) + { + SWSS_LOG_ERROR("Failed to %s dynamic iptables acl rules for Rule id %s for Table %s", opCmd == ADD ? "add" : "delete", + aclRuleKeys[1].c_str(), aclId.c_str()); + } + else + { + isRuleSet = true; + SWSS_LOG_INFO("%s dynamic iptables acl rules for Rule id %s for Table %s", opCmd == ADD ? "Added" : "Deleted", + aclRuleKeys[1].c_str(), aclId.c_str()); + } + + setAllForwardRules = false; + } + + /* If rule is set, save the port in the binding cache */ + if (isRuleSet and (opCmd == ADD)) + { + if (m_natBindingInfo[dynamicKey].acl_interface == NONE_STRING) + { + m_natBindingInfo[dynamicKey].acl_interface = m_natAclTableInfo[aclId]; + } + else + { + m_natBindingInfo[dynamicKey].acl_interface += (comma + m_natAclTableInfo[aclId]); + } + } + } + + /* After deletion, set acl_interface to None */ + if (opCmd == DELETE) + { + m_natBindingInfo[dynamicKey].acl_interface == NONE_STRING; + } + } + + /* To set all forward rules */ + if (setAllForwardRules) + { + /* Set pool ip to APPL_DB */ + setNaptPoolIpTable(opCmd, ip_range, port_range); + + /* Set dynamic iptables rule without acls*/ + if (!setDynamicNatIptablesRulesWithoutAcl(opCmd, pool_interface, ip_range, port_range, m_natBindingInfo[dynamicKey].static_key)) + { + SWSS_LOG_ERROR("Failed to %s dynamic iptables rules for %s", opCmd == ADD ? "add" : "delete", dynamicKey.c_str()); + } + else + { + SWSS_LOG_INFO("%s dynamic iptables rules for %s", opCmd == ADD ? "Added" : "Deleted", dynamicKey.c_str()); + } + } +} + +/* To Add Dynamic NAT rules based on Binding Key if all valid conditions are met */ +void NatMgr::addDynamicNatRule(const string &key) +{ + /* Example: + * Entry is NAT_BINDINGS|BindingName and key is BindingName + */ + + string pool_interface = EMPTY_STRING; + string pool_name = m_natBindingInfo[key].pool_name; + string acls_name = m_natBindingInfo[key].acl_name; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping dynamic nat rules addition for %s", key.c_str()); + return; + } + + /* Check the pool is present in cache, otherwise return */ + if (m_natPoolInfo.find(pool_name) == m_natPoolInfo.end()) + { + SWSS_LOG_INFO("Pool %s is not yet enabled, skipping dynamic nat rules addition for %s", pool_name.c_str(), key.c_str()); + return; + } + + vector nat_ip = tokenize(m_natPoolInfo[pool_name].ip_range, range_specifier); + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool %s is not valid, skipping dynamic nat rules addition for %s", pool_name.c_str(), key.c_str()); + return; + } + + /* Get the matching Ip interface otherwise return */ + if (!getIpEnabledIntf(nat_ip[0], pool_interface)) + { + SWSS_LOG_INFO("L3 Interface is not yet enabled for %s, skipping dynamic nat rules addition", key.c_str()); + return; + } + + m_natBindingInfo[key].pool_interface = pool_interface; + + if (m_natBindingInfo[key].twice_nat_id.empty()) + { + /* Add Dynamic rules for Single NAT */ + SWSS_LOG_INFO("Adding dynamic single nat rules for %s", key.c_str()); + setDynamicAllForwardOrAclbasedRules(ADD, pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, acls_name, key); + } + else + { + /* Add Dynamic rules for Twice NAT */ + SWSS_LOG_INFO("Adding dynamic twice nat rules for %s", key.c_str()); + addDynamicTwiceNatRule(key); + } +} + +/* To delete Dynamic NAT/NAPT iptables rules based on Binding Key if all valid conditions are met */ +void NatMgr::removeDynamicNatRule(const string &key) +{ + /* Example: + * Entry is NAT_BINDINGS|BindingName and key is BindingName + */ + + string pool_interface = m_natBindingInfo[key].pool_interface; + string pool_name = m_natBindingInfo[key].pool_name; + string acls_name = m_natBindingInfo[key].acl_name; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping dynamic nat rules deletion for %s", key.c_str()); + return; + } + + /* Check the pool is present in cache otherwise return */ + if (m_natPoolInfo.find(pool_name) == m_natPoolInfo.end()) + { + SWSS_LOG_INFO("Pool %s is not yet enabled, skipping dynamic nat rules deletion for %s", pool_name.c_str(), key.c_str()); + return; + } + + /* Check pool interface is valid, otherwise return */ + if (m_natBindingInfo[key].pool_interface == NONE_STRING) + { + SWSS_LOG_INFO("Pool interface is not enabled, skipping dynamic nat rules deletion for %s", key.c_str()); + return; + } + + vector nat_ip = tokenize(m_natPoolInfo[pool_name].ip_range, range_specifier); + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool %s is not valid, skipping dynamic nat rules deletion for %s", pool_name.c_str(), key.c_str()); + return; + } + + if (m_natBindingInfo[key].twice_nat_id.empty()) + { + /* Delete Dynamic rules for Single NAT */ + SWSS_LOG_INFO("Deleting dynamic single nat rules for %s", key.c_str()); + setDynamicAllForwardOrAclbasedRules(DELETE, pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, acls_name, key); + } + else + { + /* Delete Dynamic rules for Twice NAT */ + SWSS_LOG_INFO("Deleting dynamic twice nat rules for %s", key.c_str()); + deleteDynamicTwiceNatRule(key); + } + + m_natBindingInfo[key].pool_interface = NONE_STRING; + m_natBindingInfo[key].acl_interface = NONE_STRING; +} + +/* To Add Dynamic NAT/NAPT iptables rules based on ACLs if all valid conditions are met */ +void NatMgr::addDynamicNatRuleByAcl(const string &aclKey, bool isRuleId) +{ + /* Example: + * Key : (AclId | AclRuleId) or (AclId) + */ + + string aclTableId, aclRuleId; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping dynamic iptables rules addition for %s", aclKey.c_str()); + return; + } + + /* Get the aclTable and aclRule */ + if (isRuleId == true) + { + vector keys = tokenize(aclKey, config_db_key_delimiter); + aclTableId = keys[0]; + aclRuleId = keys[1]; + } + else + { + aclTableId = aclKey; + } + + /* Get all binding Info */ + for (auto it = m_natBindingInfo.begin(); it != m_natBindingInfo.end(); it++) + { + string pool_name = (*it).second.pool_name; + string acls_name = (*it).second.acl_name; + string poolInterface, aclInterface; + string port_range, ip_range; + bool isRuleSet = false; + + /* Check the pool is present in cache, otherwise continue */ + if (m_natPoolInfo.find(pool_name) == m_natPoolInfo.end()) + { + continue; + } + + /* Check if pool interface is valid, otherwise continue */ + if ((*it).second.pool_interface == NONE_STRING) + { + continue; + } + + /* Check the ACL Ids are configured, otherwise continue */ + if ((*it).second.acl_name == EMPTY_STRING) + { + continue; + } + + /* Check if the twice nat id is configured and not added */ + if ((!(*it).second.twice_nat_id.empty()) and ((*it).second.twice_nat_added)) + { + continue; + } + + poolInterface = (*it).second.pool_interface; + aclInterface = (*it).second.acl_interface; + ip_range = m_natPoolInfo[pool_name].ip_range; + port_range = m_natPoolInfo[pool_name].port_range; + + vector nat_ip = tokenize(m_natPoolInfo[pool_name].ip_range, range_specifier); + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool %s is not valid, skipping dynamic iptables rules addition for %s", pool_name.c_str(), (*it).first.c_str()); + continue; + } + + vector access_list = tokenize(acls_name, comma); + + /* Get all aclIds */ + for (string aclId : access_list) + { + /* Check the Acl-id is matching with given one, otherwise continue */ + if (aclTableId != aclId) + { + continue; + } + + /* isRuleId is true means we have both AclTableId and AclRuleId */ + if (isRuleId == true) + { + /* Check the Acl-id is enabled otherwise continue */ + if (m_natAclTableInfo.find(aclTableId) == m_natAclTableInfo.end()) + { + SWSS_LOG_INFO("Acl-id %s is not yet enabled", aclTableId.c_str()); + return; + } + + /* aclInterface is None means delete all forward rule first, otherwise nothing */ + if (aclInterface == NONE_STRING) + { + /* Set pool ip to APPL_DB */ + setNaptPoolIpTable(DELETE, ip_range, port_range); + + /* Set dynamic iptables rule without acl */ + if (!setDynamicNatIptablesRulesWithoutAcl(DELETE, poolInterface, ip_range, port_range, (*it).second.static_key)) + { + SWSS_LOG_ERROR("Failed to remove dynamic iptables rules for %s", aclKey.c_str()); + } + else + { + SWSS_LOG_INFO("Deleted dynamic iptables rules for %s", aclKey.c_str()); + } + + (*it).second.acl_interface = m_natAclTableInfo[aclTableId]; + } + else + { + (*it).second.acl_interface = comma + m_natAclTableInfo[aclTableId]; + } + + /* Set pool ip to APPL_DB */ + setNaptPoolIpTable(ADD, ip_range, port_range); + + /* Set dynamic iptables rule with acls*/ + if (!setDynamicNatIptablesRulesWithAcl(ADD, poolInterface, ip_range, port_range, m_natAclRuleInfo[aclKey], (*it).second.static_key)) + { + SWSS_LOG_ERROR("Failed to add dynamic iptables acl rules for Rule id %s for Table %s", aclRuleId.c_str(), aclTableId.c_str()); + } + else + { + SWSS_LOG_INFO("Added dynamic iptables acl rules for Rule id %s for Table %s", aclRuleId.c_str(), aclTableId.c_str()); + } + return; + } + else + { + /* Get all AclRule Info */ + for (auto it2 = m_natAclRuleInfo.begin(); it2 != m_natAclRuleInfo.end(); it2++) + { + vector aclRuleKeys = tokenize((*it2).first, config_db_key_delimiter); + + /* Check the matching aclTableId, otherwise continue */ + if (aclRuleKeys[0] != aclTableId) + { + continue; + } + + SWSS_LOG_INFO("Rule-id %s is mapped to Acl-id %s", aclRuleKeys[1].c_str(), aclTableId.c_str()); + + /* Set pool ip to APPL_DB */ + setNaptPoolIpTable(ADD, ip_range, port_range); + + /* Add dynamic iptables rule with acls */ + if (!setDynamicNatIptablesRulesWithAcl(ADD, poolInterface, ip_range, port_range, (*it2).second, (*it).second.static_key)) + { + SWSS_LOG_ERROR("Failed to add dynamic iptables acl rules for Rule id %s for Table %s", aclRuleKeys[1].c_str(), aclTableId.c_str()); + } + else + { + isRuleSet = true; + SWSS_LOG_INFO("Added dynamic iptables acl rules for Rule id %s for Table %s", aclRuleKeys[1].c_str(), aclTableId.c_str()); + } + } + + /* aclInterface is None means have to delete the All forward rules */ + if ((aclInterface == NONE_STRING) and (isRuleSet == true)) + { + /* Set pool ip to APPL_DB */ + setNaptPoolIpTable(DELETE, ip_range, port_range); + + /* Delete dynamic iptables rule without acl */ + if (!setDynamicNatIptablesRulesWithoutAcl(DELETE, poolInterface, ip_range, port_range, (*it).second.static_key)) + { + SWSS_LOG_ERROR("Failed to remove dynamic iptables rules for %s", aclKey.c_str()); + } + else + { + SWSS_LOG_INFO("Deleted dynamic iptables rules for %s", aclKey.c_str()); + } + + (*it).second.acl_interface = m_natAclTableInfo[aclTableId]; + } + return; + } + } + } +} + +/* To Delete Dynamic NAT/NAPT iptables rules based on ACLs if all valid conditions are met */ +void NatMgr::removeDynamicNatRuleByAcl(const string &aclKey, bool isRuleId) +{ + /* Example: + * Key : (AclId | AclRuleId) or (AclId) + */ + + string aclTableId, aclRuleId; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping dynamic nat rules deletion for %s", aclKey.c_str()); + return; + } + + /* Get the aclTable and aclRule */ + if (isRuleId == true) + { + vector keys = tokenize(aclKey, config_db_key_delimiter); + aclTableId = keys[0]; + aclRuleId = keys[1]; + } + else + { + aclTableId = aclKey; + } + + /* Get all Binding Info */ + for (auto it = m_natBindingInfo.begin(); it != m_natBindingInfo.end(); it++) + { + string pool_name = (*it).second.pool_name; + string acls_name = (*it).second.acl_name; + string poolInterface, aclInterface; + string port_range, ip_range; + bool isRuleSet = false, isRulePresent = false; + + /* Check the pool is present in cache, otherwise continue */ + if (m_natPoolInfo.find(pool_name) == m_natPoolInfo.end()) + { + continue; + } + + /* Check if pool interface is valid, otherwise continue */ + if ((*it).second.pool_interface == NONE_STRING) + { + continue; + } + + /* Check the ACL Ids are configured, otherwise continue */ + if ((*it).second.acl_name == EMPTY_STRING) + { + continue; + } + + /* Check if the twice nat id is added */ + if ((!(*it).second.twice_nat_id.empty()) and (!(*it).second.twice_nat_added)) + { + continue; + } + + poolInterface = (*it).second.pool_interface; + aclInterface = (*it).second.acl_interface; + ip_range = m_natPoolInfo[pool_name].ip_range; + port_range = m_natPoolInfo[pool_name].port_range; + + vector nat_ip = tokenize(m_natPoolInfo[pool_name].ip_range, range_specifier); + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool %s is not valid, skipping dynamic nat rules deletion for %s", pool_name.c_str(), (*it).first.c_str()); + continue; + } + + vector access_list = tokenize(acls_name, ','); + + /* Get all aclIds */ + for (string aclId : access_list) + { + /* Check the aclId is matching with given one, otherwise continue */ + if (aclTableId != aclId) + { + continue; + } + + /* isRuleId is true means we have both AclTableId and AclRuleId */ + if (isRuleId == true) + { + /* Check the Acl-id is enabled otherwise continue */ + if (m_natAclTableInfo.find(aclTableId) == m_natAclTableInfo.end()) + { + SWSS_LOG_INFO("Acl-id %s is not yet enabled", aclTableId.c_str()); + return; + } + + /* Set pool ip to APPL_DB */ + setNaptPoolIpTable(DELETE, ip_range, port_range); + + /* Delete dynamic iptables rule with acls*/ + if (!setDynamicNatIptablesRulesWithAcl(DELETE, poolInterface, ip_range, port_range, m_natAclRuleInfo[aclKey], (*it).second.static_key)) + { + SWSS_LOG_ERROR("Failed to delete dynamic iptables acl rules for Rule id %s for Table %s", aclRuleId.c_str(), aclTableId.c_str()); + } + else + { + SWSS_LOG_INFO("Deleted dynamic iptables acl rules for Rule id %s for Table %s", aclRuleId.c_str(), aclTableId.c_str()); + } + + /* Check any other rule matching in same Table-Id */ + for (auto it = m_natAclRuleInfo.begin(); it != m_natAclRuleInfo.end(); it++) + { + vector aclRuleKeys = tokenize((*it).first, config_db_key_delimiter); + + /* Check the matching aclTableId otherwise continue */ + if (aclRuleKeys[0] != aclTableId) + { + continue; + } + + SWSS_LOG_INFO("Rule-id %s is mapped to Acl-id %s", aclRuleKeys[1].c_str(), aclTableId.c_str()); + if (aclRuleKeys[1] == aclRuleId) + { + continue; + } + isRulePresent = true; + } + + if (isRulePresent == false) + { + /* Set pool ip to APPL_DB */ + setNaptPoolIpTable(ADD, ip_range, port_range); + + /* Set dynamic iptables rule without acl */ + if (!setDynamicNatIptablesRulesWithoutAcl(ADD, poolInterface, ip_range, port_range, (*it).second.static_key)) + { + SWSS_LOG_ERROR("Failed to add dynamic iptables rules for %s", aclKey.c_str()); + } + else + { + SWSS_LOG_INFO("Added dynamic iptables rules for %s", aclKey.c_str()); + } + + (*it).second.acl_interface = NONE_STRING; + } + return; + } + else + { + /* Get all AclRule Info */ + for (auto it2 = m_natAclRuleInfo.begin(); it2 != m_natAclRuleInfo.end(); it2++) + { + vector aclRuleKeys = tokenize((*it2).first, config_db_key_delimiter); + + /* Check the matching aclTableId, otherwise continue */ + if (aclRuleKeys[0] != aclTableId) + { + continue; + } + + SWSS_LOG_INFO("Rule-id %s is mapped to Acl-id %s", aclRuleKeys[1].c_str(), aclTableId.c_str()); + + /* Set pool ip to APPL_DB */ + setNaptPoolIpTable(DELETE, ip_range, port_range); + + /* Delete dynamic iptables rule with acls */ + if (!setDynamicNatIptablesRulesWithAcl(DELETE, poolInterface, ip_range, port_range, (*it2).second, (*it).second.static_key)) + { + SWSS_LOG_ERROR("Failed to delete dynamic iptables acl rules for Rule id %s for Table %s", aclRuleKeys[1].c_str(), aclTableId.c_str()); + } + else + { + isRuleSet = true; + SWSS_LOG_INFO("Deleted dynamic iptables acl rules for Rule id %s for Table %s", aclRuleKeys[1].c_str(), aclTableId.c_str()); + } + } + + /* If aclInterface is not None, add dynamic all forward rules */ + if ((aclInterface != NONE_STRING) and (isRuleSet == true)) + { + /* Set pool ip to APPL_DB */ + setNaptPoolIpTable(ADD, ip_range, port_range); + + /* Add dynamic iptables rule without acl */ + if (!setDynamicNatIptablesRulesWithoutAcl(ADD, poolInterface, ip_range, port_range, (*it).second.static_key)) + { + SWSS_LOG_ERROR("Failed to add dynamic iptables rules for %s", aclKey.c_str()); + } + else + { + SWSS_LOG_INFO("Added dynamic iptables rules for %s", aclKey.c_str()); + } + + (*it).second.acl_interface = NONE_STRING; + } + return; + } + } + } +} + +/* To Add Dynamic NAT iptables rules based on L3 Interface if all valid conditions are met */ +void NatMgr::addDynamicNatRules(const string port, const string ipPrefix) +{ + /* Example: + * Port is Ethernet1 and ipPrefix is 10.0.0.1/24 + */ + + bool isRuleAdded = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping dynamic nat rules addition"); + return; + } + + /* Get all Binding Info */ + for (auto it = m_natBindingInfo.begin(); it != m_natBindingInfo.end(); it++) + { + string pool_interface = EMPTY_STRING; + string pool_name = (*it).second.pool_name; + + /* Check the pool interface is valid, otherwise continue */ + if ((*it).second.pool_interface != NONE_STRING) + { + continue; + } + + /* Check the pool is present in cache, otherwise continue */ + if (m_natPoolInfo.find(pool_name) == m_natPoolInfo.end()) + { + continue; + } + + /* Check if the twice nat id is added */ + if ((!(*it).second.twice_nat_id.empty()) and ((*it).second.twice_nat_added)) + { + continue; + } + + vector nat_ip = tokenize(m_natPoolInfo[pool_name].ip_range, range_specifier); + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool %s is not valid, skipping dynamic nat rules addition for %s", pool_name.c_str(), (*it).first.c_str()); + continue; + } + + if ((port == NONE_STRING) and (ipPrefix == NONE_STRING)) + { + /* Get the matching Ip interface otherwise return */ + if (!getIpEnabledIntf(nat_ip[0], pool_interface)) + { + continue; + } + + (*it).second.pool_interface = pool_interface; + } + else if ((port != NONE_STRING) and (ipPrefix == NONE_STRING)) + { + /* Get the matching Ip interface otherwise return */ + if (!getIpEnabledIntf(nat_ip[0], pool_interface)) + { + continue; + } + + if (pool_interface != port) + { + continue; + } + (*it).second.pool_interface = pool_interface; + } + else + { + /* Check global ip address is matching otherwise continue */ + if (isGlobalIpMatching(ipPrefix, nat_ip[0]) == false) + { + continue; + } + + pool_interface = port; + (*it).second.pool_interface = port; + } + + if ((*it).second.twice_nat_id.empty()) + { + /* Add Dynamic rules for Single NAT */ + SWSS_LOG_INFO("Adding dynamic single nat rules for %s", (*it).first.c_str()); + setDynamicAllForwardOrAclbasedRules(ADD, pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, (*it).second.acl_name, + (*it).first); + } + else + { + /* Add Dynamic rules for Twice NAT */ + SWSS_LOG_INFO("Adding dynamic twice nat rules for %s", (*it).first.c_str()); + addDynamicTwiceNatRule((*it).first); + } + + if ((port != NONE_STRING) and (ipPrefix != NONE_STRING)) + { + /* Send notification to Orchagent to flush the conntrack entries */ + std::vector entry; + flushNotifier->send("ENTRIES", "ALL", entry); + SWSS_LOG_WARN("Added interface is part of Binded NAT Pool range, so it is clearing all the nat translations"); + } + + isRuleAdded = true; + } + + if (!isRuleAdded) + { + SWSS_LOG_INFO("No dynamic nat rules to add"); + } +} + +/* To delete Dynamic NAT/NAPT iptables rules based on L3 Interface if all valid conditions are met */ +void NatMgr::removeDynamicNatRules(const string port, const string ipPrefix) +{ + /* Example: + * Port is Ethernet1 and ipPrefix is 10.0.0.1/24 + */ + + bool isRuleDeleted = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping dynamic nat rules deletion"); + return; + } + + /* Get all Binding Info */ + for (auto it = m_natBindingInfo.begin(); it != m_natBindingInfo.end(); it++) + { + string pool_interface = EMPTY_STRING; + string pool_name = m_natBindingInfo[(*it).first].pool_name; + vector nat_ip = tokenize(m_natPoolInfo[pool_name].ip_range, range_specifier); + + /* Check interface is matching, otherwise continue */ + if ((*it).second.pool_interface == NONE_STRING) + { + continue; + } + + /* Check the pool is present in cache, otherwise continue */ + if (m_natPoolInfo.find(pool_name) == m_natPoolInfo.end()) + { + continue; + } + + /* Check if the twice nat id is added */ + if ((!(*it).second.twice_nat_id.empty()) and (!(*it).second.twice_nat_added)) + { + (*it).second.pool_interface = NONE_STRING; + continue; + } + + /* Check the pool is valid */ + if (nat_ip.empty()) + { + SWSS_LOG_INFO("NAT pool %s is not valid, skipping dynamic rules deletion for %s", pool_name.c_str(), (*it).first.c_str()); + continue; + } + + if ((port == NONE_STRING) and (ipPrefix == NONE_STRING)) + { + pool_interface = (*it).second.pool_interface; + } + else if ((port != NONE_STRING) and (ipPrefix == NONE_STRING)) + { + /* Check interface is matching, otherwise continue */ + if ((*it).second.pool_interface != port) + { + continue; + } + + pool_interface = port; + } + else + { + /* Check interface is matching, otherwise continue */ + if ((*it).second.pool_interface != port) + { + continue; + } + + /* Check the global ip address is matching otherwise continue */ + if (isGlobalIpMatching(ipPrefix, nat_ip[0]) == false) + { + continue; + } + + pool_interface = port; + } + + if ((*it).second.twice_nat_id.empty()) + { + /* Delete Dynamic rules for Single NAT */ + SWSS_LOG_INFO("Deleting dynamic single nat rules for %s", (*it).first.c_str()); + setDynamicAllForwardOrAclbasedRules(DELETE, pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, (*it).second.acl_name, + (*it).first); + } + else + { + /* Delete Dynamic rules for Twice NAT */ + SWSS_LOG_INFO("Deleting dynamic twice nat rules for %s", (*it).first.c_str()); + deleteDynamicTwiceNatRule((*it).first); + } + + if ((port != NONE_STRING) and (ipPrefix != NONE_STRING)) + { + deleteConntrackDynamicEntries(m_natPoolInfo[pool_name].ip_range); + } + + (*it).second.pool_interface = NONE_STRING; + isRuleDeleted = true; + } + + if (!isRuleDeleted) + { + SWSS_LOG_INFO("No dynamic nat rules to delete"); + } +} + +/* To Add Dynamic Twice NAT/NAPT iptables rules based on Binding Key if all valid conditions are met */ +void NatMgr::addDynamicTwiceNatRule(const string &key) +{ + /* Example: + * Entry is NAT_BINDINGS|BindingName and key is BindingName + */ + + string port_range = EMPTY_STRING; + string pool_name = m_natBindingInfo[key].pool_name; + string acls_name = m_natBindingInfo[key].acl_name; + bool isRuleAdded = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping dynamic twice nat rules addition for %s", key.c_str()); + return; + } + + /* Check the twice NAT is added, otherwise return */ + if ((m_natBindingInfo[key].twice_nat_added) or (!m_natBindingInfo[key].static_key.empty())) + { + SWSS_LOG_INFO("Twice NAT is already added, skipping dynamic twice nat rules addition for %s", key.c_str()); + return; + } + + port_range = m_natPoolInfo[pool_name].port_range; + + if (!port_range.empty() and (port_range != "NULL")) + { + /* Check with Static NAPT entries */ + for (auto it = m_staticNaptEntry.begin(); it != m_staticNaptEntry.end(); it++) + { + /* Check the twice_nat_id is matched, otherwise continue */ + if (m_natBindingInfo[key].twice_nat_id != (*it).second.twice_nat_id) + { + continue; + } + + /* Check the twice NAT is not added, otherwise continue */ + if (((*it).second.twice_nat_added) or (!(*it).second.binding_key.empty())) + { + continue; + } + + /* Check interface is assigned, otherwise continue */ + if ((m_natBindingInfo[key].pool_interface == NONE_STRING) or ((*it).second.interface == NONE_STRING)) + { + continue; + } + + /* Check the nat type is equal, otherwise continue */ + if (m_natBindingInfo[key].nat_type != (*it).second.nat_type) + { + continue; + } + + (*it).second.twice_nat_added = true; + (*it).second.binding_key = key; + m_natBindingInfo[key].twice_nat_added = true; + m_natBindingInfo[key].static_key = (*it).first; + + /* Add Dynamic rules */ + setDynamicAllForwardOrAclbasedRules(ADD, m_natBindingInfo[key].pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, acls_name, key); + + isRuleAdded = true; + break; + } + } + else + { + /* Get all the Static NAT entries */ + for (auto it = m_staticNatEntry.begin(); it != m_staticNatEntry.end(); it++) + { + /* Check the twice_nat_id is matched, otherwise continue */ + if (m_natBindingInfo[key].twice_nat_id != (*it).second.twice_nat_id) + { + continue; + } + + /* Check the twice NAT is not added, otherwise continue */ + if (((*it).second.twice_nat_added) or (!(*it).second.binding_key.empty())) + { + continue; + } + + /* Check interface is assigned, otherwise continue */ + if ((m_natBindingInfo[key].pool_interface == NONE_STRING) or ((*it).second.interface == NONE_STRING)) + { + continue; + } + + /* Check the nat type is equal, otherwise continue */ + if (m_natBindingInfo[key].nat_type != (*it).second.nat_type) + { + continue; + } + + (*it).second.twice_nat_added = true; + (*it).second.binding_key = key; + m_natBindingInfo[key].twice_nat_added = true; + m_natBindingInfo[key].static_key = (*it).first; + + /* Add Dynamic rules */ + setDynamicAllForwardOrAclbasedRules(ADD, m_natBindingInfo[key].pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, acls_name, key); + + isRuleAdded = true; + break; + } + } + + if (!isRuleAdded) + { + SWSS_LOG_INFO("No dynamic twice nat rules to add"); + } +} + +/* To Delete Dynamic Twice NAT/NAPT iptables rules based on Binding Key if all valid conditions are met */ +void NatMgr::deleteDynamicTwiceNatRule(const string &key) +{ + /* Example: + * Entry is NAT_BINDINGS|BindingName and key is BindingName + */ + + string port_range = EMPTY_STRING; + string pool_name = m_natBindingInfo[key].pool_name; + string acls_name = m_natBindingInfo[key].acl_name; + bool isRuleDeleted = false; + + /* Check the NAT is enabled, otherwise return */ + if (!isNatEnabled()) + { + SWSS_LOG_INFO("NAT is not yet enabled, skipping dynamic twice nat rules deletion for %s", key.c_str()); + return; + } + + /* Check the twice NAT is not added, otherwise return */ + if ((!m_natBindingInfo[key].twice_nat_added) or (m_natBindingInfo[key].static_key.empty())) + { + SWSS_LOG_INFO("Twice NAT rule is not yet added, skipping dynamic twice nat rules deletion for %s", key.c_str()); + return; + } + + port_range = m_natPoolInfo[pool_name].port_range; + + if (!port_range.empty() and (port_range != "NULL")) + { + /* Check with Static NAPT entries */ + for (auto it = m_staticNaptEntry.begin(); it != m_staticNaptEntry.end(); it++) + { + /* Check the twice_nat_id is matched, otherwise continue */ + if (m_natBindingInfo[key].twice_nat_id != (*it).second.twice_nat_id) + { + continue; + } + + /* Check the twice NAT is added, otherwise continue */ + if ((!(*it).second.twice_nat_added) or ((*it).second.binding_key.empty())) + { + continue; + } + + /* Check interface is assigned, otherwise continue */ + if ((m_natBindingInfo[key].pool_interface == NONE_STRING) or ((*it).second.interface == NONE_STRING)) + { + continue; + } + + /* Check the nat type is equal, otherwise continue */ + if (m_natBindingInfo[key].nat_type != (*it).second.nat_type) + { + continue; + } + + /* Check the key is matching, otherwise continue */ + if (m_natBindingInfo[key].static_key != (*it).first) + { + continue; + } + + /* Delete Dynamic rules */ + setDynamicAllForwardOrAclbasedRules(DELETE, m_natBindingInfo[key].pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, acls_name, key); + + (*it).second.twice_nat_added = false; + (*it).second.binding_key = EMPTY_STRING; + m_natBindingInfo[key].twice_nat_added = false; + m_natBindingInfo[key].static_key = EMPTY_STRING; + isRuleDeleted = true; + break; + } + } + else + { + /* Get all the Static NAT entries */ + for (auto it = m_staticNatEntry.begin(); it != m_staticNatEntry.end(); it++) + { + /* Check the twice_nat_id is matched, otherwise continue */ + if (m_natBindingInfo[key].twice_nat_id != (*it).second.twice_nat_id) + { + continue; + } + + /* Check the twice NAT is added, otherwise continue */ + if ((!(*it).second.twice_nat_added) or ((*it).second.binding_key.empty())) + { + continue; + } + + /* Check interface is assigned, otherwise continue */ + if ((m_natBindingInfo[key].pool_interface == NONE_STRING) or ((*it).second.interface == NONE_STRING)) + { + continue; + } + + /* Check the nat type is equal, otherwise continue */ + if (m_natBindingInfo[key].nat_type != (*it).second.nat_type) + { + continue; + } + + /* Check the key is matching, otherwise continue */ + if (m_natBindingInfo[key].static_key != (*it).first) + { + continue; + } + + /* Delete Dynamic rules */ + setDynamicAllForwardOrAclbasedRules(DELETE, m_natBindingInfo[key].pool_interface, m_natPoolInfo[pool_name].ip_range, + m_natPoolInfo[pool_name].port_range, acls_name, key); + + (*it).second.twice_nat_added = false; + (*it).second.binding_key = EMPTY_STRING; + m_natBindingInfo[key].twice_nat_added = false; + m_natBindingInfo[key].static_key = EMPTY_STRING; + isRuleDeleted = true; + break; + } + } + + if (!isRuleDeleted) + { + SWSS_LOG_INFO("No dynamic twice nat rules to delete"); + } +} + +/* To enable the NAT Feature */ +void NatMgr::enableNatFeature(void) +{ + /* Create APPL_DB key */ + string appKey = VALUES; + vector fvVector; + + FieldValueTuple p(NAT_ADMIN_MODE, "enabled"); + fvVector.push_back(p); + + if (m_natTcpTimeout != NAT_TCP_TIMEOUT_DEFAULT) + { + FieldValueTuple q(NAT_TCP_TIMEOUT, std::to_string(m_natTcpTimeout)); + fvVector.push_back(q); + } + + if (m_natUdpTimeout != NAT_UDP_TIMEOUT_DEFAULT) + { + FieldValueTuple r(NAT_UDP_TIMEOUT, std::to_string(m_natUdpTimeout)); + fvVector.push_back(r); + } + + if (m_natTimeout != NAT_TIMEOUT_DEFAULT) + { + FieldValueTuple s(NAT_TIMEOUT, std::to_string(m_natTimeout)); + fvVector.push_back(s); + } + + m_appNatGlobalTableProducer.set(appKey, fvVector); + SWSS_LOG_INFO("Enabled NAT Admin Mode to APPL_DB"); + + /* Add static NAT entries */ + SWSS_LOG_INFO("Adding Static NAT entries"); + addStaticNatEntries(); + + /* Add static NAPT entries */ + SWSS_LOG_INFO("Adding Static NAPT entries"); + addStaticNaptEntries(); + + /* Add dynamic NAT rules */ + SWSS_LOG_INFO("Adding Dynamic NAT rules"); + addDynamicNatRules(); + + /* Add full-cone PRE-ROUTING DNAT rule in the kernel */ + setFullConeDnatIptablesRule(ADD); +} + +/* To disable the NAT Feature */ +void NatMgr::disableNatFeature(void) +{ + /* Create APPL_DB key */ + string appKey = VALUES; + vector fvVector; + + FieldValueTuple s(NAT_ADMIN_MODE, DISABLED); + fvVector.push_back(s); + + /* Delete static NAT entries */ + SWSS_LOG_INFO("Deleting Static NAT entries"); + removeStaticNatEntries(); + + /* Delete static NAPT entries */ + SWSS_LOG_INFO("Deleting Static NAPT entries"); + removeStaticNaptEntries(); + + /* Delete dynamic NAT/NAPT iptables */ + removeDynamicNatRules(); + + m_appNatGlobalTableProducer.set(appKey, fvVector); + SWSS_LOG_INFO("Disabled NAT Admin Mode to APPL_DB"); + + /* Delete full-cone PRE-ROUTING DNAT rule in the kernel */ + setFullConeDnatIptablesRule(DELETE); +} + +/* To parse the received Static NAT Table and save it to cache */ +void NatMgr::doStaticNatTask(Consumer &consumer) +{ + auto it = consumer.m_toSync.begin(); + + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t), op = kfvOp(t); + vector keys = tokenize(key, config_db_key_delimiter); + const vector& data = kfvFieldsValues(t); + string global_ip, ipAddress; + string local_ip = EMPTY_STRING, interface = EMPTY_STRING; + string nat_type = EMPTY_STRING, twice_nat_id = EMPTY_STRING; + bool ipFound = false, natTypeFound = false, twiceNatFound = false, nonValueFound = false, isOverlap = false; + int ip_num = 0, nat_type_num = 0, twice_nat_num = 0, twice_nat_value; + uint32_t ipv4_addr, global_addr, local_addr, pool_addr_low, pool_addr_high; + + /* Example : Config_Db + * STATIC_NAT|65.55.42.1 + * local_ip: 10.0.0.1 + * nat_type: dnat + * twice_nat_id: 100 + */ + + /* Ensure the global_ip format is x.x.x.x, otherwise ignore */ + if (inet_pton(AF_INET, key.c_str(), &global_addr) != 1) + { + SWSS_LOG_ERROR("Invalid global address format, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the key size is 1 otherwise ignore */ + if (keys.size() != STATIC_NAT_KEY_SIZE) + { + SWSS_LOG_ERROR("Invalid key size %lu, skipping %s", keys.size(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + global_addr = ntohl(global_addr); + /* Ensure the global ip is not Zero, Broadcast, Loopback, Multicast and Reserved address */ + if (IS_ZERO_ADDR(global_addr) or IS_BROADCAST_ADDR(global_addr) or IS_LOOPBACK_ADDR(global_addr) or + IS_MULTICAST_ADDR(global_addr) or IS_RESERVED_ADDR(global_addr)) + { + SWSS_LOG_ERROR("Invalid global address, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Example : APPL_DB + * NAT_TABLE:10.0.0.1 + * translated_ip: 65.55.42.1 + * nat_type: dnat + * entry_type: static + */ + + if (op == SET_COMMAND) + { + SWSS_LOG_INFO("Set command for %s", key.c_str()); + + /* Get the Config_db key values */ + for (auto idx : data) + { + const auto &field = fvField(idx); + const auto &value = fvValue(idx); + if (field == LOCAL_IP) + { + local_ip = value; + ipFound = true; + ip_num++; + } + else if (field == NAT_TYPE) + { + nat_type = value; + natTypeFound = true; + nat_type_num++; + } + else if (field == TWICE_NAT_ID) + { + twice_nat_id = value; + twiceNatFound = true; + twice_nat_num++; + } + else + { + nonValueFound = true; + } + } + + /* Ensure the local_ip value is valid otherwise ignore */ + if ((ipFound == false) or (ip_num != 1) or nonValueFound == true) + { + SWSS_LOG_ERROR("Invalid local_ip values, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the nat_type value is valid otherwise ignore */ + if ((natTypeFound == true) and (nat_type_num != 1)) + { + SWSS_LOG_ERROR("Invalid nat_type value, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the twice_nat_id value is valid otherwise ignore */ + if ((twiceNatFound == true) and (twice_nat_num != 1)) + { + SWSS_LOG_ERROR("Invalid twice_nat_id value, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the non value is not present otherwise ignore */ + if (nonValueFound == true) + { + SWSS_LOG_ERROR("Invalid value, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the local_ip format is x.x.x.x, otherwise ignore */ + if (inet_pton(AF_INET, local_ip.c_str(), &local_addr) != 1) + { + SWSS_LOG_ERROR("Invalid local ip address format %s, skipping %s", local_ip.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + local_addr = ntohl(local_addr); + /* Ensure the local ip is not Zero, Broadcast, Loopback, Multicast and Reserved address */ + if (IS_ZERO_ADDR(local_addr) or IS_BROADCAST_ADDR(local_addr) or IS_LOOPBACK_ADDR(local_addr) or + IS_MULTICAST_ADDR(local_addr) or IS_RESERVED_ADDR(local_addr)) + { + SWSS_LOG_ERROR("Invalid local address %s, skipping %s", local_ip.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the nat_type value is snat or dnat otherwise ignore */ + if ((natTypeFound == true) and ((nat_type != SNAT_NAT_TYPE) and (nat_type != DNAT_NAT_TYPE))) + { + SWSS_LOG_ERROR("Invalid nat_type, it is neither snat or dnat, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (twiceNatFound == true) + { + /* Ensure the given twice_nat_id is integer, otherwise ignore */ + try + { + twice_nat_value = stoi(twice_nat_id); + } + catch(...) + { + SWSS_LOG_ERROR("Invalid twice_nat_id %s, skipping %s", twice_nat_id.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the twice_nat_id is within the limits (1 to 9999), otherwise ignore */ + if ((twice_nat_value < TWICE_NAT_ID_MIN) or (twice_nat_value > TWICE_NAT_ID_MAX)) + { + SWSS_LOG_ERROR("Invalid twice_nat_id, not in limits, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + } + + /* check the global address is overlapping with any NAPT entry */ + global_ip = key; + if (nat_type == SNAT_NAT_TYPE) + { + global_ip = local_ip; + } + + for (auto it = m_staticNaptEntry.begin(); it != m_staticNaptEntry.end(); it++) + { + vector napt_keys = tokenize((*it).first, config_db_key_delimiter); + ipAddress = napt_keys[0]; + if ((*it).second.nat_type == SNAT_NAT_TYPE) + { + ipAddress = (*it).second.local_ip; + } + + if (ipAddress == global_ip) + { + isOverlap = true; + break; + } + } + + if (isOverlap) + { + SWSS_LOG_ERROR("Global Ip overlaps with static NAPT entry, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* check the global address is overlapping with any Dynamic Pool entry */ + ipv4_addr = global_addr; + if (nat_type == SNAT_NAT_TYPE) + { + ipv4_addr = local_addr; + } + + for (auto it = m_natPoolInfo.begin(); it != m_natPoolInfo.end(); it++) + { + vector nat_ip = tokenize((*it).second.ip_range, range_specifier); + + /* Check the pool is valid */ + if ((nat_ip.empty()) or (nat_ip.size() > 2)) + { + continue; + } + else if (nat_ip.size() == 2) + { + inet_pton(AF_INET, nat_ip[1].c_str(), &pool_addr_high); + pool_addr_high = ntohl(pool_addr_high); + + inet_pton(AF_INET, nat_ip[0].c_str(), &pool_addr_low); + pool_addr_low = ntohl(pool_addr_low); + } + else if (nat_ip.size() == 1) + { + inet_pton(AF_INET, nat_ip[0].c_str(), &pool_addr_low); + pool_addr_high = ntohl(pool_addr_low); + pool_addr_low = ntohl(pool_addr_low); + } + + if ((ipv4_addr >= pool_addr_low) and (ipv4_addr <= pool_addr_high)) + { + isOverlap = true; + break; + } + } + + if (isOverlap) + { + SWSS_LOG_ERROR("Global Ip overlaps with Dynamic Pool IP entry, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Check the key is already present in cache */ + if (m_staticNatEntry.find(key) != m_staticNatEntry.end()) + { + SWSS_LOG_INFO("Static NAT %s exists", key.c_str()); + + if (m_staticNatEntry[key].local_ip == local_ip) + { + /* Received the same Key and value, ignore */ + SWSS_LOG_ERROR("Duplicate Static NAT and it's values, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + else + { + SWSS_LOG_INFO("Static NAT %s with updated info", key.c_str()); + + /* Received key with new value */ + if (m_staticNatEntry[key].interface != NONE_STRING) + { + /* Remove the Static NAT Entry */ + SWSS_LOG_INFO("Deleting the Static NAT entry for %s", key.c_str()); + removeStaticNatEntry(key); + } + } + } + + /* New Key, Add it to cache */ + m_staticNatEntry[key].interface = NONE_STRING; + m_staticNatEntry[key].local_ip = local_ip; + if (nat_type.empty()) + { + m_staticNatEntry[key].nat_type = DNAT_NAT_TYPE; + } + else + { + m_staticNatEntry[key].nat_type = nat_type; + } + m_staticNatEntry[key].twice_nat_id = twice_nat_id; + m_staticNatEntry[key].twice_nat_added = false; + m_staticNatEntry[key].binding_key = EMPTY_STRING; + SWSS_LOG_INFO("Static NAT %s is added to cache", key.c_str()); + + /* Add the new Static NAT entry */ + SWSS_LOG_INFO("Adding the Static NAT entry for %s", key.c_str()); + addStaticNatEntry(key); + + it = consumer.m_toSync.erase(it); + } + else if (op == DEL_COMMAND) + { + SWSS_LOG_INFO("Del command for %s", key.c_str()); + + /* Check the key is already present in cache */ + if (m_staticNatEntry.find(key) != m_staticNatEntry.end()) + { + /* Remove the Static NAT Entry */ + SWSS_LOG_INFO("Deleting the Static NAT entry for %s", key.c_str()); + removeStaticNatEntry(key); + + /* Cleaned the cache */ + SWSS_LOG_INFO("Static NAT %s is removed from the cache", key.c_str()); + m_staticNatEntry.erase(key); + } + else + { + SWSS_LOG_ERROR("Invalid Static NAT %s from Config_db, do nothing", key.c_str()); + } + + it = consumer.m_toSync.erase(it); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +/* To parse the received Static NAPT Table and save it to cache */ +void NatMgr::doStaticNaptTask(Consumer &consumer) +{ + auto it = consumer.m_toSync.begin(); + + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t), op = kfvOp(t); + vector keys = tokenize(key, config_db_key_delimiter); + const vector& data = kfvFieldsValues(t); + string global_ip, ipAddress; + string local_ip = EMPTY_STRING, local_port = EMPTY_STRING, interface = EMPTY_STRING; + string nat_type = EMPTY_STRING, twice_nat_id = EMPTY_STRING; + bool ipFound = false, portFound = false, natTypeFound = false, twiceNatFound = false, nonValueFound = false, isOverlap = false; + int ip_num = 0, port_num = 0, portValue = 0, nat_type_num = 0, twice_nat_num = 0, twice_nat_value; + uint32_t ipv4_addr; + + /* Example : Config_Db + * STATIC_NAPT|65.55.42.1|TCP|1024 + * local_ip: 10.0.0.1 + * local_port: 6000 + */ + + /* Ensure the key size is 3 otherwise ignore */ + if (keys.size() != STATIC_NAPT_KEY_SIZE) + { + SWSS_LOG_ERROR("Invalid key size %lu, skipping %s", keys.size(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the global ip format is x.x.x.x, otherwise ignore */ + if (inet_pton(AF_INET, keys[0].c_str(), &ipv4_addr) != 1) + { + SWSS_LOG_ERROR("Invalid global ip address format %s, skipping %s", keys[0].c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + ipv4_addr = ntohl(ipv4_addr); + /* Ensure the local ip is not Zero, Broadcast, Loopback, Multicast and Reserved address */ + if (IS_ZERO_ADDR(ipv4_addr) or IS_BROADCAST_ADDR(ipv4_addr) or IS_LOOPBACK_ADDR(ipv4_addr) or + IS_MULTICAST_ADDR(ipv4_addr) or IS_RESERVED_ADDR(ipv4_addr)) + { + SWSS_LOG_ERROR("Invalid global address %s, skipping %s", keys[0].c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the prototype is UDP or TCP otherwise ignore */ + if ((keys[1] != to_upper(IP_PROTOCOL_TCP)) and (keys[1] != to_upper(IP_PROTOCOL_UDP))) + { + SWSS_LOG_ERROR("Invalid ip prototype %s, skipping %s", keys[1].c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the given portValue is integer, otherwise ignore */ + try + { + portValue = stoi(keys[2]); + } + catch(...) + { + SWSS_LOG_ERROR("Invalid global port %s, skipping %s", keys[2].c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the global port is inbetween 1 to 65535, otherwise ignore */ + if ((portValue < L4_PORT_MIN) or (portValue > L4_PORT_MAX)) + { + SWSS_LOG_ERROR("Invalid global port value %d, skipping %s", portValue, key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Example : APPL_DB + * NAPT_TABLE:TCP:10.0.0.1:6000 + * translated_ip: 65.55.42.1 + * translated_port: 1024 + * nat_type: dnat + * entry_type: static + */ + + if (op == SET_COMMAND) + { + SWSS_LOG_INFO("Set command for %s", key.c_str()); + + /* Get the Config_db key values */ + for (auto idx : data) + { + const auto &field = fvField(idx); + const auto &value = fvValue(idx); + if (field == LOCAL_IP) + { + local_ip = value; + ipFound = true; + ip_num++; + } + else if (field == LOCAL_PORT) + { + local_port = value; + portFound = true; + port_num++; + } + else if (field == NAT_TYPE) + { + nat_type = value; + natTypeFound = true; + nat_type_num++; + } + else if (field == TWICE_NAT_ID) + { + twice_nat_id = value; + twiceNatFound = true; + twice_nat_num++; + } + else + { + nonValueFound = true; + } + } + + /* Ensure the local_ip value is valid otherwise ignore */ + if ((ipFound == true) and (ip_num != 1)) + { + SWSS_LOG_ERROR("Invalid local_ip value, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the local_port value is valid otherwise ignore */ + if ((portFound == true) and (port_num != 1)) + { + SWSS_LOG_ERROR("Invalid local_port value, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the nat_type value is valid otherwise ignore */ + if ((natTypeFound == true) and (nat_type_num != 1)) + { + SWSS_LOG_ERROR("Invalid nat_type value, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the twice_nat_id value is valid otherwise ignore */ + if ((twiceNatFound == true) and (twice_nat_num != 1)) + { + SWSS_LOG_ERROR("Invalid twice_nat_id value, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the non value is not present otherwise ignore */ + if (nonValueFound == true) + { + SWSS_LOG_ERROR("Invalid value, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the local ip format is x.x.x.x, otherwise ignore */ + if (inet_pton(AF_INET, local_ip.c_str(), &ipv4_addr) != 1) + { + SWSS_LOG_ERROR("Invalid local ip address format %s, skipping %s", local_ip.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + ipv4_addr = ntohl(ipv4_addr); + /* Ensure the local ip is not Zero, Broadcast, Loopback, Multicast and Reserved address */ + if (IS_ZERO_ADDR(ipv4_addr) or IS_BROADCAST_ADDR(ipv4_addr) or IS_LOOPBACK_ADDR(ipv4_addr) or + IS_MULTICAST_ADDR(ipv4_addr) or IS_RESERVED_ADDR(ipv4_addr)) + { + SWSS_LOG_ERROR("Invalid local address %s, skipping %s", local_ip.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the given local_port is integer, otherwise ignore */ + try + { + portValue = stoi(local_port); + } + catch(...) + { + SWSS_LOG_ERROR("Invalid local port %s, skipping %s", local_port.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the local port is inbetween 1 to 65535, otherwise ignore */ + if ((portValue < L4_PORT_MIN) or (portValue > L4_PORT_MAX)) + { + SWSS_LOG_ERROR("Invalid internal port value %d, skipping %s", portValue, key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the nat_type value is snat or dnat otherwise ignore */ + if ((natTypeFound == true) and ((nat_type != SNAT_NAT_TYPE) and (nat_type != DNAT_NAT_TYPE))) + { + SWSS_LOG_ERROR("Invalid nat_type, it is neither snat or dnat, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (twiceNatFound == true) + { + /* Ensure the given twice_nat_id is integer, otherwise ignore */ + try + { + twice_nat_value = stoi(twice_nat_id); + } + catch(...) + { + SWSS_LOG_ERROR("Invalid twice_nat_id %s, skipping %s", twice_nat_id.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the twice_nat_id is within the limits (1 to 9999), otherwise ignore */ + if ((twice_nat_value < TWICE_NAT_ID_MIN) or (twice_nat_value > TWICE_NAT_ID_MAX)) + { + SWSS_LOG_ERROR("Invalid twice_nat_id %d, not in limits, skipping %s", twice_nat_value, key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + } + + /* check the global address is overlapping with any NAT entry */ + global_ip = keys[0]; + if (nat_type == SNAT_NAT_TYPE) + { + global_ip = local_ip; + } + + for (auto it = m_staticNatEntry.begin(); it != m_staticNatEntry.end(); it++) + { + ipAddress = (*it).first; + if ((*it).second.nat_type == SNAT_NAT_TYPE) + { + ipAddress = (*it).second.local_ip; + } + + if (ipAddress == global_ip) + { + isOverlap = true; + break; + } + } + + if (isOverlap) + { + SWSS_LOG_ERROR("Global Ip overlaps with static NAT entry, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Check the key is already present in cache */ + if (m_staticNaptEntry.find(key) != m_staticNaptEntry.end()) + { + SWSS_LOG_INFO("Static NAPT %s exists", key.c_str()); + if ((m_staticNaptEntry[key].local_ip == local_ip) and + (m_staticNaptEntry[key].local_port == local_port)) + { + /* Received the same Key and value, ignore */ + SWSS_LOG_ERROR("Duplicate Static NAPT and it's values, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + else + { + SWSS_LOG_INFO("Static NAPT %s with updated info", key.c_str()); + + /* Received key with new value */ + if (m_staticNatEntry[key].interface != NONE_STRING) + { + /* Remove the Static NAPT Entry */ + SWSS_LOG_INFO("Deleting the Static NAPT entry for %s", key.c_str()); + removeStaticNaptEntry(key); + } + } + } + + /* New Key, Add it to cache */ + m_staticNaptEntry[key].interface = NONE_STRING; + m_staticNaptEntry[key].local_ip = local_ip; + m_staticNaptEntry[key].local_port = local_port; + if (nat_type.empty()) + { + m_staticNaptEntry[key].nat_type = DNAT_NAT_TYPE; + } + else + { + m_staticNaptEntry[key].nat_type = nat_type; + } + m_staticNaptEntry[key].twice_nat_id = twice_nat_id; + m_staticNaptEntry[key].twice_nat_added = false; + m_staticNaptEntry[key].binding_key = EMPTY_STRING; + SWSS_LOG_INFO("Static NAPT %s is added to cache", key.c_str()); + + /* Add the new Static NAT entry */ + SWSS_LOG_INFO("Adding the Static NAPT entry for %s", key.c_str()); + addStaticNaptEntry(key); + + it = consumer.m_toSync.erase(it); + } + else if (op == DEL_COMMAND) + { + SWSS_LOG_INFO("Del command for %s", key.c_str()); + + /* Check the key is already present in cache */ + if (m_staticNaptEntry.find(key) != m_staticNaptEntry.end()) + { + /* Remove the Static NAPT Entry */ + SWSS_LOG_INFO("Deleting the Static NAPT entry for %s", key.c_str()); + removeStaticNaptEntry(key); + + /* Cleaned the cache */ + SWSS_LOG_INFO("Static NAPT %s is removed from the cache", key.c_str()); + m_staticNaptEntry.erase(key); + } + else + { + SWSS_LOG_ERROR("Invalid Static NAPT %s from Config_db, do nothing", key.c_str()); + } + + it = consumer.m_toSync.erase(it); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +/* To parse the received NAT Pool Table and save it to cache */ +void NatMgr::doNatPoolTask(Consumer &consumer) +{ + auto it = consumer.m_toSync.begin(); + + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t), op = kfvOp(t); + vector keys = tokenize(key, config_db_key_delimiter); + const vector& data = kfvFieldsValues(t); + string nat_ip = EMPTY_STRING, nat_port = EMPTY_STRING, binding_name = EMPTY_STRING, static_ip; + bool ipFound = false, portFound = false, nonValueFound = false, isOverlap = false; + int ip_num = 0, port_num = 0, portValue_low, portValue_high; + uint32_t ipv4_addr_low, ipv4_addr_high, static_address; + + /* Example : Config_Db + * NAT_POOL|PoolName + * nat_ip: 10.0.0.1-10.0.0.5 + * nat_port: 100-105 + */ + + /* Ensure the key size is 1 otherwise ignore */ + if (keys.size() != POOL_TABLE_KEY_SIZE) + { + SWSS_LOG_ERROR("Invalid key size %lu, skipping %s", keys.size(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (op == SET_COMMAND) + { + SWSS_LOG_INFO("Set command for %s", key.c_str()); + + /* Get the Config_db key values */ + for (auto idx : data) + { + const auto &field = fvField(idx); + const auto &value = fvValue(idx); + if (field == NAT_IP) + { + nat_ip = value; + ipFound = true; + ip_num++; + } + else if (field == NAT_PORT) + { + nat_port = value; + portFound = true; + port_num++; + } + else + { + nonValueFound = true; + } + } + + /* Ensure the "nat_ip" values are valid otherwise ignore */ + if (((ipFound == true) and (ip_num != 1)) or (ipFound == false)) + { + SWSS_LOG_ERROR("Invalid nat_ip values, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the "nat_port" values are valid otherwise ignore */ + if ((portFound == true) and (port_num != 1)) + { + SWSS_LOG_ERROR("Invalid key values, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the non value is not present, otherwise ignore */ + if (nonValueFound == true) + { + SWSS_LOG_ERROR("Invalid value, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the Pool name length is not more than 32 otherwise ignore */ + if (key.length() > 32) + { + SWSS_LOG_ERROR("Invalid pool name length - %lu, skipping %s", key.length(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the nat_ip is not empty */ + if (nat_ip.empty() or (nat_ip == "NULL")) + { + SWSS_LOG_ERROR("Invalid nat_ip, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + vector nat_ip_range = tokenize(nat_ip, range_specifier); + + /* Ensure the nat_ip_range is valid */ + if (nat_ip_range.empty()) + { + SWSS_LOG_ERROR("NAT pool ip range %s is not valid, skipping %s", nat_ip.c_str(), key.c_str()); + continue; + } + + /* Ensure the ip range size is not more than 2, otherwise ignore */ + if (nat_ip_range.size() > 2) + { + SWSS_LOG_ERROR("Invalid nat ip range size %lu, skipping %s", nat_ip_range.size(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + else if (nat_ip_range.size() == 2) + { + SWSS_LOG_INFO("Pool %s contains nat_ip range", key.c_str()); + + /* Ensure the ip format is x.x.x.x, otherwise ignore */ + if (inet_pton(AF_INET, nat_ip_range[1].c_str(), &ipv4_addr_high) != 1) + { + SWSS_LOG_ERROR("Invalid ip address format %s, skipping %s", nat_ip_range[1].c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + ipv4_addr_high = ntohl(ipv4_addr_high); + /* Ensure the ip address is not Zero, Broadcast, Loopback, Multicast and Reserved address */ + if (IS_ZERO_ADDR(ipv4_addr_high) or IS_BROADCAST_ADDR(ipv4_addr_high) or IS_LOOPBACK_ADDR(ipv4_addr_high) or + IS_MULTICAST_ADDR(ipv4_addr_high) or IS_RESERVED_ADDR(ipv4_addr_high)) + { + SWSS_LOG_ERROR("Invalid ip address %s, skipping %s", nat_ip_range[1].c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the ip format is x.x.x.x, otherwise ignore */ + if (inet_pton(AF_INET, nat_ip_range[0].c_str(), &ipv4_addr_low) != 1) + { + SWSS_LOG_ERROR("Invalid ip address format %s, skipping %s", nat_ip_range[0].c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + ipv4_addr_low = ntohl(ipv4_addr_low); + /* Ensure the ip address is not Zero, Broadcast, Loopback, Multicast and Reserved address */ + if (IS_ZERO_ADDR(ipv4_addr_low) or IS_BROADCAST_ADDR(ipv4_addr_low) or IS_LOOPBACK_ADDR(ipv4_addr_low) or + IS_MULTICAST_ADDR(ipv4_addr_low) or IS_RESERVED_ADDR(ipv4_addr_low)) + { + SWSS_LOG_ERROR("Invalid ip address %s, skipping %s", nat_ip_range[0].c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the ip range is proper */ + if (ipv4_addr_low >= ipv4_addr_high) + { + SWSS_LOG_ERROR("NAT pool ip range %s is not valid, skipping %s", nat_ip.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + } + else + { + /* Ensure the ip format is x.x.x.x, otherwise ignore */ + if (inet_pton(AF_INET, nat_ip_range[0].c_str(), &ipv4_addr_low) != 1) + { + SWSS_LOG_ERROR("Invalid ip address format %s, skipping %s", nat_ip_range[0].c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + ipv4_addr_high = ntohl(ipv4_addr_low); + ipv4_addr_low = ntohl(ipv4_addr_low); + + /* Ensure the ip address is not Zero, Broadcast, Loopback, Multicast and Reserved address */ + if (IS_ZERO_ADDR(ipv4_addr_low) or IS_BROADCAST_ADDR(ipv4_addr_low) or IS_LOOPBACK_ADDR(ipv4_addr_low) or + IS_MULTICAST_ADDR(ipv4_addr_low) or IS_RESERVED_ADDR(ipv4_addr_low)) + { + SWSS_LOG_ERROR("Invalid ip address %s, skipping %s", nat_ip_range[0].c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + } + + /* Check the Pool table contains nat_port */ + if (!nat_port.empty() and (nat_port != "NULL")) + { + SWSS_LOG_INFO("Pool %s contains nat_port info", key.c_str()); + + vector nat_port_range = tokenize(nat_port, range_specifier); + + /* Ensure the port range size is not more than 2, otherwise ignore */ + if (nat_port_range.size() > 2) + { + SWSS_LOG_ERROR("Invalid nat port range size %lu, skipping %s", nat_port_range.size(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + else if (nat_port_range.size() == 2) + { + /* Ensure the given port is integer, otherwise ignore */ + try + { + portValue_low = stoi(nat_port_range[0]); + } + catch(...) + { + SWSS_LOG_ERROR("Invalid port %s, skipping %s", nat_port.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the port is inbetween 1 to 65535, otherwise ignore */ + if ((portValue_low < L4_PORT_MIN) or (portValue_low > L4_PORT_MAX)) + { + SWSS_LOG_ERROR("Invalid port value %d, skipping %s", portValue_low, key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the given port is integer, otherwise ignore */ + try + { + portValue_high = stoi(nat_port_range[1]); + } + catch(...) + { + SWSS_LOG_ERROR("Invalid port %s, skipping %s", nat_port.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the port is inbetween 1 to 65535, otherwise ignore */ + if ((portValue_high < L4_PORT_MIN) or (portValue_high > L4_PORT_MAX)) + { + SWSS_LOG_ERROR("Invalid port value %d, skipping %s", portValue_high, key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (portValue_low >= portValue_high) + { + SWSS_LOG_ERROR("Invalid nat port range %s, skipping %s", nat_port.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + } + else + { + /* Ensure the given port is integer, otherwise ignore */ + try + { + portValue_low = stoi(nat_port_range[0]); + } + catch(...) + { + SWSS_LOG_ERROR("Invalid port %s, skipping %s", nat_port.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the port is inbetween 1 to 65535, otherwise ignore */ + if ((portValue_low < L4_PORT_MIN) or (portValue_low > L4_PORT_MAX)) + { + SWSS_LOG_ERROR("Invalid port value %d, skipping %s", portValue_low, key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + } + } + + /* check the pool ip address is overlapping with any NAT entry */ + for (auto it = m_staticNatEntry.begin(); it != m_staticNatEntry.end(); it++) + { + static_ip = (*it).first; + if ((*it).second.nat_type == SNAT_NAT_TYPE) + { + static_ip = (*it).second.local_ip; + } + + inet_pton(AF_INET, static_ip.c_str(), &static_address); + static_address = ntohl(static_address); + + if ((static_address >= ipv4_addr_low) and (static_address <= ipv4_addr_high)) + { + isOverlap = true; + break; + } + } + + if (isOverlap) + { + SWSS_LOG_ERROR("Pool Ip address is overlaps with static NAT entry, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Check the key is already present in cache */ + if (m_natPoolInfo.find(key) != m_natPoolInfo.end()) + { + SWSS_LOG_INFO("Pool %s exists", key.c_str()); + + if ((m_natPoolInfo[key].ip_range == nat_ip) and (m_natPoolInfo[key].port_range == nat_port)) + { + /* Received the same Key and value, ignore */ + SWSS_LOG_ERROR("Duplicate Pool and it's values, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + else + { + SWSS_LOG_INFO("Pool %s with updated info", key.c_str()); + + /* Check this pool name on any nat binding */ + if (isPoolMappedtoBinding(key, binding_name)) + { + /* Remove the existing Dynamic NAT rules */ + SWSS_LOG_INFO("Deleting the Dynamic NAT/NAPT iptables rules for %s", key.c_str()); + removeDynamicNatRule(binding_name); + } + } + } + + /* New Key, Add it to cache */ + m_natPoolInfo[key].ip_range = nat_ip; + if (!nat_port.empty() and (nat_port != "NULL")) + { + m_natPoolInfo[key].port_range = nat_port; + } + else + { + m_natPoolInfo[key].port_range = EMPTY_STRING; + } + + /* Check this pool name on any nat binding */ + if (isPoolMappedtoBinding(key, binding_name)) + { + SWSS_LOG_INFO("Pool %s info is added to the cache", key.c_str()); + + /* Add the new Dynamic Nat Rules */ + SWSS_LOG_INFO("Adding the Dynamic NAT rules for %s", key.c_str()); + addDynamicNatRule(binding_name); + } + else + { + SWSS_LOG_INFO("Pool %s is not yet binded, saved it to cache", key.c_str()); + } + + it = consumer.m_toSync.erase(it); + } + else if (op == DEL_COMMAND) + { + SWSS_LOG_INFO("Del command for %s", key.c_str()); + + string binding_name = EMPTY_STRING; + + /* Check the key is already present in cache */ + if (m_natPoolInfo.find(key) != m_natPoolInfo.end()) + { + /* Check this pool name on any nat binding */ + if (isPoolMappedtoBinding(key, binding_name)) + { + SWSS_LOG_INFO("Pool %s is mapped on binding %s, deleting the dynamic iptables rules", key.c_str(), binding_name.c_str()); + /* Remove the existing Dynamic NAT rules */ + removeDynamicNatRule(binding_name); + } + + /* Clean the pool Info */ + m_natPoolInfo.erase(key); + SWSS_LOG_INFO("Pool %s is cleaned from the cache", key.c_str()); + } + else + { + SWSS_LOG_ERROR("Invalid NAT Pool %s from Config_db, do nothing", key.c_str()); + } + + it = consumer.m_toSync.erase(it); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +/* To parse the received NAT Binding Table and save it to cache */ +void NatMgr::doNatBindingTask(Consumer &consumer) +{ + auto it = consumer.m_toSync.begin(); + + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t), op = kfvOp(t); + vector keys = tokenize(key, config_db_key_delimiter); + const vector& data = kfvFieldsValues(t); + string nat_pool = EMPTY_STRING, nat_acl = EMPTY_STRING; + string nat_type = NONE_STRING, twice_nat_id = EMPTY_STRING; + bool poolFound = false, aclFound = false, natTypeFound = false, twiceNatFound = false, nonValueFound = false; + int pool_num = 0, acl_num = 0, nat_type_num = 0, twice_nat_num = 0, twice_nat_value; + + /* Example : + * NAT_BINDINGS|bindingName + * nat_pool: poolName + * access_list: aclName,aclName2 + */ + + /* Ensure the key size is 1 otherwise ignore */ + if (keys.size() != BINDING_TABLE_KEY_SIZE) + { + SWSS_LOG_ERROR("Invalid key size %lu, skipping %s", keys.size(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the Binding name length is not more than 32 otherwise ignore */ + if (key.length() > 32) + { + SWSS_LOG_ERROR("Invalid binding name length - %lu, skipping %s", key.length(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (op == SET_COMMAND) + { + SWSS_LOG_INFO("Set command for %s", key.c_str()); + + /* Get the Config_db key values */ + for (auto idx : data) + { + const auto &field = fvField(idx); + const auto &value = fvValue(idx); + if (field == NAT_POOL) + { + nat_pool = value; + poolFound = true; + pool_num++; + } + else if (field == NAT_ACLS) + { + nat_acl = value; + aclFound = true; + acl_num++; + } + else if (field == NAT_TYPE) + { + nat_type = value; + natTypeFound = true; + nat_type_num++; + } + else if (field == TWICE_NAT_ID) + { + twice_nat_id = value; + twiceNatFound = true; + twice_nat_num++; + } + else + { + nonValueFound = true; + } + } + + /* Ensure the nat_pool value is valid, otherwise ignore */ + if ((poolFound == true) and (pool_num != 1)) + { + SWSS_LOG_ERROR("Invalid nat_pool values, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the access_list value is valid, otherwise ignore */ + if ((aclFound == true) and (acl_num != 1)) + { + SWSS_LOG_ERROR("Invalid access_list values, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the nat_type value is valid otherwise ignore */ + if ((natTypeFound == true) and (nat_type_num != 1)) + { + SWSS_LOG_ERROR("Invalid nat_type value, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the twice_nat_id value is valid otherwise ignore */ + if ((twiceNatFound == true) and (twice_nat_num != 1)) + { + SWSS_LOG_ERROR("Invalid twice_nat_id value, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the non value is not present, otherwise ignore */ + if (nonValueFound == true) + { + SWSS_LOG_ERROR("Invalid value, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the nat_type value is snat otherwise ignore */ + if ((natTypeFound == true) and (nat_type != SNAT_NAT_TYPE)) + { + SWSS_LOG_ERROR("Invalid nat_type %s, skipping %s", nat_type.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (twice_nat_id == "NULL") + { + twiceNatFound = false; + twice_nat_id = EMPTY_STRING; + } + + if (twiceNatFound == true) + { + /* Ensure the given twice_nat_id is integer, otherwise ignore */ + try + { + twice_nat_value = stoi(twice_nat_id); + } + catch(...) + { + SWSS_LOG_ERROR("Invalid twice_nat_id %s, skipping %s", twice_nat_id.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the twice_nat_id is within the limits (1 to 9999), otherwise ignore */ + if ((twice_nat_value < TWICE_NAT_ID_MIN) or (twice_nat_value > TWICE_NAT_ID_MAX)) + { + SWSS_LOG_ERROR("Invalid twice_nat_id %d, not in limits, skipping %s", twice_nat_value, key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + } + + /* Ensure the Pool name length is not more than 32 otherwise ignore */ + if (nat_pool.length() > 32) + { + SWSS_LOG_ERROR("Invalid pool name length - %lu, skipping %s", nat_pool.length(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Check the key is already present in cache */ + if (m_natBindingInfo.find(key) != m_natBindingInfo.end()) + { + SWSS_LOG_INFO("Binding %s exists", key.c_str()); + if ((m_natBindingInfo[key].pool_name == nat_pool) and (m_natBindingInfo[key].acl_name == nat_acl)) + { + /* Received the same Key and value, ignore */ + SWSS_LOG_ERROR("Duplicate Binding and it's values, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + else + { + /* Remove the existing Dynamic NAT rules */ + SWSS_LOG_INFO("Deleting the Dynamic NAT rules for %s", key.c_str()); + removeDynamicNatRule(key); + } + } + + /* New Key, Add it to cache */ + m_natBindingInfo[key].pool_name = nat_pool; + m_natBindingInfo[key].acl_name = nat_acl; + m_natBindingInfo[key].pool_interface = NONE_STRING; + m_natBindingInfo[key].acl_interface = NONE_STRING; + m_natBindingInfo[key].twice_nat_id = twice_nat_id; + m_natBindingInfo[key].twice_nat_added = false; + if (nat_type.empty()) + { + m_natBindingInfo[key].nat_type = SNAT_NAT_TYPE; + } + else + { + m_natBindingInfo[key].nat_type = nat_type; + } + m_natBindingInfo[key].static_key = EMPTY_STRING; + SWSS_LOG_INFO("Binding Info %s is added to the cache", key.c_str()); + + /* Add the new Dynamic Nat Rules */ + SWSS_LOG_INFO("Adding the Dynamic NAT rules for %s", key.c_str()); + addDynamicNatRule(key); + + it = consumer.m_toSync.erase(it); + } + else if (op == DEL_COMMAND) + { + SWSS_LOG_INFO("Del command for %s", key.c_str()); + + /* Check the key is already present in cache */ + if (m_natBindingInfo.find(key) != m_natBindingInfo.end()) + { + /* Remove the existing Dynamic NAT rules */ + SWSS_LOG_INFO("Deleting the Dynamic NAT rules for %s", key.c_str()); + removeDynamicNatRule(key); + + /* Clean the binding info */ + m_natBindingInfo.erase(key); + SWSS_LOG_INFO("Binding Info %s is cleaned from the cache", key.c_str()); + } + else + { + SWSS_LOG_ERROR("Invalid NAT Binding %s from Config_Db, do nothing", key.c_str()); + } + + it = consumer.m_toSync.erase(it); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +/* To parse the received NAT Global Table and save it to cache */ +void NatMgr::doNatGlobalTask(Consumer &consumer) +{ + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + auto &t = it->second; + string key = kfvKey(t), op = kfvOp(t), adminMode; + bool tcpFound = false, udpFound = false, timeoutFound = false; + bool nonValueFound = false, adminModeFound = false; + int tcp_timeout = 0, udp_timeout = 0, timeout = 0; + int tcp_num = 0, udp_num = 0, timeout_num = 0, admin_mode_num = 0; + + /* Example : Config_DB + * NAT_GLOBAL|Values + * admin_mode: disabled + * nat_timeout: 600 + * nat_tcp_timeout: 300 + * nat_udp_timeout: 50 + */ + + /* Ensure the key is "Values" otherwise ignore */ + if (strcmp(key.c_str(), VALUES)) + { + SWSS_LOG_ERROR("Invalid key %s format. No Values", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Example : APPL_DB + * NAT_GLOBAL_TABLE:Values + * admin_mode: disabled + * nat_timeout : 600 + * nat_tcp_timeout : 300 + * nat_udp_timeout : 50 + */ + + if (op == SET_COMMAND) + { + SWSS_LOG_INFO("Set command for %s", key.c_str()); + + /* Create APPL_DB key */ + string appKey = VALUES; + vector fvVector; + + /* Get the Config_db values */ + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == NAT_ADMIN_MODE) + { + adminMode = fvValue(i); + adminModeFound = true; + admin_mode_num++; + } + else if (fvField(i) == NAT_TCP_TIMEOUT) + { + /* Ensure the given tcp_timeout is integer, otherwise ignore */ + try + { + tcp_timeout = stoi(fvValue(i)); + } + catch(...) + { + SWSS_LOG_ERROR("Invalid tcp_timeout %s, skipping %s", fvValue(i).c_str(), key.c_str()); + continue; + } + tcpFound = true; + tcp_num++; + } + else if (fvField(i) == NAT_UDP_TIMEOUT) + { + /* Ensure the given udp_timeout is integer, otherwise ignore */ + try + { + udp_timeout = stoi(fvValue(i)); + } + catch(...) + { + SWSS_LOG_ERROR("Invalid udp_timeout %s, skipping %s", fvValue(i).c_str(), key.c_str()); + continue; + } + udpFound = true; + udp_num++; + } + else if (fvField(i) == NAT_TIMEOUT) + { + /* Ensure the given timeout is integer, otherwise ignore */ + try + { + timeout = stoi(fvValue(i)); + } + catch(...) + { + SWSS_LOG_ERROR("Invalid timeout %s, skipping %s", fvValue(i).c_str(), key.c_str()); + continue; + } + timeoutFound = true; + timeout_num++; + } + else + { + nonValueFound = true; + } + } + + /* Ensure the admin_mode value is valid otherwise ignore */ + if ((adminModeFound == true) and (admin_mode_num != 1)) + { + SWSS_LOG_ERROR("Invalid admin_mode, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the nat_tcp_timeout values is valid otherwise ignore */ + if ((tcpFound == true) and (tcp_num != 1)) + { + SWSS_LOG_ERROR("Invalid nat_tcp_timeout, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the nat_udp_timeout value is valid otherwise ignore */ + if ((udpFound == true) and (udp_num != 1)) + { + SWSS_LOG_ERROR("Invalid nat_udp_timeout, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the nat_timeout value is valid otherwise ignore */ + if ((timeoutFound == true) and (timeout_num != 1)) + { + SWSS_LOG_ERROR("Invalid nat_timeout, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the value is valid otherwise ignore */ + if (((tcpFound == false) and (udpFound == false) and (timeoutFound == false) and (adminModeFound == false)) or + (nonValueFound == true)) + { + SWSS_LOG_ERROR("Invalid, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the admin_mode is enabled/disabled, otherwise ignore */ + if ((adminModeFound == true) and ((adminMode != ENABLED) and (adminMode != DISABLED))) + { + SWSS_LOG_ERROR("Invalid admin_mode value %s, skipping %s", adminMode.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the tcp timeout is inbetween 300 to 432000, otherwise ignore */ + if ((tcpFound == true) and ((tcp_timeout < NAT_TCP_TIMEOUT_MIN) or (tcp_timeout > NAT_TCP_TIMEOUT_MAX))) + { + SWSS_LOG_ERROR("Invalid tcp timeout value %d, skipping %s", tcp_timeout, key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the udp timeout is inbetween 120 to 600, otherwise ignore */ + if ((udpFound == true) and ((udp_timeout < NAT_UDP_TIMEOUT_MIN) or (udp_timeout > NAT_UDP_TIMEOUT_MAX))) + { + SWSS_LOG_ERROR("Invalid udp timeout value %d, skipping %s", udp_timeout, key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the timeout is inbetween 300 to 432000, otherwise ignore */ + if ((timeoutFound == true) and ((timeout < NAT_TIMEOUT_MIN) or (timeout > NAT_TIMEOUT_MAX))) + { + SWSS_LOG_ERROR("Invalid timeout value %d, skipping %s", timeout, key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if ((tcpFound == true) and (tcp_timeout != m_natTcpTimeout)) + { + m_natTcpTimeout = tcp_timeout; + SWSS_LOG_INFO("NAT TCP Timeout %s is added to cache", key.c_str()); + if (isNatEnabled()) + { + FieldValueTuple s(NAT_TCP_TIMEOUT, std::to_string(tcp_timeout)); + fvVector.push_back(s); + } + } + + if ((udpFound == true) and (udp_timeout != m_natUdpTimeout)) + { + m_natUdpTimeout = udp_timeout; + SWSS_LOG_INFO("NAT UDP Timeout %s is added to cache", key.c_str()); + if (isNatEnabled()) + { + FieldValueTuple s(NAT_UDP_TIMEOUT, std::to_string(udp_timeout)); + fvVector.push_back(s); + } + } + + if ((timeoutFound == true) and (timeout != m_natTimeout)) + { + m_natTimeout = timeout; + SWSS_LOG_INFO("NAT Timeout %s is added to cache", key.c_str()); + if (isNatEnabled()) + { + FieldValueTuple s(NAT_TIMEOUT, std::to_string(timeout)); + fvVector.push_back(s); + } + } + + if ((isNatEnabled() == true) and ((timeoutFound == true) or (tcpFound == true) or (udpFound == true))) + { + m_appNatGlobalTableProducer.set(appKey, fvVector); + SWSS_LOG_INFO("Added NAT Values %s to APPL_DB", key.c_str()); + } + + if ((adminModeFound == true) and (adminMode != natAdminMode)) + { + if (adminMode == ENABLED) + { + SWSS_LOG_INFO("NAT Admin Mode enabled is added to cache"); + natAdminMode = adminMode; + enableNatFeature(); + } + else + { + disableNatFeature(); + natAdminMode = adminMode; + SWSS_LOG_INFO("NAT Admin Mode disabled is added to cache"); + } + } + } + else if (op == DEL_COMMAND) + { + /* Create APPL_DB key */ + string appKey = VALUES; + vector fvVector; + + /* Set NAT default timeout as 600 seconds */ + m_natTimeout = NAT_TIMEOUT_DEFAULT; + + /* Set NAT default tcp timeout as 86400 seconds (1 Day) */ + m_natTcpTimeout = NAT_TCP_TIMEOUT_DEFAULT; + + /* Set NAT default udp timeout as 300 seconds */ + m_natUdpTimeout = NAT_UDP_TIMEOUT_DEFAULT; + + if (natAdminMode == ENABLED) + { + FieldValueTuple p(NAT_TIMEOUT, std::to_string(m_natTimeout)); + FieldValueTuple q(NAT_UDP_TIMEOUT, std::to_string(m_natUdpTimeout)); + FieldValueTuple r(NAT_TCP_TIMEOUT, std::to_string(m_natTcpTimeout)); + fvVector.push_back(p); + fvVector.push_back(q); + fvVector.push_back(r); + m_appNatGlobalTableProducer.set(appKey, fvVector); + + disableNatFeature(); + natAdminMode = DISABLED; + SWSS_LOG_INFO("NAT Admin Mode disabled is added to cache"); + } + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + } + + it = consumer.m_toSync.erase(it); + } +} + +/* To parse the received L3 Interface Table and save it to cache */ +void NatMgr::doNatIpInterfaceTask(Consumer &consumer) +{ + auto it = consumer.m_toSync.begin(); + + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t), nat_zone = "1"; + vector keys = tokenize(kfvKey(t), config_db_key_delimiter); + string op = kfvOp(t), port(keys[0]); + bool skipAddition = false, skipDeletion = false; + int prefixLen = 0, nat_zone_value = 1; + vector ipPrefixKeys; + + /* Example : Config_DB + * INTERFACE|Ethernet28|10.0.0.1/24 + * or + * INTERFACE|Ethernet28 + * { + * nat_zone = "0" + * } + */ + + /* Ensure the key size is 2 or 1, otherwise ignore */ + if ((keys.size() != L3_INTERFACE_KEY_SIZE) and (keys.size() != L3_INTERFACE_ZONE_SIZE)) + { + SWSS_LOG_INFO("Invalid key size %lu, skipping %s", keys.size(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + else + { + SWSS_LOG_INFO("Key size %lu for %s", keys.size(), key.c_str()); + } + + /* Ensure the key starts with "Vlan" or "Ethernet" or "PortChannel" or "Loopback", otherwise ignore */ + if ((strncmp(keys[0].c_str(), VLAN_PREFIX, strlen(VLAN_PREFIX))) and + (strncmp(keys[0].c_str(), ETHERNET_PREFIX, strlen(ETHERNET_PREFIX))) and + (strncmp(keys[0].c_str(), LOOPBACK_PREFIX, strlen(LOOPBACK_PREFIX))) and + (strncmp(keys[0].c_str(), LAG_PREFIX, strlen(LAG_PREFIX)))) + { + SWSS_LOG_INFO("Invalid key %s format, skipping %s", keys[0].c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (keys.size() == L3_INTERFACE_KEY_SIZE) + { + ipPrefixKeys = tokenize(keys[1], ip_address_delimiter); + + /* Ensure the ipPrefix key size is 2 otherwise ignore */ + if (ipPrefixKeys.size() != IP_PREFIX_SIZE) + { + SWSS_LOG_INFO("Invalid IpPrefix size %s, skipping %s", keys[1].c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the ip address is ipv4, otherwise ignore */ + try + { + IpAddress ipAddress(ipPrefixKeys[0]); + + if (!ipAddress.isV4()) + { + /* Ignore the IPv6 addresses */ + SWSS_LOG_INFO("IPv6 address is not supported, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + } + catch(...) + { + SWSS_LOG_INFO("Invalid ip address %s format, skipping %s", ipPrefixKeys[0].c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the given PrefixLen is integer, otherwise ignore */ + try + { + prefixLen = stoi(ipPrefixKeys[1]); + } + catch(...) + { + SWSS_LOG_ERROR("Invalid ip mask len %s, skipping %s", ipPrefixKeys[1].c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the ip mask len is valid, otherwise ignore */ + if ((prefixLen < IP_ADDR_MASK_LEN_MIN) or (prefixLen > IP_ADDR_MASK_LEN_MAX)) + { + SWSS_LOG_INFO("Invalid ip mask len %s, skipping %s", ipPrefixKeys[1].c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + } + + if (op == SET_COMMAND) + { + SWSS_LOG_INFO("Set command for %s", key.c_str()); + + /* + * Don't proceed if Port/LAG/VLAN is not ready yet. + * The pending task will be checked periodically and retried. + */ + if ((strncmp(keys[0].c_str(), LOOPBACK_PREFIX, strlen(LOOPBACK_PREFIX))) and + (!isPortStateOk(port))) + { + SWSS_LOG_INFO("Port is not ready, skipping %s", port.c_str()); + it++; + continue; + } + + if (keys.size() == L3_INTERFACE_ZONE_SIZE) + { + const vector& data = kfvFieldsValues(t); + + /* Get the Config_db key values */ + for (auto idx : data) + { + if (fvField(idx) == NAT_ZONE) + { + /* Ensure the given nat_zone is integer, otherwise ignore */ + try + { + nat_zone_value = stoi(fvValue(idx)); + } + catch(...) + { + SWSS_LOG_ERROR("Invalid nat_zone %s, skipping %s", fvValue(idx).c_str(), key.c_str()); + continue; + } + + /* Add plus 1 to avoid adding default zero mark in iptables mangle table */ + nat_zone_value++; + nat_zone = to_string(nat_zone_value); + break; + } + } + + /* Check the key is present in zone_interface cache */ + if (m_natZoneInterfaceInfo.find(port) != m_natZoneInterfaceInfo.end()) + { + /* Check the nat_zone is same or not */ + if (m_natZoneInterfaceInfo[port] != nat_zone) + { + /* Delete the mangle iptables rules for non-loopback interface */ + if (strncmp(keys[0].c_str(), LOOPBACK_PREFIX, strlen(LOOPBACK_PREFIX))) + { + setMangleIptablesRules(DELETE, port, m_natZoneInterfaceInfo[port]); + } + + /* Check the port is present in ip_interface cache */ + if (m_natIpInterfaceInfo.find(keys[0]) != m_natIpInterfaceInfo.end()) + { + /* Delete the Static NAT and NAPT iptables rules */ + SWSS_LOG_INFO("Deleting Static NAT iptables rules for %s", key.c_str()); + removeStaticNatIptables(keys[0]); + + SWSS_LOG_INFO("Deleting Static NAPT iptables rules for %s", key.c_str()); + removeStaticNaptIptables(keys[0]); + + /* Delete the Dynamic NAT rules */ + SWSS_LOG_INFO("Deleting Dynamic NAT rules for %s", key.c_str()); + removeDynamicNatRules(keys[0]); + } + + m_natZoneInterfaceInfo[port] = nat_zone; + + /* Add the mangle iptables rules for non-loopback interface */ + if (strncmp(keys[0].c_str(), LOOPBACK_PREFIX, strlen(LOOPBACK_PREFIX))) + { + setMangleIptablesRules(ADD, port, nat_zone); + } + + /* Check the port is present in ip_interface cache */ + if (m_natIpInterfaceInfo.find(keys[0]) != m_natIpInterfaceInfo.end()) + { + /* Add the Static NAT and NAPT iptables rules */ + SWSS_LOG_INFO("Adding Static NAT iptables rules for %s", key.c_str()); + addStaticNatIptables(keys[0]); + + SWSS_LOG_INFO("Adding Static NAPT iptables rules for %s", key.c_str()); + addStaticNaptIptables(keys[0]); + + /* Add the Dynamic NAT rules */ + SWSS_LOG_INFO("Adding Dynamic NAT rules for %s", key.c_str()); + addDynamicNatRules(keys[0]); + } + } + else + { + SWSS_LOG_INFO("Received same nat_zone %s, skipping %s", nat_zone.c_str(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + } + else + { + m_natZoneInterfaceInfo[port] = nat_zone; + + /* Add the mangle iptables rules for non-loopback interface */ + if (strncmp(keys[0].c_str(), LOOPBACK_PREFIX, strlen(LOOPBACK_PREFIX))) + { + setMangleIptablesRules(ADD, port, nat_zone); + } + } + } + else if (keys.size() == L3_INTERFACE_KEY_SIZE) + { + /* + * Don't proceed if Interface is not ready yet. + * The pending task will be checked periodically and retried. + */ + if (!isIntfStateOk(key)) + { + SWSS_LOG_INFO("Interface is not ready, skipping %s", key.c_str()); + it++; + continue; + } + + /* Check the key is present in ip_interface cache */ + if (m_natIpInterfaceInfo.find(port) != m_natIpInterfaceInfo.end()) + { + if (m_natIpInterfaceInfo[port].find(keys[1]) != m_natIpInterfaceInfo[port].end()) + { + SWSS_LOG_INFO("Duplicate Ip Interface, skipping %s", key.c_str()); + skipAddition = true; + } + else + { + for (auto it = m_natIpInterfaceInfo[port].begin(); it != m_natIpInterfaceInfo[port].end(); it++) + { + IpPrefix entry(keys[1]), prefix(*it); + + if (prefix.isAddressInSubnet(entry.getIp())) + { + SWSS_LOG_INFO("IP Address %s belongs to existing subnet, skipped adding entries", ipPrefixKeys[0].c_str()); + skipAddition = true; + break; + } + } + + m_natIpInterfaceInfo[port].insert(keys[1]); + SWSS_LOG_INFO("Ip Interface %s is added to the existing Port cache", key.c_str()); + } + } + else + { + m_natIpInterfaceInfo[port].insert(keys[1]); + SWSS_LOG_INFO("Ip Interface %s is added to the Port cache", key.c_str()); + } + + if (!skipAddition) + { + /* Add the Static NAT and NAPT entries */ + SWSS_LOG_INFO("Adding Static NAT entries for %s", key.c_str()); + addStaticNatEntries(keys[0], keys[1]); + + SWSS_LOG_INFO("Adding Static NAPT entries for %s", key.c_str()); + addStaticNaptEntries(keys[0], keys[1]); + + /* Add the Dynamic NAT rules */ + SWSS_LOG_INFO("Adding Dynamic NAT rules for %s", key.c_str()); + addDynamicNatRules(keys[0], keys[1]); + } + } + else + { + SWSS_LOG_INFO("Invalid key size %lu, skipping %s", keys.size(), key.c_str()); + } + it = consumer.m_toSync.erase(it); + } + else if (op == DEL_COMMAND) + { + SWSS_LOG_INFO("Del command for %s", key.c_str()); + + if (keys.size() == L3_INTERFACE_ZONE_SIZE) + { + /* Check the key is present in zone_interface cache*/ + if (m_natZoneInterfaceInfo.find(port) != m_natZoneInterfaceInfo.end()) + { + /* Set the mangle iptables rules for non-loopback interface */ + if (strncmp(keys[0].c_str(), LOOPBACK_PREFIX, strlen(LOOPBACK_PREFIX))) + { + setMangleIptablesRules(DELETE, port, m_natZoneInterfaceInfo[port]); + } + + SWSS_LOG_INFO("Nat Zone %s for Interface %s is cleaned from the cache", m_natZoneInterfaceInfo[port].c_str(), key.c_str()); + m_natZoneInterfaceInfo.erase(port); + } + else + { + SWSS_LOG_INFO("Zone Interface is not present in cache, skipping %s", key.c_str()); + } + } + else if (keys.size() == L3_INTERFACE_KEY_SIZE) + { + /* Check the key is present in ip_interface cache*/ + if (m_natIpInterfaceInfo.find(port) != m_natIpInterfaceInfo.end()) + { + if (m_natIpInterfaceInfo[port].find(keys[1]) != m_natIpInterfaceInfo[port].end()) + { + for (auto ipPrefix = m_natIpInterfaceInfo[port].begin(); ipPrefix != m_natIpInterfaceInfo[port].end(); ipPrefix++) + { + if (*ipPrefix == keys[1]) + { + continue; + } + + IpPrefix entry(keys[1]), prefix(*ipPrefix); + + if (prefix.isAddressInSubnet(entry.getIp())) + { + SWSS_LOG_INFO("IP Address %s belongs to existing subnet, skipping deleting the entries", ipPrefixKeys[0].c_str()); + skipDeletion = true; + break; + } + } + + if (!skipDeletion) + { + /* Delete the Static NAT and NAPT entries */ + SWSS_LOG_INFO("Deleting Static NAT entries for %s", key.c_str()); + removeStaticNatEntries(keys[0], keys[1]); + + SWSS_LOG_INFO("Deleting Static NAPT entries for %s", key.c_str()); + removeStaticNaptEntries(keys[0], keys[1]); + + /* Delete the Dynamic NAT rules */ + SWSS_LOG_INFO("Deleting Dynamic NAT rules for %s", key.c_str()); + removeDynamicNatRules(keys[0], keys[1]); + } + + m_natIpInterfaceInfo[port].erase(keys[1]); + SWSS_LOG_INFO("Ip Interface %s is cleaned from the existing Port cache", keys[1].c_str()); + + if (m_natIpInterfaceInfo[port].empty()) + { + m_natIpInterfaceInfo.erase(port); + SWSS_LOG_INFO("Ip Interface %s is cleaned from the cache", key.c_str()); + } + } + else + { + SWSS_LOG_INFO("Ip Interface %s from Config_Db not in cache, do nothing", keys[1].c_str()); + } + } + else + { + SWSS_LOG_INFO("Invalid Ip Interface %s from Config_Db, do nothing", key.c_str()); + } + } + else + { + SWSS_LOG_INFO("Invalid key size %lu, skipping %s", keys.size(), key.c_str()); + } + it = consumer.m_toSync.erase(it); + } + else + { + SWSS_LOG_INFO("Unknown operation type %s", op.c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +/* To parse the received ACL Table and save it to cache */ +void NatMgr::doNatAclTableTask(Consumer &consumer) +{ + auto it = consumer.m_toSync.begin(); + + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t), op = kfvOp(t), ports; + vector keys = tokenize(key, config_db_key_delimiter); + const vector& data = kfvFieldsValues(t); + bool isNatAclNotValid = false; + + /* Example : Config_DB + * ACL_TABLE|Table-Id + * policy_desc: nat_acl + * stage: INGRESS + * type: l3 + * ports: Ethernet10 + */ + + /* Ensure the key size is 1 otherwise ignore */ + if (keys.size() != ACL_TABLE_KEY_SIZE) + { + SWSS_LOG_INFO("Invalid key size %lu, skipping %s", keys.size(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (op == SET_COMMAND) + { + SWSS_LOG_INFO("Set command for %s", key.c_str()); + + /* Get the Config_db key values */ + for (auto idx : data) + { + const auto &field = to_upper(fvField(idx)); + const auto &value = fvValue(idx); + if (field == TABLE_TYPE) + { + /* Ensure the Table type is L3, otherwise ignore */ + if (to_upper(value) != TABLE_TYPE_L3) + { + isNatAclNotValid = true; + SWSS_LOG_INFO("Invalid table type %s, skipping %s", value.c_str(), key.c_str()); + break; + } + } + else if (field == TABLE_STAGE) + { + /* Ensure the Table stage is Ingress, otherwise ignore */ + if (to_upper(value) != TABLE_STAGE_INGRESS) + { + isNatAclNotValid = true; + SWSS_LOG_INFO("Invalid stage %s, skipping %s", value.c_str(), key.c_str()); + break; + } + } + else if (field == TABLE_PORTS) + { + ports = value; + } + } + + /* Ensure the Table ports starts with "Vlan" or "Ethernet" or "PortChannel" otherwise ignore */ + vector ports_list = tokenize(ports, comma); + for (string port : ports_list) + { + /* Ensure the key starts with "Vlan" or "Ethernet" or "PortChannel" otherwise ignore */ + if ((strncmp(port.c_str(), VLAN_PREFIX, strlen(VLAN_PREFIX))) and + (strncmp(port.c_str(), ETHERNET_PREFIX, strlen(ETHERNET_PREFIX))) and + (strncmp(port.c_str(), LAG_PREFIX, strlen(LAG_PREFIX)))) + { + SWSS_LOG_INFO("Invalid Port %s format, skipping %s", ports.c_str(), key.c_str()); + isNatAclNotValid = true; + break; + } + } + + /* Ensure the ACL Table is valid for NAT, otherwise ignore */ + if (isNatAclNotValid == true) + { + SWSS_LOG_INFO("Not a valid ACL Table for NAT, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Check the key is already present in cache */ + if (m_natAclTableInfo.find(key) != m_natAclTableInfo.end()) + { + SWSS_LOG_INFO("ACL Table %s exists, skipping", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* New Key, Add it to cache */ + m_natAclTableInfo[key] = ports; + SWSS_LOG_INFO("ACL Table Info %s is added to cache", key.c_str()); + + /* Add the new Dynamic Nat Rules */ + SWSS_LOG_INFO("Adding Dynamic NAT rules for %s", key.c_str()); + addDynamicNatRuleByAcl(key); + + it = consumer.m_toSync.erase(it); + } + else if (op == DEL_COMMAND) + { + SWSS_LOG_INFO("Del command for %s", key.c_str()); + + /* Check the key is already present in cache */ + if (m_natAclTableInfo.find(key) != m_natAclTableInfo.end()) + { + /* Remove the existing Dynamic NAT rules */ + SWSS_LOG_INFO("Deleting Dynamic NAT rules for %s", key.c_str()); + removeDynamicNatRuleByAcl(key); + + /* Clean the ACL Table info */ + m_natAclTableInfo.erase(key); + SWSS_LOG_INFO("ACL Table Info %s is cleaned from cache", key.c_str()); + } + else + { + SWSS_LOG_INFO("Invalid ACL Table %s from Config_Db, do nothing", key.c_str()); + } + + it = consumer.m_toSync.erase(it); + } + else + { + SWSS_LOG_INFO("Unknown operation type %s", op.c_str()); + SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +/* To parse the received ACL Rule Table and save it to cache */ +void NatMgr::doNatAclRuleTask(Consumer &consumer) +{ + auto it = consumer.m_toSync.begin(); + + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t), op = kfvOp(t); + vector keys = tokenize(key, config_db_key_delimiter); + const vector& data = kfvFieldsValues(t); + string aclSrcIpAddress = NONE_STRING, aclDstIpAddress = NONE_STRING, aclIpProtocol = NONE_STRING; + string aclSrcPort = NONE_STRING, aclDstPort = NONE_STRING, aclPacketAction; + bool isNatAclRuleNotValid = false, isPacketActionSet = false; + uint32_t ipv4_addr, aclPriority = 0; + uint8_t ip_protocol; + + /* Example : Config_DB + * ACL_Rule|Table-Id|Rule-Id + * priority: 55 + * ip_type: ipv4any + * src_ip: 10.10.0.26/32 + * dst_ip: 10.10.1.26/32 + * packet_action: forward + */ + + /* Ensure the key size is 2 otherwise ignore */ + if (keys.size() != ACL_RULE_TABLE_KEY_SIZE) + { + SWSS_LOG_INFO("Invalid key size %lu, skipping %s", keys.size(), key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (op == SET_COMMAND) + { + SWSS_LOG_INFO("Set command for %s", key.c_str()); + + /* Get the Config_db key values */ + for (auto idx : data) + { + const auto &field = to_upper(fvField(idx)); + const auto &value = fvValue(idx); + + if (field == ACTION_PACKET_ACTION) + { + /* Ensure the packet action is valid, otherwise ignore */ + if ((to_upper(value) != PACKET_ACTION_FORWARD) and (to_upper(value) != PACKET_ACTION_DO_NOT_NAT)) + { + isNatAclRuleNotValid = true; + SWSS_LOG_INFO("Invalid packet action %s for Packet Action Field, skipping %s", value.c_str(), key.c_str()); + break; + } + isPacketActionSet = true; + aclPacketAction = to_upper(value); + } + else if (field == MATCH_IP_TYPE) + { + /* Ensure the ip type is valid, otherwise ignore */ + if ((to_upper(value) != IP_TYPE_IP) and (to_upper(value) != IP_TYPE_IPv4ANY)) + { + isNatAclRuleNotValid = true; + SWSS_LOG_INFO("Invalid ip type %s for Matching IP Type Field, skipping %s", value.c_str(), key.c_str()); + break; + } + } + else if ((field == MATCH_SRC_IP) or (field == MATCH_DST_IP)) + { + vector acl_ip = tokenize(value, ip_address_delimiter); + + /* Ensure the ip format is x.x.x.x, otherwise ignore */ + if (inet_pton(AF_INET, acl_ip[0].c_str(), &ipv4_addr) != 1) + { + SWSS_LOG_INFO("Invalid ip address %s format for Matching IP Field, skipping %s", value.c_str(), key.c_str()); + isNatAclRuleNotValid = true; + break; + } + + /* Ensure the ip address is non-zero, otherwise ignore */ + if (ipv4_addr == 0) + { + SWSS_LOG_INFO("Invalid ip address %s for Matching IP Field, skipping %s", value.c_str(), key.c_str()); + isNatAclRuleNotValid = true; + break; + } + + if (acl_ip.size() > 1) + { + /* Ensure the mask length is valid otherwise ignore */ + if ((stoi(acl_ip[1]) < IP_ADDR_MASK_LEN_MIN) or (stoi(acl_ip[1]) > IP_ADDR_MASK_LEN_MAX)) + { + SWSS_LOG_INFO("Invalid ip address mask length for Matching IP Field, skipping %s", key.c_str()); + isNatAclRuleNotValid = true; + break; + } + } + + if (field == MATCH_SRC_IP) + { + aclSrcIpAddress = value; + } + else if (field == MATCH_DST_IP) + { + aclDstIpAddress = value; + } + } + else if (field == MATCH_IP_PROTOCOL) + { + ip_protocol = to_uint(value); + + /* Ensure the ip protocol is TCP, UDP or ICMP, otherwise ignore */ + if (ip_protocol == MATCH_IP_PROTOCOL_TCP) + { + aclIpProtocol = IP_PROTOCOL_TCP; + } + else if (ip_protocol == MATCH_IP_PROTOCOL_UDP) + { + aclIpProtocol = IP_PROTOCOL_UDP; + } + else if (ip_protocol == MATCH_IP_PROTOCOL_ICMP) + { + aclIpProtocol = IP_PROTOCOL_ICMP; + } + else + { + SWSS_LOG_INFO("Invalid ip protocol %d for Matching Ip Protocol Field, skipping %s", ip_protocol, key.c_str()); + isNatAclRuleNotValid = true; + break; + } + } + else if ((field == MATCH_L4_SRC_PORT) or (field == MATCH_L4_DST_PORT)) + { + /* Ensure the port is inbetween 1 to 65535, otherwise ignore */ + if ((stoi(value) < L4_PORT_MIN) or (stoi(value) > L4_PORT_MAX)) + { + SWSS_LOG_INFO("Invalid port value %s for Matching Port Field, skipping %s", value.c_str(), key.c_str()); + isNatAclRuleNotValid = true; + break; + } + + if (field == MATCH_L4_SRC_PORT) + { + aclSrcPort = value; + } + else if (field == MATCH_L4_DST_PORT) + { + aclDstPort = value; + } + } + else if ((field == MATCH_L4_SRC_PORT_RANGE) or (field == MATCH_L4_DST_PORT_RANGE)) + { + vector port_range = tokenize(value, range_specifier); + + /* Ensure the port range size is valid, otherwise ignore */ + if (port_range.size() != L4_PORT_RANGE_SIZE) + { + SWSS_LOG_INFO("Invalid port range size %lu for Matching Port Range Field, skipping %s", port_range.size(), key.c_str()); + isNatAclRuleNotValid = true; + break; + } + + /* Ensure the port is inbetween 1 to 65535, otherwise ignore */ + if ((stoi(port_range[0]) < L4_PORT_MIN) or (stoi(port_range[1]) > L4_PORT_MAX)) + { + SWSS_LOG_INFO("Invalid port range %s for Matching Port Range Field, skipping %s", value.c_str(), key.c_str()); + isNatAclRuleNotValid = true; + break; + } + + if (field == MATCH_L4_SRC_PORT_RANGE) + { + aclSrcPort = value; + } + else if (field == MATCH_L4_DST_PORT_RANGE) + { + aclDstPort = value; + } + } + else if (field == RULE_PRIORITY) + { + aclPriority = stoi(value); + } + else + { + isNatAclRuleNotValid = true; + break; + } + } + + /* Ensure the ACL Table Rule is valid for NAT, otherwise ignore */ + if (isNatAclRuleNotValid == true) + { + SWSS_LOG_INFO("Not a valid ACL Rule for NAT, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the ACL Rule has packet action for NAT, otherwise ignore */ + if (isPacketActionSet == false) + { + SWSS_LOG_INFO("Packet action is missing for NAT ACL, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Check the key is already present in cache */ + if (m_natAclRuleInfo.find(key) != m_natAclRuleInfo.end()) + { + /* Remove the existing Dynamic NAT rules */ + SWSS_LOG_INFO("Deleting Dynamic NAT rules for %s", key.c_str()); + removeDynamicNatRuleByAcl(key, true); + + /* Clean the ACL Rule info */ + m_natAclRuleInfo.erase(key); + SWSS_LOG_INFO("ACL Rule Info %s is cleaned from cache", key.c_str()); + } + + /* Save the ACL Rule info to cache */ + m_natAclRuleInfo[key].packet_action = aclPacketAction; + m_natAclRuleInfo[key].priority = aclPriority; + m_natAclRuleInfo[key].src_ip_range = aclSrcIpAddress; + m_natAclRuleInfo[key].dst_ip_range = aclDstIpAddress; + m_natAclRuleInfo[key].src_l4_port_range = aclSrcPort; + m_natAclRuleInfo[key].dst_l4_port_range = aclDstPort; + m_natAclRuleInfo[key].ip_protocol = aclIpProtocol; + SWSS_LOG_INFO("ACL Rule Info %s is added to cache", key.c_str()); + + /* Add the new Dynamic Nat Rules */ + SWSS_LOG_INFO("Adding Dynamic NAT rules for %s", key.c_str()); + addDynamicNatRuleByAcl(key, true); + + it = consumer.m_toSync.erase(it); + } + else if (op == DEL_COMMAND) + { + SWSS_LOG_INFO("Del command for %s", key.c_str()); + + /* Check the key is already present in cache */ + if (m_natAclRuleInfo.find(key) != m_natAclRuleInfo.end()) + { + /* Remove the existing Dynamic NAT rules */ + SWSS_LOG_INFO("Deleting Dynamic NAT rules for %s", key.c_str()); + removeDynamicNatRuleByAcl(key, true); + + /* Clean the ACL Rule info */ + m_natAclRuleInfo.erase(key); + SWSS_LOG_INFO("ACL Rule Info %s is cleaned from cache", key.c_str()); + } + else + { + SWSS_LOG_INFO("Invalid ACL Rule %s from Config_Db, do nothing", key.c_str()); + } + + it = consumer.m_toSync.erase(it); + } + else + { + SWSS_LOG_INFO("Unknown operation type %s", op.c_str()); + SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +/* To parse the received Table Task */ +void NatMgr::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + string table_name = consumer.getTableName(); + + if (table_name == CFG_STATIC_NAT_TABLE_NAME) + { + SWSS_LOG_INFO("Received update from CFG_STATIC_NAT_TABLE_NAME"); + doStaticNatTask(consumer); + } + else if (table_name == CFG_STATIC_NAPT_TABLE_NAME) + { + SWSS_LOG_INFO("Received update from CFG_STATIC_NAPT_TABLE_NAME"); + doStaticNaptTask(consumer); + } + else if (table_name == CFG_NAT_POOL_TABLE_NAME) + { + SWSS_LOG_INFO("Received update from CFG_NAT_POOL_TABLE_NAME"); + doNatPoolTask(consumer); + } + else if (table_name == CFG_NAT_BINDINGS_TABLE_NAME) + { + SWSS_LOG_INFO("Received update from CFG_NAT_BINDINGS_TABLE_NAME"); + doNatBindingTask(consumer); + } + else if (table_name == CFG_NAT_GLOBAL_TABLE_NAME) + { + SWSS_LOG_INFO("Received update from CFG_NAT_GLOBAL_TABLE_NAME"); + doNatGlobalTask(consumer); + } + else if ((table_name == CFG_INTF_TABLE_NAME) || (table_name == CFG_LAG_INTF_TABLE_NAME) || + (table_name == CFG_VLAN_INTF_TABLE_NAME) || (table_name == CFG_LOOPBACK_INTERFACE_TABLE_NAME)) + { + SWSS_LOG_INFO("Received update from CFG_INTF_TABLE_NAME"); + doNatIpInterfaceTask(consumer); + } + else if (table_name == CFG_ACL_TABLE_TABLE_NAME) + { + SWSS_LOG_INFO("Received update from CFG_ACL_TABLE_TABLE_NAME"); + doNatAclTableTask(consumer); + } + else if (table_name == CFG_ACL_RULE_TABLE_NAME) + { + SWSS_LOG_INFO("Received update from CFG_ACL_RULE_TABLE_NAME"); + doNatAclRuleTask(consumer); + } + else + { + SWSS_LOG_ERROR("Unknown config table %s ", table_name.c_str()); + throw runtime_error("NatMgr doTask failure."); + } +} + diff --git a/cfgmgr/natmgr.h b/cfgmgr/natmgr.h new file mode 100644 index 0000000000..8f6222a98f --- /dev/null +++ b/cfgmgr/natmgr.h @@ -0,0 +1,350 @@ +/* + * Copyright 2019 Broadcom Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NATMGR__ +#define __NATMGR__ + +#include "dbconnector.h" +#include "producerstatetable.h" +#include "orch.h" +#include "notificationproducer.h" +#include +#include +#include +#include + +namespace swss { + +#define STATIC_NAT_KEY_SIZE 1 +#define LOCAL_IP "local_ip" +#define TRANSLATED_IP "translated_ip" +#define NAT_TYPE "nat_type" +#define SNAT_NAT_TYPE "snat" +#define DNAT_NAT_TYPE "dnat" +#define TWICE_NAT_ID "twice_nat_id" +#define TWICE_NAT_ID_MIN 1 +#define TWICE_NAT_ID_MAX 9999 +#define ENTRY_TYPE "entry_type" +#define STATIC_ENTRY_TYPE "static" +#define DYNAMIC_ENTRY_TYPE "dynamic" +#define STATIC_NAPT_KEY_SIZE 3 +#define LOCAL_PORT "local_port" +#define TRANSLATED_L4_PORT "translated_l4_port" +#define TRANSLATED_SRC_IP "translated_src_ip" +#define TRANSLATED_SRC_L4_PORT "translated_src_l4_port" +#define TRANSLATED_DST_IP "translated_dst_ip" +#define TRANSLATED_DST_L4_PORT "translated_dst_l4_port" +#define POOL_TABLE_KEY_SIZE 1 +#define NAT_IP "nat_ip" +#define NAT_PORT "nat_port" +#define BINDING_TABLE_KEY_SIZE 1 +#define NAT_POOL "nat_pool" +#define NAT_ACLS "access_list" +#define VALUES "Values" +#define NAT_ADMIN_MODE "admin_mode" +#define NAT_ZONE "nat_zone" +#define NAT_TIMEOUT "nat_timeout" +#define NAT_TIMEOUT_MIN 300 +#define NAT_TIMEOUT_MAX 432000 +#define NAT_TIMEOUT_DEFAULT 600 +#define NAT_TCP_TIMEOUT "nat_tcp_timeout" +#define NAT_TCP_TIMEOUT_MIN 300 +#define NAT_TCP_TIMEOUT_MAX 432000 +#define NAT_TCP_TIMEOUT_DEFAULT 86400 +#define NAT_UDP_TIMEOUT "nat_udp_timeout" +#define NAT_UDP_TIMEOUT_MIN 120 +#define NAT_UDP_TIMEOUT_MAX 600 +#define NAT_UDP_TIMEOUT_DEFAULT 300 +#define L3_INTERFACE_KEY_SIZE 2 +#define L3_INTERFACE_ZONE_SIZE 1 +#define VLAN_PREFIX "Vlan" +#define LAG_PREFIX "PortChannel" +#define ETHERNET_PREFIX "Ethernet" +#define LOOPBACK_PREFIX "Loopback" +#define ACL_TABLE_KEY_SIZE 1 +#define TABLE_TYPE "TYPE" +#define TABLE_STAGE "STAGE" +#define TABLE_PORTS "PORTS" +#define TABLE_TYPE_L3 "L3" +#define TABLE_STAGE_INGRESS "INGRESS" +#define ACL_RULE_TABLE_KEY_SIZE 2 +#define ACTION_PACKET_ACTION "PACKET_ACTION" +#define PACKET_ACTION_FORWARD "FORWARD" +#define PACKET_ACTION_DO_NOT_NAT "DO_NOT_NAT" +#define MATCH_IP_TYPE "IP_TYPE" +#define IP_TYPE_IP "IP" +#define IP_TYPE_IPv4ANY "IPV4ANY" +#define RULE_PRIORITY "PRIORITY" +#define MATCH_SRC_IP "SRC_IP" +#define MATCH_DST_IP "DST_IP" +#define MATCH_IP_PROTOCOL "IP_PROTOCOL" +#define MATCH_IP_PROTOCOL_ICMP 1 +#define MATCH_IP_PROTOCOL_TCP 6 +#define MATCH_IP_PROTOCOL_UDP 17 +#define MATCH_L4_SRC_PORT "L4_SRC_PORT" +#define MATCH_L4_DST_PORT "L4_DST_PORT" +#define MATCH_L4_SRC_PORT_RANGE "L4_SRC_PORT_RANGE" +#define MATCH_L4_DST_PORT_RANGE "L4_DST_PORT_RANGE" +#define IP_PREFIX_SIZE 2 +#define IP_ADDR_MASK_LEN_MIN 1 +#define IP_ADDR_MASK_LEN_MAX 32 +#define IP_PROTOCOL_ICMP "icmp" +#define IP_PROTOCOL_TCP "tcp" +#define IP_PROTOCOL_UDP "udp" +#define L4_PORT_MIN 1 +#define L4_PORT_MAX 65535 +#define L4_PORT_RANGE_SIZE 2 +#define EMPTY_STRING "" +#define NONE_STRING "None" +#define ADD "A" +#define INSERT "I" +#define DELETE "D" +#define ENABLED "enabled" +#define DISABLED "disabled" +#define IS_LOOPBACK_ADDR(ipaddr) ((ipaddr & 0xFF000000) == 0x7F000000) +#define IS_MULTICAST_ADDR(ipaddr) ((ipaddr >= 0xE0000000) and (ipaddr <= 0xEFFFFFFF)) +#define IS_RESERVED_ADDR(ipaddr) (ipaddr >= 0xF0000000) +#define IS_ZERO_ADDR(ipaddr) (ipaddr == 0) +#define IS_BROADCAST_ADDR(ipaddr) (ipaddr == 0xFFFFFFFF) + +const char ip_address_delimiter = '/'; + +/* Pool Info */ +typedef struct { + std::string ip_range; + std::string port_range; +} natPool_t; + +/* Binding Info */ +typedef struct { + std::string pool_name; + std::string acl_name; + std::string nat_type; + std::string twice_nat_id; + std::string pool_interface; + std::string acl_interface; + std::string static_key; + bool twice_nat_added; +} natBinding_t; + +/* Static NAT Entry Info */ +typedef struct { + std::string local_ip; + std::string nat_type; + std::string twice_nat_id; + std::string interface; + std::string binding_key; + bool twice_nat_added; +} staticNatEntry_t; + +/* Static NAPT Entry Info */ +typedef struct { + std::string local_ip; + std::string local_port; + std::string nat_type; + std::string twice_nat_id; + std::string interface; + std::string binding_key; + bool twice_nat_added; +} staticNaptEntry_t; + +/* NAT ACL Table Rules Info */ +typedef struct{ + std::string packet_action; + uint32_t priority; + std::string src_ip_range; + std::string dst_ip_range; + std::string src_l4_port_range; + std::string dst_l4_port_range; + std::string ip_protocol; +} natAclRule_t; + +/* Containers to store NAT Info */ + +/* To store NAT Pool configuration, + * Key is "Pool_name" + * Value is "natPool_t" + */ +typedef std::map natPool_map_t; + +/* To store NAT Binding configuration, + * Key is "Binding_name" + * Value is "natBinding_t" + */ +typedef std::map natBinding_map_t; + +/* To store Static NAT configuration, + * Key is "Global_ip" (Eg. 65.55.45.1) + * Value is "staticNatEntry_t" + */ +typedef std::map staticNatEntry_map_t; + +/* To store Static NAPT configuration, + * Key is "Global_ip|ip_protocol|Global_port" (Eg. 65.55.45.1|TCP|500) + * Value is "staticNaptEntry_t" + */ +typedef std::map staticNaptEntry_map_t; + +/* To store NAT Ip Interface configuration, + * Key is "Port" (Eg. Ethernet1) + * Value is "ip_address_list" (Eg. 10.0.0.1/24,20.0.0.1/24) + */ +typedef std::map> natIpInterface_map_t; + +/* To store NAT ACL Table configuration, + * Key is "ACL_Table_Id" (Eg. 1) + * Value is "ports" (Eg. Ethernet4,Vlan10) + */ +typedef std::map natAclTable_map_t; + +/* To store NAT ACL Rules configuration, + * Key is "ACL_Tabel_Id|ACL_Rule_Id" (Eg. 1|1) + * Value is "natAclRule_t" + */ +typedef std::map natAclRule_map_t; + +/* To store NAT Zone Interface configuration, + * Key is "Port" (Eg. Ethernet1) + * Value is "nat_zone" (Eg. "1") + */ +typedef std::map natZoneInterface_map_t; + +/* Define NatMgr Class inherited from Orch Class */ +class NatMgr : public Orch +{ +public: + /* NatMgr Constructor */ + NatMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const std::vector &tableNames); + using Orch::doTask; + + /* Function to be called from signal handler on nat docker stop */ + void cleanupPoolIpTable(); + void cleanupMangleIpTables(); + bool isPortInitDone(DBConnector *app_db); + +private: + /* Declare APPL_DB, CFG_DB and STATE_DB tables */ + ProducerStateTable m_appNatTableProducer, m_appNaptTableProducer, m_appNatGlobalTableProducer; + ProducerStateTable m_appTwiceNatTableProducer, m_appTwiceNaptTableProducer; + Table m_statePortTable, m_stateLagTable, m_stateVlanTable, m_stateInterfaceTable, m_appNaptPoolIpTable; + std::shared_ptr flushNotifier; + + /* Declare containers to store NAT Info */ + int m_natTimeout; + int m_natTcpTimeout; + int m_natUdpTimeout; + std::string natAdminMode; + + natPool_map_t m_natPoolInfo; + natBinding_map_t m_natBindingInfo; + staticNatEntry_map_t m_staticNatEntry; + staticNaptEntry_map_t m_staticNaptEntry; + natIpInterface_map_t m_natIpInterfaceInfo; + natZoneInterface_map_t m_natZoneInterfaceInfo; + natAclTable_map_t m_natAclTableInfo; + natAclRule_map_t m_natAclRuleInfo; + + /* Declare doTask related fucntions */ + void doTask(Consumer &consumer); + void doStaticNatTask(Consumer &consumer); + void doStaticNaptTask(Consumer &consumer); + void doNatPoolTask(Consumer &consumer); + void doNatBindingTask(Consumer &consumer); + void doNatGlobalTask(Consumer &consumer); + void doNatIpInterfaceTask(Consumer &consumer); + void doNatAclTableTask(Consumer &consumer); + void doNatAclRuleTask(Consumer &consumer); + + /* Declare all NAT functionality member functions*/ + void enableNatFeature(void); + void disableNatFeature(void); + void addConntrackSingleNatEntry(const std::string &key); + void addConntrackSingleNaptEntry(const std::string &key); + void deleteConntrackSingleNatEntry(const std::string &key); + void deleteConntrackSingleNaptEntry(const std::string &key); + void addConntrackTwiceNatEntry(const std::string &snatKey, const std::string &dnatKey); + void addConntrackTwiceNaptEntry(const std::string &snatKey, const std::string &dnatKey); + void deleteConntrackTwiceNatEntry(const std::string &snatKey, const std::string &dnatKey); + void deleteConntrackTwiceNaptEntry(const std::string &snatKey, const std::string &dnatKey); + void deleteConntrackDynamicEntries(const std::string &ip_range); + void addStaticNatEntry(const std::string &key); + void addStaticNaptEntry(const std::string &key); + void addStaticSingleNatEntry(const std::string &key); + void addStaticSingleNaptEntry(const std::string &key); + void addStaticSingleNatIptables(const std::string &key); + void addStaticSingleNaptIptables(const std::string &key); + void addStaticTwiceNatEntry(const std::string &key); + void addStaticTwiceNaptEntry(const std::string &key); + void addStaticTwiceNatIptables(const std::string &key); + void addStaticTwiceNaptIptables(const std::string &key); + void removeStaticNatEntry(const std::string &key); + void removeStaticNaptEntry(const std::string &key); + void removeStaticSingleNatEntry(const std::string &key); + void removeStaticSingleNaptEntry(const std::string &key); + void removeStaticSingleNatIptables(const std::string &key); + void removeStaticSingleNaptIptables(const std::string &key); + void removeStaticTwiceNatEntry(const std::string &key); + void removeStaticTwiceNaptEntry(const std::string &key); + void removeStaticTwiceNatIptables(const std::string &key); + void removeStaticTwiceNaptIptables(const std::string &key); + void addStaticNatEntries(const std::string port = NONE_STRING, const std::string ipPrefix = NONE_STRING); + void addStaticNaptEntries(const std::string port = NONE_STRING, const std::string ipPrefix = NONE_STRING); + void removeStaticNatEntries(const std::string port = NONE_STRING, const std::string ipPrefix = NONE_STRING); + void removeStaticNaptEntries(const std::string port= NONE_STRING, const std::string ipPrefix = NONE_STRING); + void addStaticNatIptables(const std::string port); + void addStaticNaptIptables(const std::string port); + void removeStaticNatIptables(const std::string port); + void removeStaticNaptIptables(const std::string port); + void addDynamicNatRule(const std::string &key); + void removeDynamicNatRule(const std::string &key); + void addDynamicNatRuleByAcl(const std::string &key, bool isRuleId = false); + void removeDynamicNatRuleByAcl(const std::string &key, bool isRuleId = false); + void addDynamicNatRules(const std::string port = NONE_STRING, const std::string ipPrefix = NONE_STRING); + void removeDynamicNatRules(const std::string port = NONE_STRING, const std::string ipPrefix = NONE_STRING); + void addDynamicTwiceNatRule(const std::string &key); + void deleteDynamicTwiceNatRule(const std::string &key); + void setDynamicAllForwardOrAclbasedRules(const std::string &opCmd, const std::string &pool_interface, const std::string &ip_range, + const std::string &port_range, const std::string &acls_name, const std::string &dynamicKey); + + bool isNatEnabled(void); + bool isPortStateOk(const std::string &alias); + bool isIntfStateOk(const std::string &alias); + bool isPoolMappedtoBinding(const std::string &pool_name, std::string &binding_name); + bool isMatchesWithStaticNat(const std::string &global_ip, std::string &local_ip); + bool isMatchesWithStaticNapt(const std::string &global_ip, std::string &local_ip); + bool isGlobalIpMatching(const std::string &intf_keys, const std::string &global_ip); + bool getIpEnabledIntf(const std::string &global_ip, std::string &interface); + void setNaptPoolIpTable(const std::string &opCmd, const std::string &nat_ip, const std::string &nat_port); + bool setFullConeDnatIptablesRule(const std::string &opCmd); + bool setMangleIptablesRules(const std::string &opCmd, const std::string &interface, const std::string &nat_zone); + bool setStaticNatIptablesRules(const std::string &opCmd, const std::string &interface, const std::string &external_ip, const std::string &internal_ip, const std::string &nat_type); + bool setStaticNaptIptablesRules(const std::string &opCmd, const std::string &interface, const std::string &prototype, const std::string &external_ip, + const std::string &external_port, const std::string &internal_ip, const std::string &internal_port, const std::string &nat_type); + bool setStaticTwiceNatIptablesRules(const std::string &opCmd, const std::string &interface, const std::string &src_ip, const std::string &translated_src_ip, + const std::string &dest_ip, const std::string &translated_dest_ip); + bool setStaticTwiceNaptIptablesRules(const std::string &opCmd, const std::string &interface, const std::string &prototype, const std::string &src_ip, const std::string &src_port, + const std::string &translated_src_ip, const std::string &translated_src_port, const std::string &dest_ip, const std::string &dest_port, + const std::string &translated_dest_ip, const std::string &translated_dest_port); + bool setDynamicNatIptablesRulesWithAcl(const std::string &opCmd, const std::string &interface, const std::string &external_ip, + const std::string &external_port_range, natAclRule_t &natAclRuleId, const std::string &static_key); + bool setDynamicNatIptablesRulesWithoutAcl(const std::string &opCmd, const std::string &interface, const std::string &external_ip, + const std::string &external_port_range, const std::string &static_key); + +}; + +} + +#endif diff --git a/cfgmgr/natmgrd.cpp b/cfgmgr/natmgrd.cpp new file mode 100644 index 0000000000..5a685d9f6c --- /dev/null +++ b/cfgmgr/natmgrd.cpp @@ -0,0 +1,173 @@ +/* + * Copyright 2019 Broadcom Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "dbconnector.h" +#include "select.h" +#include "exec.h" +#include "schema.h" +#include "macaddress.h" +#include "producerstatetable.h" +#include "notificationproducer.h" +#include "natmgr.h" +#include "shellcmd.h" +#include "warm_restart.h" + +using namespace std; +using namespace swss; + +/* select() function timeout retry time, in millisecond */ +#define SELECT_TIMEOUT 1000 + +/* + * Following global variables are defined here for the purpose of + * using existing Orch class which is to be refactored soon to + * eliminate the direct exposure of the global variables. + * + * Once Orch class refactoring is done, these global variables + * should be removed from here. + */ +int gBatchSize = 0; +bool gSwssRecord = false; +bool gLogRotate = false; +ofstream gRecordOfs; +string gRecordFile; +mutex gDbMutex; +NatMgr *natmgr = NULL; + +std::shared_ptr cleanupNotifier; + +void sigterm_handler(int signo) +{ + int ret = 0; + std::string res; + const std::string iptablesFlushNat = "iptables -t nat -F"; + const std::string conntrackFlush = "conntrack -F"; + + SWSS_LOG_NOTICE("Got SIGTERM"); + + /*If there are any iptables and conntrack entries, clean them */ + ret = swss::exec(iptablesFlushNat, res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", iptablesFlushNat.c_str(), ret); + } + ret = swss::exec(conntrackFlush, res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", conntrackFlush.c_str(), ret); + } + + /* Send notification to Orchagent to clean up the REDIS and ASIC database */ + if (cleanupNotifier != NULL) + { + SWSS_LOG_NOTICE("Sending notification to orchagent to cleanup NAT entries in REDIS/ASIC"); + + std::vector entry; + + cleanupNotifier->send("nat_cleanup", "all", entry); + } + + if (natmgr) + { + natmgr->cleanupMangleIpTables(); + natmgr->cleanupPoolIpTable(); + } +} + +int main(int argc, char **argv) +{ + Logger::linkToDbNative("natmgrd"); + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("--- Starting natmgrd ---"); + + try + { + vector cfg_tables = { + CFG_STATIC_NAT_TABLE_NAME, + CFG_STATIC_NAPT_TABLE_NAME, + CFG_NAT_POOL_TABLE_NAME, + CFG_NAT_BINDINGS_TABLE_NAME, + CFG_NAT_GLOBAL_TABLE_NAME, + CFG_INTF_TABLE_NAME, + CFG_LAG_INTF_TABLE_NAME, + CFG_VLAN_INTF_TABLE_NAME, + CFG_LOOPBACK_INTERFACE_TABLE_NAME, + CFG_ACL_TABLE_TABLE_NAME, + CFG_ACL_RULE_TABLE_NAME + }; + + DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + + cleanupNotifier = std::make_shared(&appDb, "NAT_DB_CLEANUP_NOTIFICATION"); + + if (signal(SIGTERM, sigterm_handler) == SIG_ERR) + { + SWSS_LOG_ERROR("failed to setup SIGTERM action handler"); + exit(1); + } + + natmgr = new NatMgr(&cfgDb, &appDb, &stateDb, cfg_tables); + + natmgr->isPortInitDone(&appDb); + + std::vector cfgOrchList = {natmgr}; + + swss::Select s; + for (Orch *o : cfgOrchList) + { + s.addSelectables(o->getSelectables()); + } + + SWSS_LOG_NOTICE("starting main loop"); + while (true) + { + Selectable *sel; + int ret; + + ret = s.select(&sel, SELECT_TIMEOUT); + if (ret == Select::ERROR) + { + SWSS_LOG_NOTICE("Error: %s!", strerror(errno)); + continue; + } + if (ret == Select::TIMEOUT) + { + natmgr->doTask(); + continue; + } + + auto *c = (Executor *)sel; + c->execute(); + } + } + catch(const std::exception &e) + { + SWSS_LOG_ERROR("Runtime error: %s", e.what()); + } + return -1; +} + diff --git a/cfgmgr/shellcmd.h b/cfgmgr/shellcmd.h index a8a7afa51c..31fd7e3270 100644 --- a/cfgmgr/shellcmd.h +++ b/cfgmgr/shellcmd.h @@ -12,6 +12,8 @@ #define GREP_CMD "/bin/grep" #define TEAMD_CMD "/usr/bin/teamd" #define TEAMDCTL_CMD "/usr/bin/teamdctl" +#define IPTABLES_CMD "/sbin/iptables" +#define CONNTRACK_CMD "/usr/sbin/conntrack" #define EXEC_WITH_ERROR_THROW(cmd, res) ({ \ int ret = swss::exec(cmd, res); \ diff --git a/cfgmgr/teammgr.cpp b/cfgmgr/teammgr.cpp index 6121c30f0c..2120ab033f 100644 --- a/cfgmgr/teammgr.cpp +++ b/cfgmgr/teammgr.cpp @@ -492,13 +492,19 @@ task_process_status TeamMgr::addLag(const string &alias, int min_links, bool fal while (getline(aliasfile, line)) { ifstream memberfile(dump_path + line, ios::binary); - uint8_t mac_temp[ETHER_ADDR_LEN]; + uint8_t mac_temp[ETHER_ADDR_LEN] = {0}; + uint8_t null_mac[ETHER_ADDR_LEN] = {0}; if (!memberfile.is_open()) continue; memberfile.seekg(partner_system_id_offset, std::ios::beg); memberfile.read(reinterpret_cast(mac_temp), ETHER_ADDR_LEN); + + /* During negotiation stage partner info of pdu is empty , skip it */ + if (memcmp(mac_temp, null_mac, ETHER_ADDR_LEN) == 0) + continue; + mac_boot = MacAddress(mac_temp); break; } diff --git a/cfgmgr/vlanmgr.cpp b/cfgmgr/vlanmgr.cpp index d8834d12d9..888ced509a 100644 --- a/cfgmgr/vlanmgr.cpp +++ b/cfgmgr/vlanmgr.cpp @@ -209,7 +209,7 @@ bool VlanMgr::removeHostVlanMember(int vlan_id, const string &port_alias) IP_CMD " link set " << shellquote(port_alias) << " nomaster; " "elif [ $ret -eq 1 ]; then exit 0; " "else exit $ret; fi )"; - cmds << BASH_CMD " -c " << shellquote(cmds.str()); + cmds << BASH_CMD " -c " << shellquote(inner.str()); std::string res; EXEC_WITH_ERROR_THROW(cmds.str(), res); diff --git a/cfgmgr/vrfmgr.cpp b/cfgmgr/vrfmgr.cpp index d8718813ef..f0db83a04d 100644 --- a/cfgmgr/vrfmgr.cpp +++ b/cfgmgr/vrfmgr.cpp @@ -7,6 +7,7 @@ #include "vrfmgr.h" #include "exec.h" #include "shellcmd.h" +#include "warm_restart.h" #define VRF_TABLE_START 1001 #define VRF_TABLE_END 2000 @@ -58,9 +59,24 @@ VrfMgr::VrfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, con rowType = DETAILS_ROW; break; case DETAILS_ROW: - table = static_cast(stoul(items[6])); - m_vrfTableMap[vrfName] = table; - m_freeTables.erase(table); + if (WarmStart::isWarmStart()) + { + table = static_cast(stoul(items[6])); + m_vrfTableMap[vrfName] = table; + m_freeTables.erase(table); + } + else + { + SWSS_LOG_NOTICE("Remove vrf device %s", vrfName.c_str()); + cmd.str(""); + cmd.clear(); + cmd << IP_CMD << " link del " << vrfName; + int ret = swss::exec(cmd.str(), res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + } + } rowType = LINK_ROW; break; } diff --git a/cfgmgr/vrfmgrd.cpp b/cfgmgr/vrfmgrd.cpp index 6a347896b3..c81f7bdadd 100644 --- a/cfgmgr/vrfmgrd.cpp +++ b/cfgmgr/vrfmgrd.cpp @@ -8,6 +8,7 @@ #include "vrfmgr.h" #include #include +#include "warm_restart.h" using namespace std; using namespace swss; @@ -49,6 +50,9 @@ int main(int argc, char **argv) DBConnector appDb("APPL_DB", 0); DBConnector stateDb("STATE_DB", 0); + WarmStart::initialize("vrfmgrd", "swss"); + WarmStart::checkWarmStart("vrfmgrd", "swss"); + VrfMgr vrfmgr(&cfgDb, &appDb, &stateDb, cfg_vrf_tables); // TODO: add tables in stateDB which interface depends on to monitor list diff --git a/configure.ac b/configure.ac index 04899b0249..7faa50b995 100644 --- a/configure.ac +++ b/configure.ac @@ -79,6 +79,7 @@ AC_CONFIG_FILES([ orchagent/Makefile fpmsyncd/Makefile neighsyncd/Makefile + natsyncd/Makefile portsyncd/Makefile teamsyncd/Makefile swssconfig/Makefile diff --git a/natsyncd/Makefile.am b/natsyncd/Makefile.am new file mode 100644 index 0000000000..1740d8b097 --- /dev/null +++ b/natsyncd/Makefile.am @@ -0,0 +1,16 @@ +INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/warmrestart + +bin_PROGRAMS = natsyncd + +if DEBUG +DBGFLAGS = -ggdb -DDEBUG +else +DBGFLAGS = -g +endif + +natsyncd_SOURCES = natsyncd.cpp natsync.cpp $(top_srcdir)/warmrestart/warmRestartAssist.cpp + +natsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) +natsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) +natsyncd_LDADD = -lnl-3 -lnl-route-3 -lnl-nf-3 -lswsscommon + diff --git a/natsyncd/natsync.cpp b/natsyncd/natsync.cpp new file mode 100644 index 0000000000..1db37c5c26 --- /dev/null +++ b/natsyncd/natsync.cpp @@ -0,0 +1,996 @@ +/* + * Copyright 2019 Broadcom Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "logger.h" +#include "dbconnector.h" +#include "producerstatetable.h" +#include "ipaddress.h" +#include "netmsg.h" +#include "linkcache.h" + +#include "natsync.h" +#include "warm_restart.h" + +using namespace std; +using namespace swss; + +#define NL_IP_ADDR(addrp) (((struct nl_ip_addr *)addrp)->a_addr) +#define IS_LOOPBACK_ADDR(ipaddr) ((ipaddr & 0xFF000000) == 0x7F000000) + +#define CT_UDP_EXPIRY_TIMEOUT 600 /* Max conntrack timeout in the user configurable range */ + +NatSync::NatSync(RedisPipeline *pipelineAppDB, DBConnector *appDb, DBConnector *stateDb, NfNetlink *nfnl) : + m_natTable(appDb, APP_NAT_TABLE_NAME), + m_naptTable(appDb, APP_NAPT_TABLE_NAME), + m_natTwiceTable(appDb, APP_NAT_TWICE_TABLE_NAME), + m_naptTwiceTable(appDb, APP_NAPT_TWICE_TABLE_NAME), + m_natCheckTable(appDb, APP_NAT_TABLE_NAME), + m_naptCheckTable(appDb, APP_NAPT_TABLE_NAME), + m_twiceNatCheckTable(appDb, APP_NAT_TWICE_TABLE_NAME), + m_twiceNaptCheckTable(appDb, APP_NAPT_TWICE_TABLE_NAME), + m_naptPoolCheckTable(appDb, APP_NAPT_POOL_IP_TABLE_NAME), + m_stateNatRestoreTable(stateDb, STATE_NAT_RESTORE_TABLE_NAME) +{ + nfsock = nfnl; + + m_AppRestartAssist = new AppRestartAssist(pipelineAppDB, "natsyncd", "nat", DEFAULT_NATSYNC_WARMSTART_TIMER); + if (m_AppRestartAssist) + { + m_AppRestartAssist->registerAppTable(APP_NAT_TABLE_NAME, &m_natTable); + m_AppRestartAssist->registerAppTable(APP_NAPT_TABLE_NAME, &m_naptTable); + m_AppRestartAssist->registerAppTable(APP_NAT_TWICE_TABLE_NAME, &m_natTwiceTable); + m_AppRestartAssist->registerAppTable(APP_NAPT_TWICE_TABLE_NAME, &m_naptTwiceTable); + } +} + +/* To check the port init is done or not */ +bool NatSync::isPortInitDone(DBConnector *app_db) +{ + bool portInit = 0; + long cnt = 0; + + while(!portInit) { + Table portTable(app_db, APP_PORT_TABLE_NAME); + std::vector tuples; + portInit = portTable.get("PortInitDone", tuples); + + if(portInit) + break; + sleep(1); + cnt++; + } + sleep(5); + SWSS_LOG_NOTICE("PORT_INIT_DONE : %d %ld", portInit, cnt); + return portInit; +} + +// Check if nat conntrack entries are restored in kernel +bool NatSync::isNatRestoreDone() +{ + SWSS_LOG_ENTER(); + + string value; + + m_stateNatRestoreTable.hget("Flags", "restored", value); + if (value == "true") + { + SWSS_LOG_NOTICE("Conntrack table restore for NAT entries to kernel is complete"); + return true; + } + return false; +} + +std::string ctStatusStr(uint32_t ct_status) +{ + string ct_status_str = ""; + + if (ct_status & IPS_EXPECTED) + ct_status_str += "EXPECTED,"; + + if (!(ct_status & IPS_SEEN_REPLY)) + ct_status_str += "NOREPLY,"; + + if (ct_status & IPS_ASSURED) + ct_status_str += "ASSURED,"; + + if (!(ct_status & IPS_CONFIRMED)) + ct_status_str += "NOTSENT,"; + + if (ct_status & IPS_SRC_NAT) + ct_status_str += "SNAT,"; + + if (ct_status & IPS_DST_NAT) + ct_status_str += "DNAT,"; + + if (ct_status & IPS_SEQ_ADJUST) + ct_status_str += "SEQADJUST,"; + + if (!(ct_status & IPS_SRC_NAT_DONE)) + ct_status_str += "SNAT_INIT,"; + + if (!(ct_status & IPS_DST_NAT_DONE)) + ct_status_str += "DNAT_INIT,"; + + if (ct_status & IPS_DYING) + ct_status_str += "DYING,"; + + if (ct_status & IPS_FIXED_TIMEOUT) + ct_status_str += "FIXED_TIMEOUT"; + + return ct_status_str; +} + +/* Parse the valid conntrack notifications that can be added to hardware NAT table */ +int NatSync::parseConnTrackMsg(const struct nfnl_ct *ct, struct naptEntry &entry) +{ + SWSS_LOG_ENTER(); + + string ct_status_str; + char proto_str[32] = {0}; + + /* Only IPv4 family connections are handled */ + if (nfnl_ct_get_family(ct) != AF_INET) + { + SWSS_LOG_DEBUG("Conntrack entry protocol is not AF_INET (%d)", entry.protocol); + return -1; + } + + /* If the connection is not subjected to either SNAT or DNAT, + * we are not interested in those connection entries. + */ + entry.ct_status = nfnl_ct_get_status(ct); + + if (! ((entry.ct_status & IPS_SRC_NAT_DONE) || (entry.ct_status & IPS_DST_NAT_DONE))) + { + SWSS_LOG_DEBUG("Conntrack entry is not SNAT or DNAT"); + return -1; + } + + entry.orig_src_ip = NL_IP_ADDR(nfnl_ct_get_src(ct, 0)); + entry.orig_dest_ip = NL_IP_ADDR(nfnl_ct_get_dst(ct, 0)); + entry.nat_src_ip = NL_IP_ADDR(nfnl_ct_get_dst(ct, 1)); + entry.nat_dest_ip = NL_IP_ADDR(nfnl_ct_get_src(ct, 1)); + + /* Ignore those conntrack entries that correspond to internal loopback socket + * connections in the system. ie., if source ip or destination ip are in 127.0.0.X. + * Ideally, such connections would not have been subjected to SNAT/DNAT and should + * have been ignored in the above check already. + */ + if (((IS_LOOPBACK_ADDR(ntohl(entry.orig_src_ip.getV4Addr()))) && + (IS_LOOPBACK_ADDR(ntohl(entry.orig_dest_ip.getV4Addr())))) || + ((IS_LOOPBACK_ADDR(ntohl(entry.nat_src_ip.getV4Addr()))) && + (IS_LOOPBACK_ADDR(ntohl(entry.nat_dest_ip.getV4Addr()))))) + { + SWSS_LOG_DEBUG("Conntrack entry is a loopback entry, ignoring it."); + return -1; + } + + entry.orig_src_l4_port = nfnl_ct_get_src_port(ct, 0); + entry.orig_dst_l4_port = nfnl_ct_get_dst_port(ct, 0); + entry.nat_src_l4_port = nfnl_ct_get_dst_port(ct, 1); + entry.nat_dst_l4_port = nfnl_ct_get_src_port(ct, 1); + + entry.protocol = nfnl_ct_get_proto(ct); + entry.conntrack_id = nfnl_ct_get_id(ct); + ct_status_str = ctStatusStr(nfnl_ct_get_status(ct)); + + nl_ip_proto2str(entry.protocol, proto_str, sizeof(proto_str)); + + if ((entry.protocol == IPPROTO_TCP) || (entry.protocol == IPPROTO_UDP)) + { + SWSS_LOG_INFO("Conntrack entry : protocol %s, src %s:%d, dst %s:%d, natted src %s:%d, dst %s:%d, CT status %s", + proto_str, entry.orig_src_ip.to_string().c_str(), entry.orig_src_l4_port, + entry.orig_dest_ip.to_string().c_str(), entry.orig_dst_l4_port, + entry.nat_src_ip.to_string().c_str(), entry.nat_src_l4_port, + entry.nat_dest_ip.to_string().c_str(), entry.nat_dst_l4_port, ct_status_str.c_str()); + } + else if (entry.protocol == IPPROTO_ICMP) + { + /* Don't add ICMP NAT entries to hardware */ + SWSS_LOG_INFO("Conntrack entry : protocol icmp, src %s, dst %s, icmp_type %d, code %d, icmp_id %d, \ + natted src %s, dst %s, icmp_type %d, code %d, icmp_id %d, CT status %s", + entry.orig_src_ip.to_string().c_str(), entry.orig_dest_ip.to_string().c_str(), + nfnl_ct_get_icmp_type(ct, 0), nfnl_ct_get_icmp_code(ct, 0), nfnl_ct_get_icmp_id(ct, 0), + entry.nat_src_ip.to_string().c_str(), entry.nat_dest_ip.to_string().c_str(), + nfnl_ct_get_icmp_type(ct, 1), nfnl_ct_get_icmp_code(ct, 1), nfnl_ct_get_icmp_id(ct, 1), + ct_status_str.c_str()); + + return -1; + } + if (! (entry.ct_status & IPS_CONFIRMED)) + { + SWSS_LOG_INFO("Conntrack entry is not CONFIRMED (not went out of the box, don't process them)"); + return -1; + } + return 0; +} + +/* Process the netfiliter conntrack notifications from the kernel via netlink */ +void NatSync::onMsg(int nlmsg_type, struct nl_object *obj) +{ + SWSS_LOG_ENTER(); + + struct nfnl_ct *ct = (struct nfnl_ct *)obj; + struct naptEntry napt; + + nlmsg_type = NFNL_MSG_TYPE(nlmsg_type); + + SWSS_LOG_DEBUG("Conntrack entry notification, msg type :%s (%d)", + (((nlmsg_type == IPCTNL_MSG_CT_NEW) ? "CT_NEW" : ((nlmsg_type == IPCTNL_MSG_CT_DELETE) ? "CT_DELETE" : "OTHER"))), + nlmsg_type); + + if ((nlmsg_type != IPCTNL_MSG_CT_NEW) && (nlmsg_type != IPCTNL_MSG_CT_DELETE)) + { + SWSS_LOG_DEBUG("Conntrack entry notification, msg type not NEW or DELETE, ignoring"); + + } + + /* Parse the conntrack notification from the kernel */ + if (-1 == parseConnTrackMsg(ct, napt)) + { + return; + } + + if (nlmsg_type == IPCTNL_MSG_CT_NEW) + { + if ((napt.protocol == IPPROTO_TCP) && (napt.ct_status & IPS_ASSURED)) + { + addNatEntry(ct, napt, 1); + } + else if (napt.protocol == IPPROTO_UDP) + { + if (0 == addNatEntry(ct, napt, 1)) + { + if (! (napt.ct_status & IPS_ASSURED)) + { + /* Update the connection tracking entry status to ASSURED for UDP connection. + * Since application takes care of timing it out, and we don't want the kernel + * to age the UDP entries prematurely. + */ + napt.ct_status |= (IPS_SEEN_REPLY | IPS_ASSURED); + + nfnl_ct_set_status(ct, napt.ct_status); + nfnl_ct_set_timeout(ct, CT_UDP_EXPIRY_TIMEOUT); + + updateConnTrackEntry(ct); + } + } + } + } + else if ((nlmsg_type == IPCTNL_MSG_CT_DELETE) && (napt.ct_status & IPS_ASSURED)) + { + /* Delete only ASSURED NAT entries from APP_DB */ + addNatEntry(ct, napt, 0); + } +} + +/* Conntrack notifications from the kernel don't have a flag to indicate if the + * NAT is NAPT or basic NAT. The original L4 port and the translated L4 port may + * be the same and still can be the NAPT (can happen if the original L4 port is + * already in the L4 port pool range). To find out if it is a case of NAPT, we + * check if the Nat'ted IP is one of the NAPT pool range IP addresses, or if + * there is no static or dynamic NAT for that IP address. If both checks are not + * met, then it is the case of NAT basic translation where only IP address is NAT'ted. */ +bool NatSync::matchingSnaptPoolExists(const IpAddress &natIp) +{ + string key = natIp.to_string(); + std::vector values; + + if (m_naptPoolCheckTable.get(key, values)) + { + SWSS_LOG_INFO("Matching pool IP exists for NAT IP %s", key.c_str()); + return true; + } + + return false; +} + +bool NatSync::matchingSnaptEntryExists(const naptEntry &entry) +{ + string key = entry.orig_src_ip.to_string() + ":" + to_string(entry.orig_src_l4_port); + string reverseEntryKey = entry.nat_src_ip.to_string() + ":" + to_string(entry.nat_src_l4_port); + std::vector values; + + if (m_naptCheckTable.get(key, values) || m_naptCheckTable.get(reverseEntryKey, values)) + { + SWSS_LOG_INFO("Matching SNAPT entry exists for key %s or reverse key %s", + key.c_str(), reverseEntryKey.c_str()); + return true; + } + return false; +} + +bool NatSync::matchingDnaptEntryExists(const naptEntry &entry) +{ + string key = entry.orig_dest_ip.to_string() + ":" + to_string(entry.orig_dst_l4_port); + string reverseEntryKey = entry.nat_dest_ip.to_string() + ":" + to_string(entry.nat_dst_l4_port); + std::vector values; + + if (m_naptCheckTable.get(key, values) || m_naptCheckTable.get(reverseEntryKey, values)) + { + SWSS_LOG_INFO("Matching DNAPT entry exists for key %s or reverse key %s", + key.c_str(), reverseEntryKey.c_str()); + return true; + } + return false; +} + +/* Add the NAT entries to APP_DB based on the criteria: + * ---------------------------------------------------- + * - If only source ip changed, add it as SNAT entry. + * - If only destination ip changed, add it as DNAT entry. + * - If SNAT happened and the l4 port changes or part of any dynamic pool range + * or if there is matching static or dynamic NAPT entry, add it as SNAPT entry. + * - If DNAT happened and the l4 port changes or if there is matching static + * or dynamic NAPT entry, add it as DNAPT entry. + * - If SNAT and DNAT happened, add it as Twice NAT entry. + * - If SNAPT and DNAPT conditions are met or if there is no static + * or dynamic Twice NAT entry, then it is a Twice NAPT entry. + */ +int NatSync::addNatEntry(struct nfnl_ct *ct, struct naptEntry &entry, bool addFlag) +{ + SWSS_LOG_ENTER(); + + bool src_ip_natted = (entry.orig_src_ip != entry.nat_src_ip); + bool dst_ip_natted = (entry.orig_dest_ip != entry.nat_dest_ip); + bool src_port_natted = (src_ip_natted && ((entry.orig_src_l4_port != entry.nat_src_l4_port) || + (matchingSnaptPoolExists(entry.nat_src_ip)) || + (matchingSnaptEntryExists(entry)))); + bool dst_port_natted = (dst_ip_natted && ((entry.orig_dst_l4_port != entry.nat_dst_l4_port) || + (matchingDnaptEntryExists(entry)))); + bool entryExists = 0, reverseEntryExists = 0; + + SWSS_LOG_INFO("Flags: src natted %d, dst natted %d, src port natted %d, dst port natted %d", src_ip_natted, dst_ip_natted, src_port_natted, dst_port_natted); + + std::vector fvVector, reverseFvVector; + string protostr = ((entry.protocol == IPPROTO_TCP) ? "TCP:" : "UDP:"); + string opStr = ((addFlag) ? "CREATE" : "DELETE"); + string key = "", reverseEntryKey = ""; + + FieldValueTuple snat_type("nat_type", "snat"); + FieldValueTuple dnat_type("nat_type", "dnat"); + FieldValueTuple dynamic_entry("entry_type", "dynamic"); + + if (src_ip_natted && dst_ip_natted) + { + if (addFlag) + { + FieldValueTuple translated_src_ip("translated_src_ip", entry.nat_src_ip.to_string()); + FieldValueTuple translated_dst_ip("translated_dst_ip", entry.nat_dest_ip.to_string()); + FieldValueTuple reverse_translated_src_ip("translated_src_ip", entry.orig_dest_ip.to_string()); + FieldValueTuple reverse_translated_dst_ip("translated_dst_ip", entry.orig_src_ip.to_string()); + + fvVector.push_back(dynamic_entry); + fvVector.push_back(translated_src_ip); + fvVector.push_back(translated_dst_ip); + + reverseFvVector.push_back(dynamic_entry); + reverseFvVector.push_back(reverse_translated_src_ip); + reverseFvVector.push_back(reverse_translated_dst_ip); + } + + string tmpKey = key + entry.orig_src_ip.to_string() + ":" + entry.orig_dest_ip.to_string(); + string tmpReverseEntryKey = reverseEntryKey + entry.nat_dest_ip.to_string() + ":" + entry.nat_src_ip.to_string(); + + std::vector values; + if (m_twiceNatCheckTable.get(tmpKey, values)) + { + src_port_natted = dst_port_natted = false; + + for (auto iter : values) + { + /* If a matching Static Twice NAT entry exists in the APP_DB, + * it has higher priority than the dynamic twice nat entry. */ + if ((fvField(iter) == "entry_type") && (fvValue(iter) == "static")) + { + SWSS_LOG_INFO("Static Twice NAT %s: entry exists, not processing twice NAT entry notification", opStr.c_str()); + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_NAT_TWICE_TABLE_NAME, tmpKey, fvVector, (!addFlag)); + m_AppRestartAssist->insertToMap(APP_NAT_TWICE_TABLE_NAME, tmpReverseEntryKey, reverseFvVector, (!addFlag)); + } + return 1; + } + } + if (addFlag) + { + SWSS_LOG_INFO("Twice SNAT CREATE: ignoring the duplicated Twice SNAT notification"); + return 1; + } + } + + if (src_port_natted || dst_port_natted) + { + /* Case of Twice NAPT entry, where both the SIP, DIP and + * the L4 port(s) are NAT'ted. */ + SWSS_LOG_INFO("Twice NAPT %s conntrack notification", opStr.c_str()); + + key += protostr + entry.orig_src_ip.to_string(); + reverseEntryKey += protostr + entry.nat_dest_ip.to_string(); + + string src_l4_port = std::to_string(entry.orig_src_l4_port); + string dst_l4_port = std::to_string(entry.orig_dst_l4_port); + string nat_src_l4_port = std::to_string(entry.nat_src_l4_port); + string nat_dst_l4_port = std::to_string(entry.nat_dst_l4_port); + + key += ":" + src_l4_port + ":" + entry.orig_dest_ip.to_string() + + ":" + dst_l4_port; + reverseEntryKey += ":" + nat_dst_l4_port + ":" + entry.nat_src_ip.to_string() + + ":" + nat_src_l4_port; + + std::vector values; + /* If a matching Static Twice NAPT entry exists in the APP_DB, + * it has higher priority than the dynamic twice napt entry. */ + if (m_twiceNaptCheckTable.get(key, values)) + { + for (auto iter : values) + { + if ((fvField(iter) == "entry_type") && (fvValue(iter) == "static")) + { + SWSS_LOG_INFO("Static Twice NAPT %s: entry exists, not processing dynamic twice NAPT entry", opStr.c_str()); + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_NAPT_TWICE_TABLE_NAME, key, fvVector, (!addFlag)); + m_AppRestartAssist->insertToMap(APP_NAPT_TWICE_TABLE_NAME, reverseEntryKey, reverseFvVector, (!addFlag)); + } + return 1; + } + } + if (addFlag) + { + SWSS_LOG_INFO("Twice SNAPT CREATE: ignoring the duplicated Twice SNAPT notification"); + return 1; + } + } + if (addFlag) + { + FieldValueTuple translated_src_port("translated_src_l4_port", nat_src_l4_port); + FieldValueTuple translated_dst_port("translated_dst_l4_port", nat_dst_l4_port); + FieldValueTuple reverse_translated_src_port("translated_src_l4_port", dst_l4_port); + FieldValueTuple reverse_translated_dst_port("translated_dst_l4_port", src_l4_port); + + fvVector.push_back(translated_src_port); + fvVector.push_back(translated_dst_port); + reverseFvVector.push_back(reverse_translated_src_port); + reverseFvVector.push_back(reverse_translated_dst_port); + + + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_NAPT_TWICE_TABLE_NAME, key, fvVector, false); + m_AppRestartAssist->insertToMap(APP_NAPT_TWICE_TABLE_NAME, reverseEntryKey, reverseFvVector, false); + } + else + { + m_naptTwiceTable.set(key, fvVector); + SWSS_LOG_NOTICE("Twice NAPT entry with key %s added to APP_DB", key.c_str()); + m_naptTwiceTable.set(reverseEntryKey, reverseFvVector); + SWSS_LOG_NOTICE("Twice NAPT entry with reverse key %s added to APP_DB", reverseEntryKey.c_str()); + } + } + else + { + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_NAPT_TWICE_TABLE_NAME, key, fvVector, true); + m_AppRestartAssist->insertToMap(APP_NAPT_TWICE_TABLE_NAME, reverseEntryKey, reverseFvVector, true); + } + else + { + m_naptTwiceTable.del(key); + SWSS_LOG_NOTICE("Twice NAPT entry with key %s deleted from APP_DB", key.c_str()); + m_naptTwiceTable.del(reverseEntryKey); + SWSS_LOG_NOTICE("Twice NAPT entry with reverse key %s deleted from APP_DB", reverseEntryKey.c_str()); + } + } + } + else + { + /* Case of Twice NAT entry, where only the SIP, DIP are + * NAT'ted but the port translation is not done. */ + SWSS_LOG_INFO("Twice NAT %s conntrack notification", opStr.c_str()); + + key = tmpKey; + reverseEntryKey = tmpReverseEntryKey; + + if (addFlag) + { + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_NAT_TWICE_TABLE_NAME, key, fvVector, false); + m_AppRestartAssist->insertToMap(APP_NAT_TWICE_TABLE_NAME, reverseEntryKey, reverseFvVector, false); + } + else + { + m_natTwiceTable.set(key, fvVector); + SWSS_LOG_NOTICE("Twice NAT entry with key %s added to APP_DB", key.c_str()); + m_natTwiceTable.set(reverseEntryKey, reverseFvVector); + SWSS_LOG_NOTICE("Twice NAT entry with reverse key %s added to APP_DB", reverseEntryKey.c_str()); + } + } + else + { + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_NAT_TWICE_TABLE_NAME, key, fvVector, true); + m_AppRestartAssist->insertToMap(APP_NAT_TWICE_TABLE_NAME, reverseEntryKey, reverseFvVector, true); + } + else + { + m_natTwiceTable.del(key); + SWSS_LOG_NOTICE("Twice NAT entry with key %s deleted from APP_DB", key.c_str()); + m_natTwiceTable.del(reverseEntryKey); + SWSS_LOG_NOTICE("Twice NAT entry with reverse key %s deleted from APP_DB", reverseEntryKey.c_str()); + } + } + } + } + else if (src_ip_natted || dst_ip_natted) + { + if (src_ip_natted) + { + if (addFlag) + { + FieldValueTuple snat_translated_ip("translated_ip", entry.nat_src_ip.to_string()); + FieldValueTuple dnat_translated_ip("translated_ip", entry.orig_src_ip.to_string()); + + fvVector.push_back(snat_type); + fvVector.push_back(dynamic_entry); + fvVector.push_back(snat_translated_ip); + + reverseFvVector.push_back(dnat_type); + reverseFvVector.push_back(dynamic_entry); + reverseFvVector.push_back(dnat_translated_ip); + } + + if (src_port_natted) + { + key += protostr + entry.orig_src_ip.to_string(); + reverseEntryKey += protostr + entry.nat_src_ip.to_string(); + + /* Case of NAPT entry, where SIP is NAT'ted and the L4 port is NAT'ted. */ + SWSS_LOG_INFO("SNAPT %s conntrack notification", opStr.c_str()); + + string src_l4_port = std::to_string(entry.orig_src_l4_port); + string nat_src_l4_port = std::to_string(entry.nat_src_l4_port); + + key += ":" + src_l4_port; + reverseEntryKey += ":" + nat_src_l4_port; + + std::vector values; + /* We check for existence of reverse nat entry in the app-db because the same dnat static entry + * would be reported as snat entry from the kernel if a packet that is forwarded in the kernel + * is matched by the iptables rules corresponding to the dnat static entry */ + if (! m_AppRestartAssist->isWarmStartInProgress()) + { + if ((entryExists = m_naptCheckTable.get(key, values))) + { + for (auto iter : values) + { + if ((fvField(iter) == "entry_type") && (fvValue(iter) == "static")) + { + /* If a matching Static NAPT entry exists in the APP_DB, + * it has higher priority than the dynamic napt entry. */ + SWSS_LOG_INFO("SNAPT %s: static entry exists, not processing the NAPT notification", opStr.c_str()); + return 1; + } + } + if (addFlag) + { + SWSS_LOG_INFO("SNAPT CREATE: ignoring the duplicated SNAPT notification"); + return 1; + } + else + { + /* Skip entry, if entry contains loopback destination address */ + if ((IS_LOOPBACK_ADDR(ntohl(entry.orig_dest_ip.getV4Addr()))) || + (IS_LOOPBACK_ADDR(ntohl(entry.nat_dest_ip.getV4Addr())))) + { + SWSS_LOG_INFO("SNAPT %s: static entry contains loopback address, ignoring the notification", opStr.c_str()); + return 1; + } + else + { + m_naptTable.del(key); + SWSS_LOG_NOTICE("SNAPT entry with key %s deleted from APP_DB", key.c_str()); + } + } + } + if ((reverseEntryExists = m_naptCheckTable.get(reverseEntryKey, values))) + { + for (auto iter : values) + { + if ((fvField(iter) == "entry_type") && (fvValue(iter) == "static")) + { + /* If a matching Static NAPT entry exists in the APP_DB, + * it has higher priority than the dynamic napt entry. */ + SWSS_LOG_INFO("SNAPT %s: static reverse entry exists, not processing dynamic NAPT entry", opStr.c_str()); + return 1; + } + } + if (addFlag) + { + SWSS_LOG_INFO("SNAPT CREATE: ignoring the duplicated SNAPT notification"); + return 1; + } + else + { + /* Skip entry, if entry contains loopback destination address */ + if ((IS_LOOPBACK_ADDR(ntohl(entry.orig_dest_ip.getV4Addr()))) || + (IS_LOOPBACK_ADDR(ntohl(entry.nat_dest_ip.getV4Addr())))) + { + SWSS_LOG_INFO("SNAPT %s: static entry contains loopback address, ignoring the notification", opStr.c_str()); + return 1; + } + else + { + m_naptTable.del(reverseEntryKey); + SWSS_LOG_NOTICE("Implicit DNAPT entry with key %s deleted from APP_DB", reverseEntryKey.c_str()); + } + } + } + } + if (addFlag) + { + FieldValueTuple snat_translated_port("translated_l4_port", nat_src_l4_port); + FieldValueTuple dnat_translated_port("translated_l4_port", src_l4_port); + + fvVector.push_back(snat_translated_port); + reverseFvVector.push_back(dnat_translated_port); + + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_NAPT_TABLE_NAME, key, fvVector, false); + m_AppRestartAssist->insertToMap(APP_NAPT_TABLE_NAME, reverseEntryKey, reverseFvVector, false); + } + else + { + /* Skip entry, if entry contains loopback destination address */ + if ((IS_LOOPBACK_ADDR(ntohl(entry.orig_dest_ip.getV4Addr()))) || + (IS_LOOPBACK_ADDR(ntohl(entry.nat_dest_ip.getV4Addr())))) + { + SWSS_LOG_INFO("SNAPT %s: static entry contains loopback address, ignoring the notification", opStr.c_str()); + return 1; + } + else + { + m_naptTable.set(key, fvVector); + SWSS_LOG_NOTICE("SNAPT entry with key %s added to APP_DB", key.c_str()); + m_naptTable.set(reverseEntryKey, reverseFvVector); + SWSS_LOG_NOTICE("Implicit DNAPT entry with key %s added to APP_DB", reverseEntryKey.c_str()); + } + } + } + } + else + { + /* Case of Basic SNAT entry, where SIP is NAT'ted but the port translation is not done. */ + SWSS_LOG_INFO("SNAT %s conntrack notification", opStr.c_str()); + + key += entry.orig_src_ip.to_string(); + reverseEntryKey += entry.nat_src_ip.to_string(); + + std::vector values; + if (! m_AppRestartAssist->isWarmStartInProgress()) + { + if ((entryExists = m_natCheckTable.get(key, values))) + { + for (auto iter : values) + { + if ((fvField(iter) == "entry_type") && (fvValue(iter) == "static")) + { + /* If a matching Static NAT entry exists in the APP_DB, + * it has higher priority than the dynamic napt entry. */ + SWSS_LOG_INFO("SNAT %s: static entry exists, not processing the NAT notification", opStr.c_str()); + return 1; + } + } + if (addFlag) + { + SWSS_LOG_INFO("SNAT CREATE: ignoring the duplicated notification"); + return 1; + } + else + { + /* Skip entry, if entry contains loopback destination address */ + if ((IS_LOOPBACK_ADDR(ntohl(entry.orig_dest_ip.getV4Addr()))) || + (IS_LOOPBACK_ADDR(ntohl(entry.nat_dest_ip.getV4Addr())))) + { + SWSS_LOG_INFO("SNAT %s: static entry contains loopback address, ignoring the notification", opStr.c_str()); + return 1; + } + else + { + m_natTable.del(key); + SWSS_LOG_NOTICE("SNAT entry with key %s deleted from APP_DB", key.c_str()); + } + } + } + if ((reverseEntryExists = m_natCheckTable.get(reverseEntryKey, values))) + { + for (auto iter : values) + { + if ((fvField(iter) == "entry_type") && (fvValue(iter) == "static")) + { + /* If a matching Static NAT entry exists in the APP_DB, + * it has higher priority than the dynamic napt entry. */ + SWSS_LOG_INFO("SNAT %s: static reverse entry exists, not adding dynamic NAT entry", opStr.c_str()); + return 1; + } + } + if (addFlag) + { + SWSS_LOG_INFO("SNAT CREATE: ignoring the duplicated notification"); + return 1; + } + else + { + /* Skip entry, if entry contains loopback destination address */ + if ((IS_LOOPBACK_ADDR(ntohl(entry.orig_dest_ip.getV4Addr()))) || + (IS_LOOPBACK_ADDR(ntohl(entry.nat_dest_ip.getV4Addr())))) + { + SWSS_LOG_INFO("SNAT %s: static entry contains loopback address, ignoring the notification", opStr.c_str()); + return 1; + } + else + { + m_natTable.del(reverseEntryKey); + SWSS_LOG_NOTICE("Implicit DNAT entry with key %s deleted from APP_DB", reverseEntryKey.c_str()); + } + } + } + } + if (addFlag) + { + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_NAT_TABLE_NAME, key, fvVector, false); + m_AppRestartAssist->insertToMap(APP_NAT_TABLE_NAME, reverseEntryKey, reverseFvVector, false); + } + else + { + /* Skip entry, if entry is src natted and dest is loopback destination address */ + if ((IS_LOOPBACK_ADDR(ntohl(entry.orig_dest_ip.getV4Addr()))) || + (IS_LOOPBACK_ADDR(ntohl(entry.nat_dest_ip.getV4Addr())))) + { + SWSS_LOG_INFO("SNAT %s: static entry contains loopback address, ignoring the notification", opStr.c_str()); + return 1; + } + else + { + m_natTable.set(key, fvVector); + SWSS_LOG_NOTICE("SNAT entry with key %s added to APP_DB", key.c_str()); + m_natTable.set(reverseEntryKey, reverseFvVector); + SWSS_LOG_NOTICE("Implicit DNAT entry with key %s added to APP_DB", reverseEntryKey.c_str()); + } + } + } + } + } + else + { + if (addFlag) + { + FieldValueTuple dnat_translated_ip("translated_ip", entry.nat_dest_ip.to_string()); + FieldValueTuple snat_translated_ip("translated_ip", entry.orig_dest_ip.to_string()); + + fvVector.push_back(dnat_type); + fvVector.push_back(dynamic_entry); + fvVector.push_back(dnat_translated_ip); + + reverseFvVector.push_back(snat_type); + reverseFvVector.push_back(dynamic_entry); + reverseFvVector.push_back(snat_translated_ip); + } + + if (dst_port_natted) + { + key += protostr + entry.orig_dest_ip.to_string(); + reverseEntryKey += protostr + entry.nat_dest_ip.to_string(); + + /* Case of DNAPT entry, where DIP is NAT'ted and the L4 port is NAT'ted. */ + SWSS_LOG_INFO("DNAPT %s conntrack notification", opStr.c_str()); + + string dst_l4_port = std::to_string(entry.orig_dst_l4_port); + string nat_dst_l4_port = std::to_string(entry.nat_dst_l4_port); + + key += ":" + dst_l4_port; + reverseEntryKey += ":" + nat_dst_l4_port; + + std::vector values; + if (! m_AppRestartAssist->isWarmStartInProgress()) + { + if ((entryExists = m_naptCheckTable.get(key, values))) + { + for (auto iter : values) + { + if ((fvField(iter) == "entry_type") && (fvValue(iter) == "static")) + { + /* If a matching Static NAPT entry exists in the APP_DB, + * it has higher priority than the dynamic napt entry. */ + SWSS_LOG_INFO("DNAPT %s: static entry exists, not processing the NAPT notification", opStr.c_str()); + return 1; + } + } + if (addFlag) + { + SWSS_LOG_INFO("DNAPT CREATE: ignoring the duplicated notification"); + return 1; + } + else + { + m_naptTable.del(key); + SWSS_LOG_NOTICE("DNAPT entry with key %s deleted from APP_DB", key.c_str()); + } + } + if ((reverseEntryExists = m_naptCheckTable.get(reverseEntryKey, values))) + { + for (auto iter : values) + { + if ((fvField(iter) == "entry_type") && (fvValue(iter) == "static")) + { + /* If a matching Static NAPT entry exists in the APP_DB, + * it has higher priority than the dynamic napt entry. */ + SWSS_LOG_INFO("DNAPT %s: static reverse entry exists, not adding dynamic NAPT entry", opStr.c_str()); + return 1; + } + } + if (addFlag) + { + SWSS_LOG_INFO("DNAPT CREATE: ignoring the duplicated notification"); + return 1; + } + else + { + m_naptTable.del(reverseEntryKey); + SWSS_LOG_NOTICE("Implicit SNAPT entry with key %s deleted from APP_DB", reverseEntryKey.c_str()); + } + } + } + if (addFlag) + { + FieldValueTuple dnat_translated_port("translated_l4_port", nat_dst_l4_port); + FieldValueTuple snat_translated_port("translated_l4_port", dst_l4_port); + + fvVector.push_back(dnat_translated_port); + reverseFvVector.push_back(snat_translated_port); + + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_NAPT_TABLE_NAME, key, fvVector, false); + m_AppRestartAssist->insertToMap(APP_NAPT_TABLE_NAME, reverseEntryKey, reverseFvVector, false); + } + else + { + m_naptTable.set(key, fvVector); + SWSS_LOG_NOTICE("DNAPT entry with key %s added to APP_DB", key.c_str()); + m_naptTable.set(reverseEntryKey, reverseFvVector); + SWSS_LOG_NOTICE("Implicit SNAPT entry with key %s added to APP_DB", reverseEntryKey.c_str()); + } + } + } + else + { + /* Case of Basic DNAT entry, where DIP is NAT'ted but the port translation is not done. */ + SWSS_LOG_INFO("DNAT %s conntrack notification", opStr.c_str()); + key += entry.orig_dest_ip.to_string(); + reverseEntryKey += entry.nat_dest_ip.to_string(); + + std::vector values; + if (! m_AppRestartAssist->isWarmStartInProgress()) + { + if ((entryExists = m_natCheckTable.get(key, values))) + { + for (auto iter : values) + { + if ((fvField(iter) == "entry_type") && (fvValue(iter) == "static")) + { + /* If a matching Static NAT entry exists in the APP_DB, + * it has higher priority than the dynamic napt entry. */ + SWSS_LOG_INFO("DNAT %s: static entry exists, not processing the NAT notification", opStr.c_str()); + return 1; + } + } + if (addFlag) + { + SWSS_LOG_INFO("DNAT CREATE: ignoring the duplicated notification"); + return 1; + } + else + { + m_natTable.del(key); + SWSS_LOG_NOTICE("DNAT entry with key %s deleted from APP_DB", key.c_str()); + } + } + if ((reverseEntryExists = m_natCheckTable.get(reverseEntryKey, values))) + { + for (auto iter : values) + { + if ((fvField(iter) == "entry_type") && (fvValue(iter) == "static")) + { + /* If a matching Static NAT entry exists in the APP_DB, + * it has higher priority than the dynamic napt entry. */ + SWSS_LOG_INFO("DNAT %s: static reverse entry exists, not adding dynamic NAT entry", opStr.c_str()); + return 1; + } + } + if (addFlag) + { + SWSS_LOG_INFO("DNAT CREATE: ignoring the duplicated notification"); + return 1; + } + else + { + m_natTable.del(reverseEntryKey); + SWSS_LOG_NOTICE("Implicit SNAT entry with key %s deleted from APP_DB", reverseEntryKey.c_str()); + } + } + } + if (addFlag) + { + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_NAT_TABLE_NAME, key, fvVector, false); + m_AppRestartAssist->insertToMap(APP_NAT_TABLE_NAME, reverseEntryKey, reverseFvVector, false); + } + else + { + m_natTable.set(key, fvVector); + SWSS_LOG_NOTICE("DNAT entry with key %s added to APP_DB", key.c_str()); + m_natTable.set(reverseEntryKey, reverseFvVector); + SWSS_LOG_NOTICE("Implicit SNAT entry with key %s added to APP_DB", reverseEntryKey.c_str()); + } + } + } + } + } + return 0; +} + +/* This function is called only for updating the UDP connection entries + * so as not to timeout early in the kernel. */ +void NatSync::updateConnTrackEntry(struct nfnl_ct *ct) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_INFO("Updating conntrack entry in the kernel"); + + if (nfsock) + { + nfsock->updateConnTrackEntry(ct); + } +} + +/* This function is called to delete conflicting NAT entries + * to ensure full cone NAT functionality. + * Also this function is invoked to remove low priority + * dynamic NAT entry when there is a matching static NAT entry */ +void NatSync::deleteConnTrackEntry(struct nfnl_ct *ct) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_INFO("Deleting conntrack entry in the kernel"); + + if (nfsock) + { + nfsock->deleteConnTrackEntry(ct); + } +} diff --git a/natsyncd/natsync.h b/natsyncd/natsync.h new file mode 100644 index 0000000000..e4ea8c6f18 --- /dev/null +++ b/natsyncd/natsync.h @@ -0,0 +1,112 @@ +/* + * Copyright 2019 Broadcom Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NATSYNC_H__ +#define __NATSYNC_H__ + +#include "dbconnector.h" +#include "producerstatetable.h" +#include "netmsg.h" +#include "warmRestartAssist.h" +#include "ipaddress.h" +#include "nfnetlink.h" +#include +#include +#include + +// The timeout value (in seconds) for natsyncd reconcilation logic +#define DEFAULT_NATSYNC_WARMSTART_TIMER 30 + +/* This is the timer value (in seconds) that the natsyncd waits for + * restore_nat_entries service to finish. */ + +#define RESTORE_NAT_WAIT_TIME_OUT 120 + +namespace swss { + +struct naptEntry; + +class NatSync : public NetMsg +{ +public: + NatSync(RedisPipeline *pipelineAppDB, DBConnector *appDb, DBConnector *stateDb, NfNetlink *nfnl); + + virtual void onMsg(int nlmsg_type, struct nl_object *obj); + + bool isNatRestoreDone(); + bool isPortInitDone(DBConnector *app_db); + + AppRestartAssist *getRestartAssist() + { + return m_AppRestartAssist; + } + +private: + static int parseConnTrackMsg(const struct nfnl_ct *ct, struct naptEntry &entry); + void updateConnTrackEntry(struct nfnl_ct *ct); + void deleteConnTrackEntry(struct nfnl_ct *ct); + + bool matchingSnaptPoolExists(const IpAddress &natIp); + bool matchingSnaptEntryExists(const naptEntry &entry); + bool matchingDnaptEntryExists(const naptEntry &entry); + int addNatEntry(struct nfnl_ct *ct, struct naptEntry &entry, bool addFlag); + + ProducerStateTable m_natTable; + ProducerStateTable m_naptTable; + ProducerStateTable m_natTwiceTable; + ProducerStateTable m_naptTwiceTable; + + Table m_natCheckTable; + Table m_naptCheckTable; + Table m_naptPoolCheckTable; + Table m_twiceNatCheckTable; + Table m_twiceNaptCheckTable; + + Table m_stateNatRestoreTable; + AppRestartAssist *m_AppRestartAssist; + + NfNetlink *nfsock; +}; + +struct naptEntry +{ + uint32_t conntrack_id; + uint8_t protocol; + IpAddress orig_src_ip; + uint16_t orig_src_l4_port; + IpAddress orig_dest_ip; + uint16_t orig_dst_l4_port; + IpAddress nat_src_ip; + uint16_t nat_src_l4_port; + IpAddress nat_dest_ip; + uint16_t nat_dst_l4_port; + uint32_t ct_status; +}; + +/* Copy of nl_addr from netlink-private/types.h */ +struct nl_ip_addr +{ + int a_family; + unsigned int a_maxsize; + unsigned int a_len; + int a_prefixlen; + int a_refcnt; + int a_addr; +}; + +} + +#endif /* __NATSYNC_H__ */ diff --git a/natsyncd/natsyncd.cpp b/natsyncd/natsyncd.cpp new file mode 100644 index 0000000000..bda2540620 --- /dev/null +++ b/natsyncd/natsyncd.cpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include "logger.h" +#include "select.h" +#include "netdispatcher.h" +#include "natsync.h" +#include + +using namespace std; +using namespace swss; + +int main(int argc, char **argv) +{ + Logger::linkToDbNative("natsyncd"); + + DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + RedisPipeline pipelineAppDB(&appDb); + DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + NfNetlink nfnl; + + nfnl.registerRecvCallbacks(); + NatSync sync(&pipelineAppDB, &appDb, &stateDb, &nfnl); + + sync.isPortInitDone(&appDb); + + NetDispatcher::getInstance().registerMessageHandler(NFNLMSG_TYPE(NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_NEW), &sync); + NetDispatcher::getInstance().registerMessageHandler(NFNLMSG_TYPE(NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_DELETE), &sync); + + while (1) + { + try + { + Select s; + + using namespace std::chrono; + /* + * If warmstart, read the NAT tables to cache map. + * Wait for the kernel NAT conntrack table restore to finish in case of warmreboot. + * Start reconcile timer once restore flag is set. + */ + if (sync.getRestartAssist()->isWarmStartInProgress()) + { + sync.getRestartAssist()->readTablesToMap(); + + steady_clock::time_point starttime = steady_clock::now(); + while (!sync.isNatRestoreDone()) + { + duration time_span = + duration_cast>(steady_clock::now() - starttime); + int pasttime = int(time_span.count()); + SWSS_LOG_INFO("Waited for NAT conntrack table to be restored to kernel" + " for %d seconds", pasttime); + if (pasttime > RESTORE_NAT_WAIT_TIME_OUT) + { + SWSS_LOG_ERROR("Nat conntrack table restore is not finished" + " after timed-out, exit!!!"); + exit(EXIT_FAILURE); + } + sleep(1); + } + sync.getRestartAssist()->startReconcileTimer(s); + } + + nfnl.registerGroup(NFNLGRP_CONNTRACK_NEW); + nfnl.registerGroup(NFNLGRP_CONNTRACK_UPDATE); + nfnl.registerGroup(NFNLGRP_CONNTRACK_DESTROY); + + SWSS_LOG_INFO("Listens to conntrack messages..."); + nfnl.dumpRequest(IPCTNL_MSG_CT_GET); + + s.addSelectable(&nfnl); + while (true) + { + Selectable *temps; + s.select(&temps); + /* + * If warmstart is in progress, we check the reconcile timer, + * if timer expired, we stop the timer and start the reconcile process + */ + if (sync.getRestartAssist()->isWarmStartInProgress()) + { + if (sync.getRestartAssist()->checkReconcileTimer(temps)) + { + sync.getRestartAssist()->stopReconcileTimer(s); + sync.getRestartAssist()->reconcile(); + } + } + } + } + catch (const std::exception& e) + { + SWSS_LOG_ERROR("Runtime error: %s", e.what()); + return 0; + } + } + + return 1; +} diff --git a/neighsyncd/neighsync.cpp b/neighsyncd/neighsync.cpp index 69ca563f2e..1af94450ad 100644 --- a/neighsyncd/neighsync.cpp +++ b/neighsyncd/neighsync.cpp @@ -19,9 +19,21 @@ using namespace swss; NeighSync::NeighSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb) : m_neighTable(pipelineAppDB, APP_NEIGH_TABLE_NAME), - m_stateNeighRestoreTable(stateDb, STATE_NEIGH_RESTORE_TABLE_NAME), - m_AppRestartAssist(pipelineAppDB, "neighsyncd", "swss", &m_neighTable, DEFAULT_NEIGHSYNC_WARMSTART_TIMER) + m_stateNeighRestoreTable(stateDb, STATE_NEIGH_RESTORE_TABLE_NAME) { + m_AppRestartAssist = new AppRestartAssist(pipelineAppDB, "neighsyncd", "swss", DEFAULT_NEIGHSYNC_WARMSTART_TIMER); + if (m_AppRestartAssist) + { + m_AppRestartAssist->registerAppTable(APP_NEIGH_TABLE_NAME, &m_neighTable); + } +} + +NeighSync::~NeighSync() +{ + if (m_AppRestartAssist) + { + delete m_AppRestartAssist; + } } // Check if neighbor table is restored in kernel @@ -98,9 +110,9 @@ void NeighSync::onMsg(int nlmsg_type, struct nl_object *obj) fvVector.push_back(f); // If warmstart is in progress, we take all netlink changes into the cache map - if (m_AppRestartAssist.isWarmStartInProgress()) + if (m_AppRestartAssist->isWarmStartInProgress()) { - m_AppRestartAssist.insertToMap(key, fvVector, delete_key); + m_AppRestartAssist->insertToMap(APP_NEIGH_TABLE_NAME, key, fvVector, delete_key); } else { diff --git a/neighsyncd/neighsync.h b/neighsyncd/neighsync.h index 66fd1c2645..387c849f30 100644 --- a/neighsyncd/neighsync.h +++ b/neighsyncd/neighsync.h @@ -24,6 +24,7 @@ class NeighSync : public NetMsg enum { MAX_ADDR_SIZE = 64 }; NeighSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb); + ~NeighSync(); virtual void onMsg(int nlmsg_type, struct nl_object *obj); @@ -31,13 +32,13 @@ class NeighSync : public NetMsg AppRestartAssist *getRestartAssist() { - return &m_AppRestartAssist; + return m_AppRestartAssist; } private: Table m_stateNeighRestoreTable; ProducerStateTable m_neighTable; - AppRestartAssist m_AppRestartAssist; + AppRestartAssist *m_AppRestartAssist; }; } diff --git a/neighsyncd/neighsyncd.cpp b/neighsyncd/neighsyncd.cpp index c14380fb3a..f0dab62590 100644 --- a/neighsyncd/neighsyncd.cpp +++ b/neighsyncd/neighsyncd.cpp @@ -40,7 +40,7 @@ int main(int argc, char **argv) */ if (sync.getRestartAssist()->isWarmStartInProgress()) { - sync.getRestartAssist()->readTableToMap(); + sync.getRestartAssist()->readTablesToMap(); steady_clock::time_point starttime = steady_clock::now(); while (!sync.isNeighRestoreDone()) diff --git a/neighsyncd/restore_neighbors.py b/neighsyncd/restore_neighbors.py index 73453ce7d2..63242bf1c1 100755 --- a/neighsyncd/restore_neighbors.py +++ b/neighsyncd/restore_neighbors.py @@ -194,7 +194,7 @@ def set_neigh_in_kernel(ipclass, family, intf_idx, dst_ip, dmac): def build_arp_ns_pkt(family, smac, src_ip, dst_ip): if family == 'IPv4': eth = Ether(src=smac, dst='ff:ff:ff:ff:ff:ff') - pkt = eth/ARP(op='who-has', pdst=dst_ip) + pkt = eth/ARP(op='who-has', pdst=dst_ip, psrc=src_ip, hwsrc=smac) elif family == 'IPv6': nsma = in6_getnsma(inet_pton(AF_INET6, dst_ip)) mcast_dst_ip = inet_ntop(AF_INET6, nsma) diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 79cfe99f6d..d431557b5c 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -55,7 +55,8 @@ orchagent_SOURCES = \ policerorch.cpp \ sfloworch.cpp \ chassisorch.cpp \ - debugcounterorch.cpp + debugcounterorch.cpp \ + natorch.cpp orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp orchagent_SOURCES += debug_counter/debug_counter.cpp debug_counter/drop_counter.cpp diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index b25d18a44c..9bb6d786bd 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -65,6 +65,7 @@ static acl_rule_attr_lookup_t aclL3ActionLookup = { { ACTION_PACKET_ACTION, SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION }, { ACTION_REDIRECT_ACTION, SAI_ACL_ENTRY_ATTR_ACTION_REDIRECT }, + { ACTION_DO_NOT_NAT_ACTION, SAI_ACL_ENTRY_ATTR_ACTION_NO_NAT }, }; static acl_rule_attr_lookup_t aclMirrorStageLookup = @@ -206,6 +207,13 @@ bool AclRule::validateAddMatch(string attr_name, string attr_value) SWSS_LOG_ERROR("Failed to locate port %s", alias.c_str()); return false; } + + if (port.m_type != Port::PHY) + { + SWSS_LOG_ERROR("Cannot bind rule to %s: IN_PORTS can only match physical interfaces", alias.c_str()); + return false; + } + m_inPorts.push_back(port.m_port_id); } @@ -230,6 +238,13 @@ bool AclRule::validateAddMatch(string attr_name, string attr_value) SWSS_LOG_ERROR("Failed to locate port %s", alias.c_str()); return false; } + + if (port.m_type != Port::PHY) + { + SWSS_LOG_ERROR("Cannot bind rule to %s: OUT_PORTS can only match physical interfaces", alias.c_str()); + return false; + } + m_outPorts.push_back(port.m_port_id); } @@ -797,6 +812,12 @@ bool AclRuleL3::validateAddAction(string attr_name, string _attr_value) action_str = ACTION_REDIRECT_ACTION; } + // handle PACKET_ACTION_DO_NOT_NAT in ACTION_PACKET_ACTION + else if (attr_value == PACKET_ACTION_DO_NOT_NAT) + { + value.aclaction.parameter.booldata = true; + action_str = ACTION_DO_NOT_NAT_ACTION; + } else { return false; @@ -2051,6 +2072,7 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr string platform = getenv("platform") ? getenv("platform") : ""; if (platform == BRCM_PLATFORM_SUBSTRING || platform == MLNX_PLATFORM_SUBSTRING || + platform == BFN_PLATFORM_SUBSTRING || platform == NPS_PLATFORM_SUBSTRING) { m_mirrorTableCapabilities = @@ -2081,7 +2103,8 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr } // In Mellanox platform, V4 and V6 rules are stored in different tables - if (platform == MLNX_PLATFORM_SUBSTRING) { + if (platform == MLNX_PLATFORM_SUBSTRING || + platform == BFN_PLATFORM_SUBSTRING) { m_isCombinedMirrorV6Table = false; } diff --git a/orchagent/aclorch.h b/orchagent/aclorch.h index 5f09683f5a..1df91cea59 100644 --- a/orchagent/aclorch.h +++ b/orchagent/aclorch.h @@ -63,6 +63,7 @@ #define ACTION_PACKET_ACTION "PACKET_ACTION" #define ACTION_REDIRECT_ACTION "REDIRECT_ACTION" +#define ACTION_DO_NOT_NAT_ACTION "DO_NOT_NAT_ACTION" #define ACTION_MIRROR_ACTION "MIRROR_ACTION" #define ACTION_MIRROR_INGRESS_ACTION "MIRROR_INGRESS_ACTION" #define ACTION_MIRROR_EGRESS_ACTION "MIRROR_EGRESS_ACTION" @@ -73,9 +74,10 @@ #define ACTION_DTEL_FLOW_SAMPLE_PERCENT "FLOW_SAMPLE_PERCENT" #define ACTION_DTEL_REPORT_ALL_PACKETS "REPORT_ALL_PACKETS" -#define PACKET_ACTION_FORWARD "FORWARD" -#define PACKET_ACTION_DROP "DROP" -#define PACKET_ACTION_REDIRECT "REDIRECT" +#define PACKET_ACTION_FORWARD "FORWARD" +#define PACKET_ACTION_DROP "DROP" +#define PACKET_ACTION_REDIRECT "REDIRECT" +#define PACKET_ACTION_DO_NOT_NAT "DO_NOT_NAT" #define DTEL_FLOW_OP_NOP "NOP" #define DTEL_FLOW_OP_POSTCARD "POSTCARD" diff --git a/orchagent/copporch.cpp b/orchagent/copporch.cpp index fbe3f1080a..d6cbfaa736 100644 --- a/orchagent/copporch.cpp +++ b/orchagent/copporch.cpp @@ -18,6 +18,7 @@ extern sai_switch_api_t* sai_switch_api; extern sai_object_id_t gSwitchId; extern PortsOrch* gPortsOrch; +extern bool gIsNatSupported; static map policer_meter_map = { {"packets", SAI_METER_TYPE_PACKETS}, @@ -72,7 +73,9 @@ static map trap_id_map = { {"ttl_error", SAI_HOSTIF_TRAP_TYPE_TTL_ERROR}, {"udld", SAI_HOSTIF_TRAP_TYPE_UDLD}, {"bfd", SAI_HOSTIF_TRAP_TYPE_BFD}, - {"bfdv6", SAI_HOSTIF_TRAP_TYPE_BFDV6} + {"bfdv6", SAI_HOSTIF_TRAP_TYPE_BFDV6}, + {"src_nat_miss", SAI_HOSTIF_TRAP_TYPE_SNAT_MISS}, + {"dest_nat_miss", SAI_HOSTIF_TRAP_TYPE_DNAT_MISS} }; static map packet_action_map = { @@ -189,6 +192,12 @@ void CoppOrch::getTrapIdList(vector &trap_id_name_list, vector + using std::runtime_error; using std::string; using std::unordered_map; @@ -12,6 +14,9 @@ using std::vector; extern sai_object_id_t gSwitchId; extern sai_debug_counter_api_t *sai_debug_counter_api; +#define INGRESS_DROP_REASON_PREFIX_LENGTH 19 // "SAI_IN_DROP_REASON_" +#define EGRESS_DROP_REASON_PREFIX_LENGTH 20 // "SAI_OUT_DROP_REASON_" + const unordered_map DropCounter::ingress_drop_reason_lookup = { { L2_ANY, SAI_IN_DROP_REASON_L2_ANY }, @@ -290,7 +295,7 @@ void DropCounter::updateDropReasonsInSAI() // // If the device does not support querying drop reasons, this method will // return an empty list. -vector DropCounter::getSupportedDropReasons(sai_debug_counter_attr_t drop_reason_type) +unordered_set DropCounter::getSupportedDropReasons(sai_debug_counter_attr_t drop_reason_type) { sai_s32_list_t drop_reason_list; int32_t supported_reasons[maxDropReasons]; @@ -306,20 +311,22 @@ vector DropCounter::getSupportedDropReasons(sai_debug_counter_attr_t dro return {}; } - vector supported_drop_reasons; + unordered_set supported_drop_reasons; for (uint32_t i = 0; i < drop_reason_list.count; i++) { string drop_reason; if (drop_reason_type == SAI_DEBUG_COUNTER_ATTR_IN_DROP_REASON_LIST) { drop_reason = sai_serialize_ingress_drop_reason(static_cast(drop_reason_list.list[i])); + drop_reason = drop_reason.substr(INGRESS_DROP_REASON_PREFIX_LENGTH); } else { drop_reason = sai_serialize_egress_drop_reason(static_cast(drop_reason_list.list[i])); + drop_reason = drop_reason.substr(EGRESS_DROP_REASON_PREFIX_LENGTH); } - supported_drop_reasons.push_back(drop_reason); + supported_drop_reasons.emplace(drop_reason); } return supported_drop_reasons; @@ -330,7 +337,7 @@ vector DropCounter::getSupportedDropReasons(sai_debug_counter_attr_t dro // // e.g. { "SMAC_EQUALS_DMAC", "INGRESS_VLAN_FILTER" } -> "["SMAC_EQUALS_DMAC","INGRESS_VLAN_FILTER"]" // e.g. { } -> "[]" -string DropCounter::serializeSupportedDropReasons(vector drop_reasons) +string DropCounter::serializeSupportedDropReasons(unordered_set drop_reasons) { if (drop_reasons.size() == 0) { @@ -338,7 +345,7 @@ string DropCounter::serializeSupportedDropReasons(vector drop_reasons) } string supported_drop_reasons; - for (auto const &drop_reason : drop_reasons) + for (auto const& drop_reason : drop_reasons) { supported_drop_reasons += ','; supported_drop_reasons += drop_reason; diff --git a/orchagent/debug_counter/drop_counter.h b/orchagent/debug_counter/drop_counter.h index 8ae34e3d4b..bc548b34c7 100644 --- a/orchagent/debug_counter/drop_counter.h +++ b/orchagent/debug_counter/drop_counter.h @@ -2,7 +2,6 @@ #define SWSS_UTIL_DROP_COUNTER_H_ #include -#include #include #include #include "debug_counter.h" @@ -33,8 +32,8 @@ class DropCounter : public DebugCounter static bool isIngressDropReasonValid(const std::string& drop_reason); static bool isEgressDropReasonValid(const std::string& drop_reason); - static std::vector getSupportedDropReasons(sai_debug_counter_attr_t drop_reason_type); - static std::string serializeSupportedDropReasons(std::vector drop_reasons); + static std::unordered_set getSupportedDropReasons(sai_debug_counter_attr_t drop_reason_type); + static std::string serializeSupportedDropReasons(std::unordered_set drop_reasons); static uint64_t getSupportedDebugCounterAmounts(sai_debug_counter_type_t counter_type); private: diff --git a/orchagent/debugcounterorch.cpp b/orchagent/debugcounterorch.cpp index b70c6625c6..b4f2407ca4 100644 --- a/orchagent/debugcounterorch.cpp +++ b/orchagent/debugcounterorch.cpp @@ -25,7 +25,7 @@ static const unordered_map flex_counter_type_lookup = { // object should only be initialized once. DebugCounterOrch::DebugCounterOrch(DBConnector *db, const vector& table_names, int poll_interval) : Orch(db, table_names), - flex_counter_manager(DEBUG_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, poll_interval), + flex_counter_manager(DEBUG_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, poll_interval, true), m_stateDb(new DBConnector("STATE_DB", 0)), m_debugCapabilitiesTable(new Table(m_stateDb.get(), STATE_DEBUG_COUNTER_CAPABILITIES_NAME)), m_countersDb(new DBConnector("COUNTERS_DB", 0)), @@ -34,7 +34,6 @@ DebugCounterOrch::DebugCounterOrch(DBConnector *db, const vector& table_ { SWSS_LOG_ENTER(); publishDropCounterCapabilities(); - flex_counter_manager.enableFlexCounterGroup(); } DebugCounterOrch::~DebugCounterOrch(void) @@ -182,35 +181,46 @@ void DebugCounterOrch::doTask(Consumer& consumer) // DROP_COUNTER_CAPABILITIES table in STATE_DB. void DebugCounterOrch::publishDropCounterCapabilities() { - string supported_ingress_drop_reasons = DropCounter::serializeSupportedDropReasons( - DropCounter::getSupportedDropReasons(SAI_DEBUG_COUNTER_ATTR_IN_DROP_REASON_LIST)); - string supported_egress_drop_reasons = DropCounter::serializeSupportedDropReasons( - DropCounter::getSupportedDropReasons(SAI_DEBUG_COUNTER_ATTR_OUT_DROP_REASON_LIST)); + supported_ingress_drop_reasons = DropCounter::getSupportedDropReasons(SAI_DEBUG_COUNTER_ATTR_IN_DROP_REASON_LIST); + supported_egress_drop_reasons = DropCounter::getSupportedDropReasons(SAI_DEBUG_COUNTER_ATTR_OUT_DROP_REASON_LIST); + + string ingress_drop_reason_str = DropCounter::serializeSupportedDropReasons(supported_ingress_drop_reasons); + string egress_drop_reason_str = DropCounter::serializeSupportedDropReasons(supported_egress_drop_reasons); for (auto const &counter_type : DebugCounter::getDebugCounterTypeLookup()) { - string num_counters = std::to_string(DropCounter::getSupportedDebugCounterAmounts(counter_type.second)); - string drop_reasons; if (counter_type.first == PORT_INGRESS_DROPS || counter_type.first == SWITCH_INGRESS_DROPS) { - drop_reasons = supported_ingress_drop_reasons; + drop_reasons = ingress_drop_reason_str; } else { - drop_reasons = supported_egress_drop_reasons; + drop_reasons = egress_drop_reason_str; } - // Only include available capabilities in State DB - if (num_counters != "0" && !drop_reasons.empty()) + // Don't bother publishing counters that have no drop reasons + if (drop_reasons.empty()) { - vector fieldValues; - fieldValues.push_back(FieldValueTuple("count", num_counters)); - fieldValues.push_back(FieldValueTuple("reasons", drop_reasons)); + continue; + } - SWSS_LOG_DEBUG("Setting '%s' capabilities to count='%s', reasons='%s'", counter_type.first.c_str(), num_counters.c_str(), drop_reasons.c_str()); - m_debugCapabilitiesTable->set(counter_type.first, fieldValues); + string num_counters = std::to_string(DropCounter::getSupportedDebugCounterAmounts(counter_type.second)); + + // Don't bother publishing counters that aren't available. + if (num_counters == "0") + { + continue; } + + supported_counter_types.emplace(counter_type.first); + + vector fieldValues; + fieldValues.push_back(FieldValueTuple("count", num_counters)); + fieldValues.push_back(FieldValueTuple("reasons", drop_reasons)); + + SWSS_LOG_DEBUG("Setting '%s' capabilities to count='%s', reasons='%s'", counter_type.first.c_str(), num_counters.c_str(), drop_reasons.c_str()); + m_debugCapabilitiesTable->set(counter_type.first, fieldValues); } } @@ -229,12 +239,19 @@ task_process_status DebugCounterOrch::installDebugCounter(const string& counter_ return task_process_status::task_success; } - // Note: this method currently assumes that all counters are drop counters. + // NOTE: this method currently assumes that all counters are drop counters. // If you are adding support for a non-drop counter than it may make sense // to either: a) dispatch to different handlers in doTask or b) dispatch to // different helper methods in this method. string counter_type = getDebugCounterType(attributes); + + if (supported_counter_types.find(counter_type) == supported_counter_types.end()) + { + SWSS_LOG_ERROR("Specified counter type '%s' is not supported.", counter_type.c_str()); + return task_process_status::task_failed; + } + addFreeCounter(counter_name, counter_type); reconcileFreeDropCounters(counter_name); @@ -287,7 +304,15 @@ task_process_status DebugCounterOrch::addDropReason(const string& counter_name, if (!isDropReasonValid(drop_reason)) { - return task_failed; + SWSS_LOG_ERROR("Specified drop reason '%s' is invalid.", drop_reason.c_str()); + return task_process_status::task_failed; + } + + if (supported_ingress_drop_reasons.find(drop_reason) == supported_ingress_drop_reasons.end() && + supported_egress_drop_reasons.find(drop_reason) == supported_egress_drop_reasons.end()) + { + SWSS_LOG_ERROR("Specified drop reason '%s' is not supported.", drop_reason.c_str()); + return task_process_status::task_failed; } auto it = debug_counters.find(counter_name); diff --git a/orchagent/debugcounterorch.h b/orchagent/debugcounterorch.h index 49ca64b7f7..e5b512c8e4 100644 --- a/orchagent/debugcounterorch.h +++ b/orchagent/debugcounterorch.h @@ -77,6 +77,10 @@ class DebugCounterOrch: public Orch std::shared_ptr m_counterNameToPortStatMap = nullptr; std::shared_ptr m_counterNameToSwitchStatMap = nullptr; + std::unordered_set supported_counter_types; + std::unordered_set supported_ingress_drop_reasons; + std::unordered_set supported_egress_drop_reasons; + FlexCounterStatManager flex_counter_manager; std::unordered_map> debug_counters; diff --git a/orchagent/flex_counter/flex_counter_manager.cpp b/orchagent/flex_counter/flex_counter_manager.cpp index 323bd309c4..130ea2833d 100644 --- a/orchagent/flex_counter/flex_counter_manager.cpp +++ b/orchagent/flex_counter/flex_counter_manager.cpp @@ -34,17 +34,19 @@ const unordered_map FlexCounterManager::counter_id_field_lo { { CounterType::PORT_DEBUG, PORT_DEBUG_COUNTER_ID_LIST }, { CounterType::SWITCH_DEBUG, SWITCH_DEBUG_COUNTER_ID_LIST }, + { CounterType::PORT, PORT_COUNTER_ID_LIST }, + { CounterType::QUEUE, QUEUE_COUNTER_ID_LIST } }; -// This constructor will create a group that is disabled by default. FlexCounterManager::FlexCounterManager( const string& group_name, const StatsMode stats_mode, - const uint polling_interval) : + const uint polling_interval, + const bool enabled) : group_name(group_name), stats_mode(stats_mode), polling_interval(polling_interval), - enabled(false), + enabled(enabled), flex_counter_db(new DBConnector("FLEX_COUNTER_DB", 0)), flex_counter_group_table(new ProducerTable(flex_counter_db.get(), FLEX_COUNTER_GROUP_TABLE)), flex_counter_table(new ProducerTable(flex_counter_db.get(), FLEX_COUNTER_TABLE)) @@ -72,6 +74,8 @@ FlexCounterManager::~FlexCounterManager() void FlexCounterManager::applyGroupConfiguration() { + SWSS_LOG_ENTER(); + vector field_values = { FieldValueTuple(STATS_MODE_FIELD, stats_mode_lookup.at(stats_mode)), diff --git a/orchagent/flex_counter/flex_counter_manager.h b/orchagent/flex_counter/flex_counter_manager.h index 0d444d57ed..19ff26ccf0 100644 --- a/orchagent/flex_counter/flex_counter_manager.h +++ b/orchagent/flex_counter/flex_counter_manager.h @@ -18,6 +18,8 @@ enum class StatsMode enum class CounterType { + PORT, + QUEUE, PORT_DEBUG, SWITCH_DEBUG }; @@ -33,7 +35,8 @@ class FlexCounterManager FlexCounterManager( const std::string& group_name, const StatsMode stats_mode, - const uint polling_interval); + const uint polling_interval, + const bool enabled); FlexCounterManager(const FlexCounterManager&) = delete; FlexCounterManager& operator=(const FlexCounterManager&) = delete; diff --git a/orchagent/flex_counter/flex_counter_stat_manager.cpp b/orchagent/flex_counter/flex_counter_stat_manager.cpp index 4f6cab843e..f636e5af1e 100644 --- a/orchagent/flex_counter/flex_counter_stat_manager.cpp +++ b/orchagent/flex_counter/flex_counter_stat_manager.cpp @@ -13,8 +13,9 @@ using swss::FieldValueTuple; FlexCounterStatManager::FlexCounterStatManager( const string& group_name, const StatsMode stats_mode, - const int polling_interval) : - FlexCounterManager(group_name, stats_mode, polling_interval) + const int polling_interval, + const bool enabled) : + FlexCounterManager(group_name, stats_mode, polling_interval, enabled) { SWSS_LOG_ENTER(); } diff --git a/orchagent/flex_counter/flex_counter_stat_manager.h b/orchagent/flex_counter/flex_counter_stat_manager.h index 250adb724c..7a388ae100 100644 --- a/orchagent/flex_counter/flex_counter_stat_manager.h +++ b/orchagent/flex_counter/flex_counter_stat_manager.h @@ -11,7 +11,8 @@ class FlexCounterStatManager : public FlexCounterManager FlexCounterStatManager( const std::string& group_name, const StatsMode stats_mode, - const int polling_interval); + const int polling_interval, + const bool enabled); FlexCounterStatManager(const FlexCounterStatManager&) = delete; FlexCounterStatManager& operator=(const FlexCounterStatManager&) = delete; diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index ddf2240455..a7e232fec4 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -23,12 +23,14 @@ extern Directory gDirectory; extern sai_router_interface_api_t* sai_router_intfs_api; extern sai_route_api_t* sai_route_api; extern sai_neighbor_api_t* sai_neighbor_api; +extern sai_switch_api_t* sai_switch_api; extern sai_object_id_t gSwitchId; extern PortsOrch *gPortsOrch; extern RouteOrch *gRouteOrch; extern CrmOrch *gCrmOrch; extern BufferOrch *gBufferOrch; +extern bool gIsNatSupported; const int intfsorch_pri = 35; @@ -165,6 +167,35 @@ bool IntfsOrch::setRouterIntfsMtu(const Port &port) return true; } +bool IntfsOrch::setRouterIntfsNatZoneId(Port &port) +{ + SWSS_LOG_ENTER(); + + /* Return true if the router interface is not exists */ + if (!port.m_rif_id) + { + SWSS_LOG_WARN("Router interface is not exists on %s", + port.m_alias.c_str()); + return true; + } + + sai_attribute_t attr; + attr.id = SAI_ROUTER_INTERFACE_ATTR_NAT_ZONE_ID; + attr.value.u32 = port.m_nat_zone_id; + + sai_status_t status = sai_router_intfs_api-> + set_router_interface_attribute(port.m_rif_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set router interface %s NAT Zone Id to %u, rv:%d", + port.m_alias.c_str(), port.m_nat_zone_id, status); + return false; + } + SWSS_LOG_NOTICE("Set router interface %s NAT Zone Id to %u", + port.m_alias.c_str(), port.m_nat_zone_id); + return true; +} + bool IntfsOrch::setRouterIntfsAdminStatus(const Port &port) { SWSS_LOG_ENTER(); @@ -404,9 +435,11 @@ void IntfsOrch::doTask(Consumer &consumer) } const vector& data = kfvFieldsValues(t); - string vrf_name = "", vnet_name = ""; + string vrf_name = "", vnet_name = "", nat_zone = ""; uint32_t mtu; bool adminUp; + uint32_t nat_zone_id = 0; + for (auto idx : data) { const auto &field = fvField(idx); @@ -419,6 +452,19 @@ void IntfsOrch::doTask(Consumer &consumer) { vnet_name = value; } + else if (field == "nat_zone") + { + try + { + nat_zone_id = (uint32_t)stoul(value); + } + catch (...) + { + SWSS_LOG_ERROR("Invalid argument %s for nat zone", value.c_str()); + continue; + } + nat_zone = value; + } else if (field == "mtu") { try @@ -452,6 +498,10 @@ void IntfsOrch::doTask(Consumer &consumer) } } } + else if (field == "nat_zone") + { + nat_zone = value; + } } if (alias == "eth0" || alias == "docker0") @@ -555,6 +605,23 @@ void IntfsOrch::doTask(Consumer &consumer) it++; continue; } + + /* Set nat zone id */ + if ((!nat_zone.empty()) and (port.m_nat_zone_id != nat_zone_id)) + { + port.m_nat_zone_id = nat_zone_id; + + if (gIsNatSupported) + { + setRouterIntfsNatZoneId(port); + } + else + { + SWSS_LOG_NOTICE("Not set router interface %s NAT Zone Id to %u, as NAT is not supported", + port.m_alias.c_str(), port.m_nat_zone_id); + } + gPortsOrch->setPort(alias, port); + } } it = consumer.m_toSync.erase(it); @@ -739,6 +806,15 @@ bool IntfsOrch::addRouterIntfs(sai_object_id_t vrf_id, Port &port) attr.value.u32 = port.m_mtu; attrs.push_back(attr); + if (gIsNatSupported) + { + attr.id = SAI_ROUTER_INTERFACE_ATTR_NAT_ZONE_ID; + attr.value.u32 = port.m_nat_zone_id; + + SWSS_LOG_INFO("Assinging NAT zone id %d to interface %s\n", attr.value.u32, port.m_alias.c_str()); + attrs.push_back(attr); + } + sai_status_t status = sai_router_intfs_api->create_router_interface(&port.m_rif_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); if (status != SAI_STATUS_SUCCESS) { @@ -779,6 +855,7 @@ bool IntfsOrch::removeRouterIntfs(Port &port) port.m_rif_id = 0; port.m_vr_id = 0; + port.m_nat_zone_id = 0; gPortsOrch->setPort(port.m_alias, port); SWSS_LOG_NOTICE("Remove router interface for port %s", port.m_alias.c_str()); diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index 22e41a4945..8209b5cfa1 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -40,7 +40,9 @@ class IntfsOrch : public Orch void decreaseRouterIntfsRefCount(const string&); bool setRouterIntfsMtu(const Port &port); + bool setRouterIntfsNatZoneId(Port &port); bool setRouterIntfsAdminStatus(const Port &port); + std::set getSubnetRoutes(); void generateInterfaceMap(); diff --git a/orchagent/main.cpp b/orchagent/main.cpp index 6d3d6d228e..e2cf42247d 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -50,6 +50,8 @@ bool gLogRotate = false; bool gSaiRedisLogRotate = false; bool gSyncMode = false; +extern bool gIsNatSupported; + ofstream gRecordOfs; string gRecordFile; @@ -262,6 +264,22 @@ int main(int argc, char **argv) gVirtualRouterId = attr.value.oid; SWSS_LOG_NOTICE("Get switch virtual router ID %" PRIx64, gVirtualRouterId); + /* Get the NAT supported info */ + attr.id = SAI_SWITCH_ATTR_AVAILABLE_SNAT_ENTRY; + + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("Failed to get the SNAT available entry count, rv:%d", status); + } + else + { + if (attr.value.u32 != 0) + { + gIsNatSupported = true; + } + } + /* Create a loopback underlay router interface */ vector underlay_intf_attrs; diff --git a/orchagent/natorch.cpp b/orchagent/natorch.cpp new file mode 100644 index 0000000000..ccd0e12f44 --- /dev/null +++ b/orchagent/natorch.cpp @@ -0,0 +1,4858 @@ +/* + * Copyright 2019 Broadcom Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include "exec.h" +#include "logger.h" +#include "tokenize.h" +#include "natorch.h" +#include "notifier.h" +#include "sai_serialize.h" + +extern PortsOrch *gPortsOrch; +extern sai_object_id_t gSwitchId; +extern sai_switch_api_t *sai_switch_api; +extern sai_object_id_t gVirtualRouterId; +extern sai_nat_api_t *sai_nat_api; +extern sai_hostif_api_t *sai_hostif_api; +extern bool gIsNatSupported; +#ifdef DEBUG_FRAMEWORK +extern DebugDumpOrch *gDebugDumpOrch; +#endif +uint32_t natTimerTickCntr = 0; +bool gNhTrackingSupported = false; + +NatOrch::NatOrch(DBConnector *appDb, DBConnector *stateDb, vector &tableNames, + RouteOrch *routeOrch, NeighOrch *neighOrch): + Orch(appDb, tableNames), + m_neighOrch(neighOrch), + m_routeOrch(routeOrch), + m_countersDb(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0), + m_countersNatTable(&m_countersDb, COUNTERS_NAT_TABLE), + m_countersNaptTable(&m_countersDb, COUNTERS_NAPT_TABLE), + m_countersTwiceNatTable(&m_countersDb, COUNTERS_TWICE_NAT_TABLE), + m_countersTwiceNaptTable(&m_countersDb, COUNTERS_TWICE_NAPT_TABLE), + m_countersGlobalNatTable(&m_countersDb, COUNTERS_GLOBAL_NAT_TABLE), + m_stateWarmRestartEnableTable(stateDb, STATE_WARM_RESTART_ENABLE_TABLE_NAME), + m_stateWarmRestartTable(stateDb, STATE_WARM_RESTART_TABLE_NAME), + m_natQueryTable(appDb, APP_NAT_TABLE_NAME), + m_naptQueryTable(appDb, APP_NAPT_TABLE_NAME), + m_twiceNatQueryTable(appDb, APP_NAT_TWICE_TABLE_NAME), + m_twiceNaptQueryTable(appDb, APP_NAPT_TWICE_TABLE_NAME), + nullIpv4Addr(0) +{ + /* Set NAT admin mode to disabled */ + admin_mode = "disabled"; + + /* Set NAT default timeout as 600 seconds */ + timeout = 600; + + /* Set NAT default tcp timeout as 86400 seconds (1 Day) */ + tcp_timeout = 86400; + + /* Set NAT default udp timeout as 300 seconds */ + udp_timeout = 300; + + /* Set entries count to 0 */ + totalEntries = totalSnatEntries = totalDnatEntries = 0; + totalStaticNatEntries = totalDynamicNatEntries = 0; + totalStaticNaptEntries = totalDynamicNaptEntries = 0; + totalStaticTwiceNatEntries = totalDynamicTwiceNatEntries = 0; + totalStaticTwiceNaptEntries = totalDynamicTwiceNaptEntries = 0; + + /* Add NAT notifications support from APPL_DB */ + SWSS_LOG_INFO("Add NAT notifications support from APPL_DB "); + m_flushNotificationsConsumer = new NotificationConsumer(appDb, "FLUSHNATREQUEST"); + auto flushNotifier = new Notifier(m_flushNotificationsConsumer, this, "FLUSHNATREQUEST"); + Orch::addExecutor(flushNotifier); + + SWSS_LOG_INFO("Add REDIS DB cleanup notification support"); + m_cleanupNotificationConsumer = new NotificationConsumer(appDb, "NAT_DB_CLEANUP_NOTIFICATION"); + auto cleanupNotifier = new Notifier(m_cleanupNotificationConsumer, this, "NAT_DB_CLEANUP_NOTIFICATION"); + Orch::addExecutor(cleanupNotifier); + + /* Start the timer to query NAT entry statistics every 5 secs and hitbits every 30 secs */ + SWSS_LOG_INFO("Start the HITBIT Timer "); + auto interval = timespec { .tv_sec = NAT_HITBIT_N_CNTRS_QUERY_PERIOD, .tv_nsec = 0 }; + m_natQueryTimer = new SelectableTimer(interval); + auto executor = new ExecutableTimer(m_natQueryTimer, this, "NAT_HITBIT_N_CNTRS_QUERY_TIMER"); + Orch::addExecutor(executor); + + /* Get the Maximum supported SNAT entries */ + SWSS_LOG_INFO("Get the Maximum supported SNAT entries"); + sai_status_t status; + sai_attribute_t attr; + memset(&attr, 0, sizeof(attr)); + attr.id = SAI_SWITCH_ATTR_AVAILABLE_SNAT_ENTRY; + maxAllowedSNatEntries = 0; + + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("Failed to get the SNAT available entry count, rv:%d", status); + } + else + { + maxAllowedSNatEntries = attr.value.u32; + } + + /* Set default values and Max entries to counter DB */ + std::vector values; + std::string key = "Values"; + swss::FieldValueTuple p("MAX_NAT_ENTRIES", to_string(maxAllowedSNatEntries)); + swss::FieldValueTuple q("TIMEOUT", to_string(timeout)); + swss::FieldValueTuple r("UDP_TIMEOUT", to_string(udp_timeout)); + swss::FieldValueTuple s("TCP_TIMEOUT", to_string(tcp_timeout)); + values.push_back(p); + values.push_back(q); + values.push_back(r); + values.push_back(s); + m_countersGlobalNatTable.set(key, values); + +#ifdef DEBUG_FRAMEWORK + /*Register with debug framework*/ + this->m_dbgCompName = "natorch"; + gDebugDumpOrch->addDbgCompMap(m_dbgCompName, this); +#endif + + char *platform = getenv("platform"); + if (platform && strstr(platform, BRCM_PLATFORM_SUBSTRING)) + { + gNhTrackingSupported = true; + } + SWSS_LOG_NOTICE("DNAT nexthop tracking is %s", ((gNhTrackingSupported == true) ? "enabled" : "disabled")); +} + +/* Process notifications for changes in Neighbor entries and route entries + * that resolve the DNAT entries's next-hop for the translated ip address. + */ +void NatOrch::update(SubjectType type, void *cntx) +{ + SWSS_LOG_ENTER(); + + assert(cntx); + + switch(type) + { + case SUBJECT_TYPE_NEXTHOP_CHANGE: + { + NextHopUpdate *update = static_cast(cntx); + updateNextHop(*update); + break; + } + case SUBJECT_TYPE_NEIGH_CHANGE: + { + NeighborUpdate *update = static_cast(cntx); + updateNeighbor(*update); + break; + } + default: + /* Received update in which we are not interested + * Ignore it + */ + return; + } +} + +bool NatOrch::isNextHopResolved(const NextHopUpdate &update) +{ + // Ignore default route and subnet based routes + if ((update.prefix.isDefaultRoute()) || + ((update.nexthopGroup.getSize() == 1) && (update.nexthopGroup.hasIntfNextHop()))) + { + SWSS_LOG_INFO("Ignore default or subnet nexthop update event for ip %s", update.destination.to_string().c_str()); + return false; + } + if (update.nexthopGroup == NextHopGroupKey()) + { + return false; + } + return true; +} + +// Route nexthop change notification to be processed for the DNAT entries +void NatOrch::updateNextHop(const NextHopUpdate& update) +{ + SWSS_LOG_ENTER(); + + auto it = m_nhResolvCache.find(update.destination); + + if (it == m_nhResolvCache.end()) + { + // No dnat entries to be resolved on this nexthop + return; + } + auto &nhCache = it->second; + + // If the ECMP nexthop group did not change + if (update.nexthopGroup == nhCache.nextHopGroup) + { + return; + } + + SWSS_LOG_INFO("Nexthop update event for dnat entries with translated ip %s", + update.destination.to_string().c_str()); + + if ((nhCache.nextHopGroup == NextHopGroupKey()) && (isNextHopResolved(update))) + { + nhCache.nextHopGroup = update.nexthopGroup; + if (! nhCache.neighResolved) + { + // Add all DNAT entries whose translated destination has nexthop resolved. + SWSS_LOG_INFO("Nexthop resolved for dnat entries with translated ip %s, adding the entries", + update.destination.to_string().c_str()); + addNhCacheDnatEntries(update.destination, 1); + } + } + else if ((nhCache.nextHopGroup != NextHopGroupKey()) && (! isNextHopResolved(update))) + { + nhCache.nextHopGroup = NextHopGroupKey(); + if (! nhCache.neighResolved) + { + // Delete all DNAT entries whose translated destination has nexthop unresolved. + SWSS_LOG_INFO("Nexthop unresolved for dnat entries with translated ip %s, deleting the entries", + update.destination.to_string().c_str()); + addNhCacheDnatEntries(update.destination, 0); + } + } + else if ((nhCache.nextHopGroup != update.nexthopGroup) && (isNextHopResolved(update))) + { + nhCache.nextHopGroup = update.nexthopGroup; + if (! nhCache.neighResolved) + { + // Add and delete all DNAT entries whose translated destination has ECMP group/NH modified. + SWSS_LOG_INFO("Nexthop/ECMP modified for dnat entries with translated ip %s, deleting and re-adding the entries", + update.destination.to_string().c_str()); + addNhCacheDnatEntries(update.destination, 0); + addNhCacheDnatEntries(update.destination, 1); + } + } +} + +// Neighbor change notification to be processed for the DNAT entries +void NatOrch::updateNeighbor(const NeighborUpdate& update) +{ + SWSS_LOG_ENTER(); + + auto it = m_nhResolvCache.find(update.entry.ip_address); + + // Check if the neighbor update IP matches the translated DNAT IP we are interested in + if (it == m_nhResolvCache.end()) + { + return; + } + auto &nhCache = it->second; + + SWSS_LOG_INFO("Neighbor update event for dnat entries with translated ip %s", + update.entry.ip_address.to_string().c_str()); + + if ((nhCache.neighResolved) && (! update.add)) + { + // Delete all DNAT entries whose translated destination is unresolved. + SWSS_LOG_INFO("Neighbor unresolved for dnat entries with translated ip %s, deleting the entries", + update.entry.ip_address.to_string().c_str()); + + addNhCacheDnatEntries(update.entry.ip_address, 0); + if (nhCache.nextHopGroup != NextHopGroupKey()) + { + SWSS_LOG_INFO("Nexthop exists for dnat entries with translated ip %s, adding the entries", + update.entry.ip_address.to_string().c_str()); + addNhCacheDnatEntries(update.entry.ip_address, 1); + } + } + else if ((! nhCache.neighResolved) && (update.add)) + { + if (nhCache.nextHopGroup != NextHopGroupKey()) + { + SWSS_LOG_INFO("Neighbor resolved for dnat entries with translated ip %s, deleting the entries added with route nexthop", + update.entry.ip_address.to_string().c_str()); + addNhCacheDnatEntries(update.entry.ip_address, 0); + } + // Add all DNAT entries whose translated destination is resolved. + SWSS_LOG_INFO("Neighbor resolved for dnat entries with translated ip %s, adding the entries", + update.entry.ip_address.to_string().c_str()); + addNhCacheDnatEntries(update.entry.ip_address, 1); + } + nhCache.neighResolved = update.add; +} + +/* Process all the dependent DNAT entries to handle the changes + * in neighbor or nexthop notifications + */ +void NatOrch::addNhCacheDnatEntries(const IpAddress &nhIp, bool add) +{ + SWSS_LOG_ENTER(); + auto it = m_nhResolvCache.find(nhIp); + + if (it == m_nhResolvCache.end()) + { + return; + } + auto &nhCache = it->second; + + if (nhCache.dnatIp != nullIpv4Addr) + { + auto natIter = m_natEntries.find(nhCache.dnatIp); + if (natIter != m_natEntries.end()) + { + if (add) + { + addHwDnatEntry(natIter->first); + } + else + { + removeHwDnatEntry(natIter->first); + } + } + } + auto cIter = nhCache.dnapt.begin(); + while (cIter != nhCache.dnapt.end()) + { + auto naptIter = m_naptEntries.find(*cIter); + if (naptIter != m_naptEntries.end()) + { + if (add) + { + addHwDnaptEntry(naptIter->first); + } + else + { + removeHwDnaptEntry(naptIter->first); + } + } + cIter++; + } + auto tIter = nhCache.twiceNat.begin(); + while (tIter != nhCache.twiceNat.end()) + { + auto tnatIter = m_twiceNatEntries.find(*tIter); + if (tnatIter != m_twiceNatEntries.end()) + { + if (add) + { + addHwTwiceNatEntry(tnatIter->first); + } + else + { + removeHwTwiceNatEntry(tnatIter->first); + } + } + tIter++; + } + auto tpIter = nhCache.twiceNapt.begin(); + while (tpIter != nhCache.twiceNapt.end()) + { + auto tnaptIter = m_twiceNaptEntries.find(*tpIter); + if (tnaptIter != m_twiceNaptEntries.end()) + { + if (add) + { + addHwTwiceNaptEntry(tnaptIter->first); + } + else + { + removeHwTwiceNaptEntry(tnaptIter->first); + } + } + tpIter++; + } +} + +/* Cache the DNAT entry in the NH resolution cache. + * Only if the nexthop is resolved is the DNAT entry added to hardware. + */ +void NatOrch::addDnatToNhCache(const IpAddress &translatedIp, const IpAddress &dstIp) +{ + NeighborEntry neighEntry; + MacAddress macAddr; + DnatEntries dnatEntries; + + SWSS_LOG_ENTER(); + auto cIter = m_nhResolvCache.find(translatedIp); + + SWSS_LOG_INFO("Adding to NH cache indexed by translated ip %s, the DNAT entry with ip %s", + translatedIp.to_string().c_str(), dstIp.to_string().c_str()); + if (cIter == m_nhResolvCache.end()) + { + dnatEntries.dnatIp = dstIp; + dnatEntries.nextHopGroup = NextHopGroupKey(); + dnatEntries.neighResolved = false; + + if (m_neighOrch->getNeighborEntry(translatedIp, neighEntry, macAddr)) + { + dnatEntries.neighResolved = true; + SWSS_LOG_INFO("Resolved by a neighbor entry, adding to hardware"); + addHwDnatEntry(dstIp); + } + m_nhResolvCache[translatedIp] = dnatEntries; + m_routeOrch->attach(this, translatedIp); + return; + } + else + { + if ((cIter->second).dnatIp != dstIp) + { + (cIter->second).dnatIp = dstIp; + if ((cIter->second).neighResolved || ((cIter->second).nextHopGroup != NextHopGroupKey())) + { + SWSS_LOG_INFO("Resolved by a neighbor or route entry, adding to hardware"); + addHwDnatEntry(dstIp); + } + } + } +} + +/* Cache the Twice NAT entry in the NH resolution cache. + * Only if the translated dst nexthop is resolved is the Twice NAT entry added to hardware. + */ +void NatOrch::addTwiceNatToNhCache(const IpAddress &translatedIp, const TwiceNatEntryKey &key) +{ + NeighborEntry neighEntry; + MacAddress macAddr; + DnatEntries dnatEntries; + + SWSS_LOG_ENTER(); + auto cIter = m_nhResolvCache.find(translatedIp); + + SWSS_LOG_INFO("Adding to NH cache indexed by translated ip %s, the Twice NAT entry with src ip %s, dst ip %s", + translatedIp.to_string().c_str(), key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str()); + if (cIter == m_nhResolvCache.end()) + { + dnatEntries.dnatIp = nullIpv4Addr; + dnatEntries.nextHopGroup = NextHopGroupKey(); + dnatEntries.twiceNat.insert(key); + dnatEntries.neighResolved = false; + if (m_neighOrch->getNeighborEntry(translatedIp, neighEntry, macAddr)) + { + dnatEntries.neighResolved = true; + SWSS_LOG_INFO("Resolved by a neighbor entry, adding to hardware"); + addHwTwiceNatEntry(key); + } + m_nhResolvCache[translatedIp] = dnatEntries; + m_routeOrch->attach(this, translatedIp); + return; + } + else + { + auto naptIter = ((cIter->second).twiceNat).find(key); + if (naptIter == ((cIter->second).twiceNat).end()) + { + ((cIter->second).twiceNat).insert(key); + if ((cIter->second).neighResolved || ((cIter->second).nextHopGroup != NextHopGroupKey())) + { + SWSS_LOG_INFO("Twice NAT resolved by a neighbor or route entry, adding to hardware"); + addHwTwiceNatEntry(key); + } + } + } +} + +/* Cache the Twice NAPT entry in the NH resolution cache. + * Only if the translated dst nexthop is resolved is the Twice NAPT entry added to hardware. + */ +void NatOrch::addTwiceNaptToNhCache(const IpAddress &translatedIp, const TwiceNaptEntryKey &key) +{ + NeighborEntry neighEntry; + MacAddress macAddr; + DnatEntries dnatEntries; + + SWSS_LOG_ENTER(); + auto cIter = m_nhResolvCache.find(translatedIp); + + SWSS_LOG_INFO("Adding to NH cache indexed by translated ip %s, the Twice NAPT entry with src ip %s, src port %d, dst ip %s, dst port %d", + translatedIp.to_string().c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), + key.dst_l4_port); + if (cIter == m_nhResolvCache.end()) + { + dnatEntries.dnatIp = nullIpv4Addr; + dnatEntries.nextHopGroup = NextHopGroupKey(); + dnatEntries.twiceNapt.insert(key); + dnatEntries.neighResolved = false; + if (m_neighOrch->getNeighborEntry(translatedIp, neighEntry, macAddr)) + { + dnatEntries.neighResolved = true; + SWSS_LOG_INFO("Resolved by a neighbor entry, adding to hardware"); + addHwTwiceNaptEntry(key); + } + m_nhResolvCache[translatedIp] = dnatEntries; + m_routeOrch->attach(this, translatedIp); + return; + } + else + { + auto naptIter = ((cIter->second).twiceNapt).find(key); + if (naptIter == ((cIter->second).twiceNapt).end()) + { + ((cIter->second).twiceNapt).insert(key); + if ((cIter->second).neighResolved || ((cIter->second).nextHopGroup != NextHopGroupKey())) + { + SWSS_LOG_INFO("Twice NAPT resolved by a neighbor or route entry, adding to hardware"); + addHwTwiceNaptEntry(key); + } + } + } +} + +// Remove the DNAT entry from the NH resolution cache. +void NatOrch::removeDnatFromNhCache(const IpAddress &translatedIp, const IpAddress &dstIp) +{ + SWSS_LOG_ENTER(); + + auto cIter = m_nhResolvCache.find(translatedIp); + + SWSS_LOG_INFO("Removing from NH cache the DNAT entry with ip %s, indexed by translated ip %s", + dstIp.to_string().c_str(), translatedIp.to_string().c_str()); + if (cIter == m_nhResolvCache.end()) + { + SWSS_LOG_INFO("Translated IP %s doesn't exist in NH resolve cache, cannot delete DNAT entry %s", + translatedIp.to_string().c_str(), dstIp.to_string().c_str()); + return; + } + + DnatEntries &dnatEntries = cIter->second; + + if (dnatEntries.dnatIp != dstIp) + { + SWSS_LOG_INFO("DNAT entry %s doesn't exist in NH resolve cache", dstIp.to_string().c_str()); + return; + } + dnatEntries.dnatIp = nullIpv4Addr; + + if ((dnatEntries.neighResolved) || (dnatEntries.nextHopGroup != NextHopGroupKey())) + { + removeHwDnatEntry(dstIp); + } + + m_natEntries.erase(dstIp); + + if (dnatEntries.dnapt.empty() && (dnatEntries.dnatIp == nullIpv4Addr) && + dnatEntries.twiceNat.empty() && dnatEntries.twiceNapt.empty()) + { + SWSS_LOG_INFO("No NAT/NAPT entries waiting for NH resolution of translated-ip %s", translatedIp.to_string().c_str()); + m_routeOrch->detach(this, translatedIp); + m_nhResolvCache.erase(translatedIp); + } +} + +/* Cache the DNAPT entry in the NH resolution cache. + * Only if the nexthop is resolved is the DNAT entry added to hardware. + */ +void NatOrch::addDnaptToNhCache(const IpAddress &translatedIp, const NaptEntryKey &key) +{ + NeighborEntry neighEntry; + MacAddress macAddr; + DnatEntries dnatEntries; + + SWSS_LOG_ENTER(); + auto cIter = m_nhResolvCache.find(translatedIp); + + SWSS_LOG_INFO("Adding to NH cache indexed by translated ip %s, the DNAPT NAT entry with proto %s, ip %s, port %d", + translatedIp.to_string().c_str(), key.prototype.c_str(), key.ip_address.to_string().c_str(), key.l4_port); + + if (cIter == m_nhResolvCache.end()) + { + dnatEntries.dnatIp = nullIpv4Addr; + dnatEntries.nextHopGroup = NextHopGroupKey(); + dnatEntries.dnapt.insert(key); + dnatEntries.neighResolved = false; + if (m_neighOrch->getNeighborEntry(translatedIp, neighEntry, macAddr)) + { + dnatEntries.neighResolved = true; + SWSS_LOG_INFO("Resolved by a neighbor entry, adding to hardware"); + addHwDnaptEntry(key); + } + m_nhResolvCache[translatedIp] = dnatEntries; + m_routeOrch->attach(this, translatedIp); + return; + } + else + { + auto naptIter = ((cIter->second).dnapt).find(key); + if (naptIter == ((cIter->second).dnapt).end()) + { + ((cIter->second).dnapt).insert(key); + if ((cIter->second).neighResolved || ((cIter->second).nextHopGroup != NextHopGroupKey())) + { + SWSS_LOG_INFO("Resolved by a neighbor or route entry, adding to hardware"); + addHwDnaptEntry(key); + } + } + } +} + +// Remove the DNAPT entry from the NH resolution cache. +void NatOrch::removeDnaptFromNhCache(const IpAddress &translatedIp, const NaptEntryKey &key) +{ + SWSS_LOG_ENTER(); + + auto cIter = m_nhResolvCache.find(translatedIp); + + SWSS_LOG_INFO("Removing from NH cache the DNAPT NAT entry with proto %s, ip %s, port %d, indexed by translated ip %s", + key.prototype.c_str(), key.ip_address.to_string().c_str(), key.l4_port, translatedIp.to_string().c_str()); + + if (cIter == m_nhResolvCache.end()) + { + SWSS_LOG_INFO("Translated IP %s doesn't exist in NH resolve cache, cannot delete DNAPT entry with ip %s, l4port %d", + translatedIp.to_string().c_str(), key.ip_address.to_string().c_str(), key.l4_port); + return; + } + DnatEntries &dnatEntries = cIter->second; + + if (dnatEntries.dnapt.find(key) == dnatEntries.dnapt.end()) + { + SWSS_LOG_INFO("DNAPT entry ip %s, l4port %d doesn't exist in NH resolve cache", + key.ip_address.to_string().c_str(), key.l4_port); + return; + } + if ((dnatEntries.neighResolved) || (dnatEntries.nextHopGroup != NextHopGroupKey())) + { + removeHwDnaptEntry(key); + } + dnatEntries.dnapt.erase(key); + + m_naptEntries.erase(key); + + if (dnatEntries.dnapt.empty() && (dnatEntries.dnatIp == nullIpv4Addr) && + dnatEntries.twiceNat.empty() && dnatEntries.twiceNapt.empty()) + { + SWSS_LOG_INFO("No NAT/NAPT entries waiting for NH resolution of translated-ip %s", + translatedIp.to_string().c_str()); + m_routeOrch->detach(this, translatedIp); + m_nhResolvCache.erase(translatedIp); + } +} + +// Remove the Twice NAT entry from the NH resolution cache. +void NatOrch::removeTwiceNatFromNhCache(const IpAddress &translatedIp, const TwiceNatEntryKey &key) +{ + SWSS_LOG_ENTER(); + + auto cIter = m_nhResolvCache.find(translatedIp); + + SWSS_LOG_INFO("Removing from NH cache the Twice NAT entry with src ip %s, dst ip %s, indexed by translated ip %s", + key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str(), translatedIp.to_string().c_str()); + + if (cIter == m_nhResolvCache.end()) + { + SWSS_LOG_INFO("Translated IP %s doesn't exist in NH resolve cache, cannot delete Twice NAT entry with src ip %s, dst ip %s", + translatedIp.to_string().c_str(), key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str()); + return; + } + DnatEntries &dnatEntries = cIter->second; + + if (dnatEntries.twiceNat.find(key) == dnatEntries.twiceNat.end()) + { + SWSS_LOG_NOTICE("Twice NAT entry with src ip %s, dst ip %s doesn't exist in NH resolve cache", + key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str()); + return; + } + if ((dnatEntries.neighResolved) || (dnatEntries.nextHopGroup != NextHopGroupKey())) + { + removeHwTwiceNatEntry(key); + } + dnatEntries.twiceNat.erase(key); + + m_twiceNatEntries.erase(key); + + if (dnatEntries.dnapt.empty() && (dnatEntries.dnatIp == nullIpv4Addr) && + dnatEntries.twiceNat.empty() && dnatEntries.twiceNapt.empty()) + { + SWSS_LOG_INFO("No NAT/NAPT/Twice NAT entries waiting for NH resolution of translated-ip %s", + translatedIp.to_string().c_str()); + m_routeOrch->detach(this, translatedIp); + m_nhResolvCache.erase(translatedIp); + } +} + +// Remove the Twice NAPT entry from the NH resolution cache. +void NatOrch::removeTwiceNaptFromNhCache(const IpAddress &translatedIp, const TwiceNaptEntryKey &key) +{ + SWSS_LOG_ENTER(); + + auto cIter = m_nhResolvCache.find(translatedIp); + + SWSS_LOG_INFO("Removing from NH cache the Twice NAPT entry with proto %s, src ip %s, src port %d, dst ip %s, dst port %d, indexed by translated ip %s", + key.prototype.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), key.dst_l4_port, + translatedIp.to_string().c_str()); + + if (cIter == m_nhResolvCache.end()) + { + SWSS_LOG_INFO("Translated IP %s doesn't exist in NH resolve cache, cannot delete Twice NAPT entry with proto %s, \ + src ip %s, src port %d, dst ip %s, dst port %d", translatedIp.to_string().c_str(), + key.prototype.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), key.dst_l4_port); + return; + } + DnatEntries &dnatEntries = cIter->second; + + if (dnatEntries.twiceNapt.find(key) == dnatEntries.twiceNapt.end()) + { + SWSS_LOG_NOTICE("Twice NAPT entry with proto %s, src ip %s, src port %d, dst ip %s, dst port %d doesn't exist in NH resolve cache", + key.prototype.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), key.dst_l4_port); + return; + } + if ((dnatEntries.neighResolved) || (dnatEntries.nextHopGroup != NextHopGroupKey())) + { + removeHwTwiceNaptEntry(key); + } + dnatEntries.twiceNapt.erase(key); + + m_twiceNaptEntries.erase(key); + + if (dnatEntries.dnapt.empty() && (dnatEntries.dnatIp == nullIpv4Addr) && + dnatEntries.twiceNat.empty() && dnatEntries.twiceNapt.empty()) + { + SWSS_LOG_INFO("No NAT/NAPT/Twice NAT/Twice NAPT entries waiting for NH resolution of translated-ip %s", + translatedIp.to_string().c_str()); + m_routeOrch->detach(this, translatedIp); + m_nhResolvCache.erase(translatedIp); + } +} + +// Add the DNAT entry after nexthop resolution, to the hardware +bool NatOrch::addHwDnatEntry(const IpAddress &ip_address) +{ + uint32_t attr_count; + sai_nat_entry_t dnat_entry; + sai_attribute_t nat_entry_attr[5]; + sai_status_t status; + + SWSS_LOG_ENTER(); + SWSS_LOG_INFO("Create DNAT entry for ip %s, as nexthop is resolved", ip_address.to_string().c_str()); + + if (m_natEntries.find(ip_address) == m_natEntries.end()) + { + SWSS_LOG_INFO("NAT entry isn't found for ip %s", ip_address.to_string().c_str()); + return false; + } + + NatEntryValue entry = m_natEntries[ip_address]; + + memset(nat_entry_attr, 0, sizeof(nat_entry_attr)); + + nat_entry_attr[0].id = SAI_NAT_ENTRY_ATTR_NAT_TYPE; + nat_entry_attr[0].value.u32 = SAI_NAT_TYPE_DESTINATION_NAT; + nat_entry_attr[1].id = SAI_NAT_ENTRY_ATTR_DST_IP; + nat_entry_attr[1].value.u32 = entry.translated_ip.getV4Addr(); + nat_entry_attr[2].id = SAI_NAT_ENTRY_ATTR_DST_IP_MASK; + nat_entry_attr[2].value.u32 = 0xffffffff; + nat_entry_attr[3].id = SAI_NAT_ENTRY_ATTR_ENABLE_PACKET_COUNT; + nat_entry_attr[3].value.booldata = true; + nat_entry_attr[4].id = SAI_NAT_ENTRY_ATTR_ENABLE_BYTE_COUNT; + nat_entry_attr[4].value.booldata = true; + + attr_count = 5; + + memset(&dnat_entry, 0, sizeof(dnat_entry)); + + dnat_entry.vr_id = gVirtualRouterId; + dnat_entry.switch_id = gSwitchId; + dnat_entry.data.key.dst_ip = ip_address.getV4Addr(); + dnat_entry.data.mask.dst_ip = 0xffffffff; + + status = sai_nat_api->create_nat_entry(&dnat_entry, attr_count, nat_entry_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create %s DNAT NAT entry with ip %s and it's translated ip %s", + entry.entry_type.c_str(), ip_address.to_string().c_str(), entry.translated_ip.to_string().c_str()); + + return false; + } + + SWSS_LOG_NOTICE("Created %s DNAT NAT entry with ip %s and it's translated ip %s", + entry.entry_type.c_str(), ip_address.to_string().c_str(), entry.translated_ip.to_string().c_str()); + + updateNatCounters(ip_address, 0, 0); + m_natEntries[ip_address].addedToHw = true; + + if (entry.entry_type == "static") + { + totalStaticNatEntries++; + updateStaticNatCounters(totalStaticNatEntries); + } + else + { + totalDynamicNatEntries++; + updateDynamicNatCounters(totalDynamicNatEntries); + } + totalDnatEntries++; + updateDnatCounters(totalDnatEntries); + totalEntries++; + + return true; +} + +// Add the DNAPT entry after nexthop resolution, to the hardware +bool NatOrch::addHwDnaptEntry(const NaptEntryKey &key) +{ + uint32_t attr_count; + sai_nat_entry_t dnat_entry; + sai_attribute_t nat_entry_attr[6]; + uint8_t ip_protocol = ((key.prototype == "TCP") ? IPPROTO_TCP : IPPROTO_UDP); + sai_status_t status; + + SWSS_LOG_ENTER(); + SWSS_LOG_INFO("Create DNAPT entry for proto %s, dest-ip %s, l4-port %d, as nexthop is resolved", + key.prototype.c_str(), key.ip_address.to_string().c_str(), key.l4_port); + + if (m_naptEntries.find(key) == m_naptEntries.end()) + { + SWSS_LOG_INFO("NAPT entry isn't found for Prototype - %s, ip - %s and port - %d", + key.prototype.c_str(), key.ip_address.to_string().c_str(), key.l4_port); + return false; + } + + NaptEntryValue entry = m_naptEntries[key]; + + memset(nat_entry_attr, 0, sizeof(nat_entry_attr)); + + nat_entry_attr[0].id = SAI_NAT_ENTRY_ATTR_NAT_TYPE; + nat_entry_attr[0].value.u32 = SAI_NAT_TYPE_DESTINATION_NAT; + nat_entry_attr[1].id = SAI_NAT_ENTRY_ATTR_DST_IP; + nat_entry_attr[2].id = SAI_NAT_ENTRY_ATTR_DST_IP_MASK; + nat_entry_attr[3].id = SAI_NAT_ENTRY_ATTR_L4_DST_PORT; + nat_entry_attr[1].value.u32 = entry.translated_ip.getV4Addr(); + nat_entry_attr[2].value.u32 = 0xffffffff; + nat_entry_attr[3].value.u16 = (uint16_t)(entry.translated_l4_port); + nat_entry_attr[4].id = SAI_NAT_ENTRY_ATTR_ENABLE_PACKET_COUNT; + nat_entry_attr[4].value.booldata = true; + nat_entry_attr[5].id = SAI_NAT_ENTRY_ATTR_ENABLE_BYTE_COUNT; + nat_entry_attr[5].value.booldata = true; + + attr_count = 6; + + memset(&dnat_entry, 0, sizeof(dnat_entry)); + + dnat_entry.vr_id = gVirtualRouterId; + dnat_entry.switch_id = gSwitchId; + dnat_entry.data.key.dst_ip = key.ip_address.getV4Addr(); + dnat_entry.data.key.l4_dst_port = (uint16_t)(key.l4_port); + dnat_entry.data.mask.dst_ip = 0xffffffff; + dnat_entry.data.mask.l4_dst_port = 0xffff; + dnat_entry.data.key.proto = ip_protocol; + dnat_entry.data.mask.proto = 0xff; + + status = sai_nat_api->create_nat_entry(&dnat_entry, attr_count, nat_entry_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create %s DNAT NAPT entry with ip %s, port %d, prototype %s and it's translated ip %s, translated port %d", + entry.entry_type.c_str(), key.ip_address.to_string().c_str(), key.l4_port, key.prototype.c_str(), + entry.translated_ip.to_string().c_str(), entry.translated_l4_port); + return false; + } + + SWSS_LOG_NOTICE("Created %s DNAT NAPT entry with ip %s, port %d, prototype %s and it's translated ip %s, translated port %d", + entry.entry_type.c_str(), key.ip_address.to_string().c_str(), key.l4_port, key.prototype.c_str(), + entry.translated_ip.to_string().c_str(), entry.translated_l4_port); + + m_naptEntries[key].addedToHw = true; + updateNaptCounters(key.prototype.c_str(), key.ip_address, key.l4_port, 0, 0); + + if (entry.entry_type == "static") + { + totalStaticNaptEntries++; + updateStaticNaptCounters(totalStaticNaptEntries); + } + else + { + totalDynamicNaptEntries++; + updateDynamicNaptCounters(totalDynamicNaptEntries); + } + totalDnatEntries++; + updateDnatCounters(totalDnatEntries); + totalEntries++; + + return true; +} + +// Remove the DNAT entry from the hardware +bool NatOrch::removeHwDnatEntry(const IpAddress &dstIp) +{ + sai_nat_entry_t dnat_entry; + sai_status_t status; + + SWSS_LOG_ENTER(); + SWSS_LOG_INFO("Deleting DNAT entry ip %s from hardware", dstIp.to_string().c_str()); + + /* Check the entry is present in cache */ + if (m_natEntries.find(dstIp) == m_natEntries.end()) + { + SWSS_LOG_ERROR("DNAT entry isn't found for ip %s", dstIp.to_string().c_str()); + + return false; + } + + if (m_natEntries[dstIp].addedToHw == false) + { + SWSS_LOG_INFO("DNAT entry isn't added to h/w, for ip %s", dstIp.to_string().c_str()); + + return false; + } + + NatEntryValue entry = m_natEntries[dstIp]; + + m_natEntries[dstIp].addedToHw = false; + + memset(&dnat_entry, 0, sizeof(dnat_entry)); + + dnat_entry.vr_id = gVirtualRouterId; + dnat_entry.switch_id = gSwitchId; + dnat_entry.data.key.dst_ip = dstIp.getV4Addr(); + dnat_entry.data.mask.dst_ip = 0xffffffff; + + status = sai_nat_api->remove_nat_entry(&dnat_entry); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_INFO("Failed to remove %s DNAT NAT entry with ip %s and it's translated ip %s", + entry.entry_type.c_str(), dstIp.to_string().c_str(), entry.translated_ip.to_string().c_str()); + + return false; + } + + SWSS_LOG_NOTICE("Removed %s DNAT NAT entry with ip %s and it's translated ip %s", + entry.entry_type.c_str(), dstIp.to_string().c_str(), entry.translated_ip.to_string().c_str()); + + deleteNatCounters(dstIp); + + if (entry.entry_type == "static") + { + if (totalStaticNatEntries) + { + totalStaticNatEntries--; + updateStaticNatCounters(totalStaticNatEntries); + } + } + else + { + if (totalDynamicNatEntries) + { + totalDynamicNatEntries--; + updateDynamicNatCounters(totalDynamicNatEntries); + } + } + + if (totalDnatEntries) + { + totalDnatEntries--; + updateDnatCounters(totalDnatEntries); + } + + if (totalEntries) + { + totalEntries--; + } + + return true; +} + +// Remove the Twice NAT entry from the hardware +bool NatOrch::removeHwTwiceNatEntry(const TwiceNatEntryKey &key) +{ + sai_nat_entry_t dbl_nat_entry; + sai_status_t status; + + SWSS_LOG_ENTER(); + SWSS_LOG_INFO("Deleting Twice NAT entry src ip %s, dst ip %s from the hardware", + key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str()); + + /* Check the entry is present in cache */ + if (m_twiceNatEntries.find(key) == m_twiceNatEntries.end()) + { + SWSS_LOG_ERROR("Twice NAT entry isn't found for src ip %s, dst ip %s", + key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str()); + + return false; + } + + if (m_twiceNatEntries[key].addedToHw == false) + { + SWSS_LOG_INFO("Twice NAT entry isn't added to hardware, for src ip %s, dst ip %s", + key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str()); + return false; + } + + TwiceNatEntryValue value = m_twiceNatEntries[key]; + + m_twiceNatEntries[key].addedToHw = false; + + memset(&dbl_nat_entry, 0, sizeof(dbl_nat_entry)); + + dbl_nat_entry.vr_id = gVirtualRouterId; + dbl_nat_entry.switch_id = gSwitchId; + dbl_nat_entry.data.key.src_ip = key.src_ip.getV4Addr(); + dbl_nat_entry.data.mask.src_ip = 0xffffffff; + dbl_nat_entry.data.key.dst_ip = key.dst_ip.getV4Addr(); + dbl_nat_entry.data.mask.dst_ip = 0xffffffff; + + + status = sai_nat_api->remove_nat_entry(&dbl_nat_entry); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_INFO("Failed to remove Twice NAT entry with src-ip %s, dst-ip %s", + key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str()); + + return false; + } + SWSS_LOG_NOTICE("Removed Twice NAT entry with src-ip %s, dst-ip %s", + key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str()); + + deleteTwiceNatCounters(key); + m_twiceNatEntries.erase(key); + + if (value.entry_type == "static") + { + if (totalStaticTwiceNatEntries) + { + totalStaticTwiceNatEntries--; + updateStaticTwiceNatCounters(totalStaticTwiceNatEntries); + } + } + else + { + if (totalDynamicTwiceNatEntries) + { + totalDynamicTwiceNatEntries--; + updateDynamicTwiceNatCounters(totalDynamicTwiceNatEntries); + } + } + + if (totalSnatEntries) + { + totalSnatEntries--; + updateSnatCounters(totalSnatEntries); + } + + if (totalDnatEntries) + { + totalDnatEntries--; + updateDnatCounters(totalDnatEntries); + } + + if (totalEntries >= 2) + { + // Each Twice NAT entry is equivalent to 1 SNAT and 1 DNAT entry together + totalEntries -= 2; + } + + return true; +} + +// Remove the DNAPT entry from the hardware +bool NatOrch::removeHwDnaptEntry(const NaptEntryKey &key) +{ + sai_nat_entry_t dnat_entry; + sai_status_t status; + uint8_t ip_protocol = ((key.prototype == "TCP") ? IPPROTO_TCP : IPPROTO_UDP); + + SWSS_LOG_ENTER(); + SWSS_LOG_INFO("Delete DNAPT entry for proto %s, dest-ip %s, l4-port %d", + key.prototype.c_str(), key.ip_address.to_string().c_str(), key.l4_port); + + /* Check the entry is present in cache */ + if (m_naptEntries.find(key) == m_naptEntries.end()) + { + SWSS_LOG_ERROR("DNAPT entry isn't found for ip %s, l4-port %d", key.ip_address.to_string().c_str(), key.l4_port); + + return false; + } + + if (m_naptEntries[key].addedToHw == false) + { + SWSS_LOG_ERROR("DNAPT entry isn't added to hardware, for ip %s, l4-port %d", key.ip_address.to_string().c_str(), key.l4_port); + + return false; + } + + NaptEntryValue entry = m_naptEntries[key]; + + m_naptEntries[key].addedToHw = false; + + memset(&dnat_entry, 0, sizeof(dnat_entry)); + + dnat_entry.vr_id = gVirtualRouterId; + dnat_entry.switch_id = gSwitchId; + dnat_entry.data.key.dst_ip = key.ip_address.getV4Addr(); + dnat_entry.data.key.l4_dst_port = (uint16_t)(key.l4_port); + dnat_entry.data.mask.dst_ip = 0xffffffff; + dnat_entry.data.mask.l4_dst_port = 0xffff; + dnat_entry.data.key.proto = ip_protocol; + dnat_entry.data.mask.proto = 0xff; + + status = sai_nat_api->remove_nat_entry(&dnat_entry); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_INFO("Failed to remove %s DNAT NAPT entry with ip %s, port %d, prototype %s and it's translated ip %s, translated port %d", + entry.entry_type.c_str(), key.ip_address.to_string().c_str(), key.l4_port, key.prototype.c_str(), + entry.translated_ip.to_string().c_str(), entry.translated_l4_port); + + + return false; + } + + SWSS_LOG_NOTICE("Removed %s DNAT NAPT entry with ip %s, port %d, prototype %s and it's translated ip %s, translated port %d", + entry.entry_type.c_str(), key.ip_address.to_string().c_str(), key.l4_port, key.prototype.c_str(), + entry.translated_ip.to_string().c_str(), entry.translated_l4_port); + + deleteNaptCounters(key.prototype.c_str(), key.ip_address, key.l4_port); + + if (entry.entry_type == "static") + { + if (totalStaticNaptEntries) + { + totalStaticNaptEntries--; + updateStaticNaptCounters(totalStaticNaptEntries); + } + } + else + { + if (totalDynamicNaptEntries) + { + totalDynamicNaptEntries--; + updateDynamicNaptCounters(totalDynamicNaptEntries); + } + } + + if (totalDnatEntries) + { + totalDnatEntries--; + updateDnatCounters(totalDnatEntries); + } + + if (totalEntries) + { + totalEntries--; + } + + return true; +} + +// Remove the Twice NAPT entry from the hardware +bool NatOrch::removeHwTwiceNaptEntry(const TwiceNaptEntryKey &key) +{ + sai_nat_entry_t dbl_nat_entry; + sai_status_t status; + uint8_t protoType = ((key.prototype == "TCP") ? IPPROTO_TCP : IPPROTO_UDP); + + SWSS_LOG_ENTER(); + SWSS_LOG_INFO("Delete Twice NAPT entry for proto %s, src-ip %s, src port %d, dst-ip %s, dst port %d", + key.prototype.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, + key.dst_ip.to_string().c_str(), key.dst_l4_port); + + /* Check the entry is present in cache */ + if (m_twiceNaptEntries.find(key) == m_twiceNaptEntries.end()) + { + SWSS_LOG_ERROR("Twice DNAPT entry isn't found for proto %s, src-ip %s, src port %d, dst-ip %s, dst port %d", + key.prototype.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), + key.dst_l4_port); + return false; + } + + if (m_twiceNaptEntries[key].addedToHw == false) + { + SWSS_LOG_INFO("Twice DNAPT entry isn't added to hardware, for proto %s, src-ip %s, src port %d, dst-ip %s, dst port %d", + key.prototype.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), + key.dst_l4_port); + return false; + } + + TwiceNaptEntryValue value = m_twiceNaptEntries[key]; + + m_twiceNaptEntries[key].addedToHw = false; + + memset(&dbl_nat_entry, 0, sizeof(dbl_nat_entry)); + + dbl_nat_entry.vr_id = gVirtualRouterId; + dbl_nat_entry.switch_id = gSwitchId; + dbl_nat_entry.data.key.src_ip = key.src_ip.getV4Addr(); + dbl_nat_entry.data.mask.src_ip = 0xffffffff; + dbl_nat_entry.data.key.l4_src_port = (uint16_t)(key.src_l4_port); + dbl_nat_entry.data.mask.l4_src_port = 0xffff; + dbl_nat_entry.data.key.dst_ip = key.dst_ip.getV4Addr(); + dbl_nat_entry.data.mask.dst_ip = 0xffffffff; + dbl_nat_entry.data.key.l4_dst_port = (uint16_t)(key.dst_l4_port); + dbl_nat_entry.data.mask.l4_dst_port = 0xffff; + dbl_nat_entry.data.key.proto = protoType; + dbl_nat_entry.data.mask.proto = 0xff; + + status = sai_nat_api->remove_nat_entry(&dbl_nat_entry); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_INFO("Failed to remove Twice NAPT entry with prototype %s, src-ip %s, src port %d, dst-ip %s, dst port %d", + key.prototype.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, + key.dst_ip.to_string().c_str(), key.dst_l4_port); + return false; + } + + SWSS_LOG_NOTICE("Removed Twice NAPT entry with prototype %s, src-ip %s, src port %d, dst-ip %s, dst port %d", + key.prototype.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, + key.dst_ip.to_string().c_str(), key.dst_l4_port); + + deleteTwiceNaptCounters(key); + m_twiceNaptEntries.erase(key); + + if (value.entry_type == "static") + { + if (totalStaticTwiceNaptEntries) + { + totalStaticTwiceNaptEntries--; + updateStaticTwiceNaptCounters(totalStaticTwiceNaptEntries); + } + } + else + { + if (totalDynamicTwiceNaptEntries) + { + totalDynamicTwiceNaptEntries--; + updateDynamicTwiceNaptCounters(totalDynamicTwiceNaptEntries); + } + } + + if (totalSnatEntries) + { + totalSnatEntries--; + updateSnatCounters(totalSnatEntries); + } + + if (totalDnatEntries) + { + totalDnatEntries--; + updateDnatCounters(totalDnatEntries); + } + + if (totalEntries >= 2) + { + // Each Twice NAT entry is equivalent to 1 SNAT and 1 DNAT entry together + totalEntries -= 2; + } + + return true; +} + +// Add the SNAT entry to the hardware +bool NatOrch::addHwSnatEntry(const IpAddress &ip_address) +{ + uint32_t attr_count; + sai_nat_entry_t snat_entry; + sai_attribute_t nat_entry_attr[5]; + sai_status_t status; + + SWSS_LOG_ENTER(); + SWSS_LOG_INFO("Create SNAT entry for ip %s", ip_address.to_string().c_str()); + + NatEntryValue entry = m_natEntries[ip_address]; + + memset(nat_entry_attr, 0, sizeof(nat_entry_attr)); + + nat_entry_attr[0].id = SAI_NAT_ENTRY_ATTR_NAT_TYPE; + nat_entry_attr[0].value.u32 = SAI_NAT_TYPE_SOURCE_NAT; + nat_entry_attr[1].id = SAI_NAT_ENTRY_ATTR_SRC_IP; + nat_entry_attr[1].value.u32 = entry.translated_ip.getV4Addr(); + nat_entry_attr[2].id = SAI_NAT_ENTRY_ATTR_SRC_IP_MASK; + nat_entry_attr[2].value.u32 = 0xffffffff; + nat_entry_attr[3].id = SAI_NAT_ENTRY_ATTR_ENABLE_PACKET_COUNT; + nat_entry_attr[3].value.booldata = true; + nat_entry_attr[4].id = SAI_NAT_ENTRY_ATTR_ENABLE_BYTE_COUNT; + nat_entry_attr[4].value.booldata = true; + + attr_count = 5; + + memset(&snat_entry, 0, sizeof(snat_entry)); + + snat_entry.vr_id = gVirtualRouterId; + snat_entry.switch_id = gSwitchId; + snat_entry.data.key.src_ip = ip_address.getV4Addr(); + snat_entry.data.mask.src_ip = 0xffffffff; + + status = sai_nat_api->create_nat_entry(&snat_entry, attr_count, nat_entry_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create %s SNAT NAT entry with ip %s and it's translated ip %s", + entry.entry_type.c_str(), ip_address.to_string().c_str(), entry.translated_ip.to_string().c_str()); + + return true; + } + + SWSS_LOG_NOTICE("Created %s SNAT NAT entry with ip %s and it's translated ip %s", + entry.entry_type.c_str(), ip_address.to_string().c_str(), entry.translated_ip.to_string().c_str()); + + updateNatCounters(ip_address, 0, 0); + m_natEntries[ip_address].addedToHw = true; + + if (entry.entry_type == "static") + { + totalStaticNatEntries++; + updateStaticNatCounters(totalStaticNatEntries); + } + else + { + totalDynamicNatEntries++; + updateDynamicNatCounters(totalDynamicNatEntries); + } + totalEntries++; + + return true; +} + +// Add the Twice NAT entry to the hardware +bool NatOrch::addHwTwiceNatEntry(const TwiceNatEntryKey &key) +{ + uint32_t attr_count; + sai_nat_entry_t dbl_nat_entry; + sai_attribute_t nat_entry_attr[8]; + + sai_status_t status; + + SWSS_LOG_ENTER(); + SWSS_LOG_INFO("Create Twice NAT entry for src ip %s, dst ip %s", key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str()); + + TwiceNatEntryValue value = m_twiceNatEntries[key]; + + memset(nat_entry_attr, 0, sizeof(nat_entry_attr)); + + nat_entry_attr[0].id = SAI_NAT_ENTRY_ATTR_NAT_TYPE; + nat_entry_attr[0].value.u32 = SAI_NAT_TYPE_DOUBLE_NAT; + nat_entry_attr[1].id = SAI_NAT_ENTRY_ATTR_SRC_IP; + nat_entry_attr[1].value.u32 = value.translated_src_ip.getV4Addr(); + nat_entry_attr[2].id = SAI_NAT_ENTRY_ATTR_SRC_IP_MASK; + nat_entry_attr[2].value.u32 = 0xffffffff; + nat_entry_attr[3].id = SAI_NAT_ENTRY_ATTR_DST_IP; + nat_entry_attr[3].value.u32 = value.translated_dst_ip.getV4Addr(); + nat_entry_attr[4].id = SAI_NAT_ENTRY_ATTR_DST_IP_MASK; + nat_entry_attr[4].value.u32 = 0xffffffff; + nat_entry_attr[5].id = SAI_NAT_ENTRY_ATTR_ENABLE_PACKET_COUNT; + nat_entry_attr[5].value.booldata = true; + nat_entry_attr[6].id = SAI_NAT_ENTRY_ATTR_ENABLE_BYTE_COUNT; + nat_entry_attr[6].value.booldata = true; + + attr_count = 7; + + memset(&dbl_nat_entry, 0, sizeof(dbl_nat_entry)); + + dbl_nat_entry.vr_id = gVirtualRouterId; + dbl_nat_entry.switch_id = gSwitchId; + dbl_nat_entry.data.key.src_ip = key.src_ip.getV4Addr(); + dbl_nat_entry.data.mask.src_ip = 0xffffffff; + dbl_nat_entry.data.key.dst_ip = key.dst_ip.getV4Addr(); + dbl_nat_entry.data.mask.dst_ip = 0xffffffff; + + status = sai_nat_api->create_nat_entry(&dbl_nat_entry, attr_count, nat_entry_attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create %s Twice NAT entry with src ip %s, dst ip %s, translated src ip %s, translated dst ip %s", + value.entry_type.c_str(), key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str(), + value.translated_src_ip.to_string().c_str(), value.translated_dst_ip.to_string().c_str()); + + return true; + } + + SWSS_LOG_NOTICE("Created %s Twice NAT entry with src ip %s, dst ip %s, translated src ip %s, translated dst ip %s", + value.entry_type.c_str(), key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str(), + value.translated_src_ip.to_string().c_str(), value.translated_dst_ip.to_string().c_str()); + + updateTwiceNatCounters(key, 0, 0); + m_twiceNatEntries[key].addedToHw = true; + + totalDnatEntries++; + updateDnatCounters(totalDnatEntries); + totalEntries++; + + totalSnatEntries++; + updateSnatCounters(totalSnatEntries); + totalEntries++; + + if (value.entry_type == "static") + { + totalStaticTwiceNatEntries++; + updateStaticTwiceNatCounters(totalStaticTwiceNatEntries); + } + else + { + totalDynamicTwiceNatEntries++; + updateDynamicTwiceNatCounters(totalDynamicTwiceNatEntries); + } + + return true; +} + +// Add the SNAPT entry to the hardware +bool NatOrch::addHwSnaptEntry(const NaptEntryKey &keyEntry) +{ + uint32_t attr_count; + sai_nat_entry_t snat_entry; + sai_attribute_t nat_entry_attr[6]; + uint8_t ip_protocol = ((keyEntry.prototype == "TCP") ? IPPROTO_TCP : IPPROTO_UDP); + sai_status_t status; + + SWSS_LOG_ENTER(); + SWSS_LOG_INFO("Create SNAPT entry for proto %s, src-ip %s, l4-port %d", + keyEntry.prototype.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port); + + NaptEntryValue entry = m_naptEntries[keyEntry]; + + memset(nat_entry_attr, 0, sizeof(nat_entry_attr)); + + nat_entry_attr[0].id = SAI_NAT_ENTRY_ATTR_NAT_TYPE; + nat_entry_attr[0].value.u32 = SAI_NAT_TYPE_SOURCE_NAT; + nat_entry_attr[1].id = SAI_NAT_ENTRY_ATTR_SRC_IP; + nat_entry_attr[1].value.u32 = entry.translated_ip.getV4Addr(); + nat_entry_attr[2].id = SAI_NAT_ENTRY_ATTR_SRC_IP_MASK; + nat_entry_attr[2].value.u32 = 0xffffffff; + nat_entry_attr[3].id = SAI_NAT_ENTRY_ATTR_L4_SRC_PORT; + nat_entry_attr[3].value.u16 = (uint16_t)(entry.translated_l4_port); + nat_entry_attr[4].id = SAI_NAT_ENTRY_ATTR_ENABLE_PACKET_COUNT; + nat_entry_attr[4].value.booldata = true; + nat_entry_attr[5].id = SAI_NAT_ENTRY_ATTR_ENABLE_BYTE_COUNT; + nat_entry_attr[5].value.booldata = true; + + attr_count = 6; + + memset(&snat_entry, 0, sizeof(snat_entry)); + + snat_entry.vr_id = gVirtualRouterId; + snat_entry.switch_id = gSwitchId; + snat_entry.data.key.src_ip = keyEntry.ip_address.getV4Addr(); + snat_entry.data.key.l4_src_port = (uint16_t)(keyEntry.l4_port); + snat_entry.data.mask.src_ip = 0xffffffff; + snat_entry.data.mask.l4_src_port = 0xffff; + snat_entry.data.key.proto = ip_protocol; + snat_entry.data.mask.proto = 0xff; + + status = sai_nat_api->create_nat_entry(&snat_entry, attr_count, nat_entry_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create %s SNAT NAPT entry with ip %s, port %d, prototype %s and it's translated ip %s, translated port %d", + entry.entry_type.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port, keyEntry.prototype.c_str(), + entry.translated_ip.to_string().c_str(), entry.translated_l4_port); + + return true; + } + + SWSS_LOG_NOTICE("Created %s SNAT NAPT entry with ip %s, port %d, prototype %s and it's translated ip %s, translated port %d", + entry.entry_type.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port, keyEntry.prototype.c_str(), + entry.translated_ip.to_string().c_str(), entry.translated_l4_port); + + m_naptEntries[keyEntry].addedToHw = true; + updateNaptCounters(keyEntry.prototype.c_str(), keyEntry.ip_address, keyEntry.l4_port, 0, 0); + + if (entry.entry_type == "static") + { + totalStaticNaptEntries++; + updateStaticNaptCounters(totalStaticNaptEntries); + } + else + { + totalDynamicNaptEntries++; + updateDynamicNaptCounters(totalDynamicNaptEntries); + } + totalEntries++; + + return true; +} + +// Add the Twice NAPT entry to the hardware +bool NatOrch::addHwTwiceNaptEntry(const TwiceNaptEntryKey &key) +{ + uint32_t attr_count; + sai_nat_entry_t dbl_nat_entry; + sai_attribute_t nat_entry_attr[10]; + uint8_t protoType = ((key.prototype == "TCP") ? IPPROTO_TCP : IPPROTO_UDP); + sai_status_t status; + + SWSS_LOG_ENTER(); + SWSS_LOG_INFO("Create Twice SNAPT entry for proto %s, src-ip %s, src port %d, dst-ip %s, dst port %d", + key.prototype.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, + key.dst_ip.to_string().c_str(), key.dst_l4_port); + + TwiceNaptEntryValue value = m_twiceNaptEntries[key]; + + memset(nat_entry_attr, 0, sizeof(nat_entry_attr)); + + nat_entry_attr[0].id = SAI_NAT_ENTRY_ATTR_NAT_TYPE; + nat_entry_attr[0].value.u32 = SAI_NAT_TYPE_DOUBLE_NAT; + nat_entry_attr[1].id = SAI_NAT_ENTRY_ATTR_SRC_IP; + nat_entry_attr[1].value.u32 = value.translated_src_ip.getV4Addr(); + nat_entry_attr[2].id = SAI_NAT_ENTRY_ATTR_SRC_IP_MASK; + nat_entry_attr[2].value.u32 = 0xffffffff; + nat_entry_attr[3].id = SAI_NAT_ENTRY_ATTR_L4_SRC_PORT; + nat_entry_attr[3].value.u16 = (uint16_t)(value.translated_src_l4_port); + nat_entry_attr[4].id = SAI_NAT_ENTRY_ATTR_DST_IP; + nat_entry_attr[4].value.u32 = value.translated_dst_ip.getV4Addr(); + nat_entry_attr[5].id = SAI_NAT_ENTRY_ATTR_DST_IP_MASK; + nat_entry_attr[5].value.u32 = 0xffffffff; + nat_entry_attr[6].id = SAI_NAT_ENTRY_ATTR_L4_DST_PORT; + nat_entry_attr[6].value.u16 = (uint16_t)(value.translated_dst_l4_port); + nat_entry_attr[7].id = SAI_NAT_ENTRY_ATTR_ENABLE_PACKET_COUNT; + nat_entry_attr[7].value.booldata = true; + nat_entry_attr[8].id = SAI_NAT_ENTRY_ATTR_ENABLE_BYTE_COUNT; + nat_entry_attr[8].value.booldata = true; + + attr_count = 9; + + memset(&dbl_nat_entry, 0, sizeof(dbl_nat_entry)); + + dbl_nat_entry.vr_id = gVirtualRouterId; + dbl_nat_entry.switch_id = gSwitchId; + dbl_nat_entry.data.key.src_ip = key.src_ip.getV4Addr(); + dbl_nat_entry.data.mask.src_ip = 0xffffffff; + dbl_nat_entry.data.key.l4_src_port = (uint16_t)(key.src_l4_port); + dbl_nat_entry.data.mask.l4_src_port = 0xffff; + dbl_nat_entry.data.key.dst_ip = key.dst_ip.getV4Addr(); + dbl_nat_entry.data.mask.dst_ip = 0xffffffff; + dbl_nat_entry.data.key.l4_dst_port = (uint16_t)(key.dst_l4_port); + dbl_nat_entry.data.mask.l4_dst_port = 0xffff; + dbl_nat_entry.data.key.proto = protoType; + dbl_nat_entry.data.mask.proto = 0xff; + + status = sai_nat_api->create_nat_entry(&dbl_nat_entry, attr_count, nat_entry_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create %s Twice NAPT entry with src ip %s, src port %d, dst ip %s dst port %d, prototype %s and \ + it's translated src ip %s, translated src port %d, translated dst ip %s, translated dst port %d ", + value.entry_type.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), + key.dst_l4_port, key.prototype.c_str(), value.translated_src_ip.to_string().c_str(), value.translated_src_l4_port, + value.translated_dst_ip.to_string().c_str(), value.translated_dst_l4_port); + + return true; + } + + + SWSS_LOG_NOTICE("Created %s Twice NAPT entry with src ip %s, src port %d, dst ip %s dst port %d, prototype %s and \ + it's translated src ip %s, translated src port %d, translated dst ip %s, translated dst port %d ", + value.entry_type.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), + key.dst_l4_port, key.prototype.c_str(), value.translated_src_ip.to_string().c_str(), value.translated_src_l4_port, + value.translated_dst_ip.to_string().c_str(), value.translated_dst_l4_port); + + updateTwiceNaptCounters(key, 0, 0); + m_twiceNaptEntries[key].addedToHw = true; + + totalDnatEntries++; + updateDnatCounters(totalDnatEntries); + totalEntries++; + + totalSnatEntries++; + updateSnatCounters(totalSnatEntries); + totalEntries++; + + if (value.entry_type == "static") + { + totalStaticTwiceNaptEntries++; + updateStaticTwiceNaptCounters(totalStaticTwiceNaptEntries); + } + else + { + totalDynamicTwiceNaptEntries++; + updateDynamicTwiceNaptCounters(totalDynamicTwiceNaptEntries); + } + + return true; +} + +// Remove the SNAT entry from the hardware +bool NatOrch::removeHwSnatEntry(const IpAddress &ip_address) +{ + sai_nat_entry_t snat_entry; + sai_status_t status; + + SWSS_LOG_ENTER(); + SWSS_LOG_INFO("Deleting SNAT entry ip %s from hardware", ip_address.to_string().c_str()); + + NatEntryValue entry = m_natEntries[ip_address]; + + memset(&snat_entry, 0, sizeof(snat_entry)); + + snat_entry.vr_id = gVirtualRouterId; + snat_entry.switch_id = gSwitchId; + snat_entry.data.key.src_ip = ip_address.getV4Addr(); + snat_entry.data.mask.src_ip = 0xffffffff; + + status = sai_nat_api->remove_nat_entry(&snat_entry); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_INFO("Failed to removed %s SNAT NAT entry with ip %s and it's translated ip %s", + entry.entry_type.c_str(), ip_address.to_string().c_str(), entry.translated_ip.to_string().c_str()); + } + else + { + SWSS_LOG_NOTICE("Removed %s SNAT NAT entry with ip %s and it's translated ip %s", + entry.entry_type.c_str(), ip_address.to_string().c_str(), entry.translated_ip.to_string().c_str()); + } + deleteNatCounters(ip_address); + m_natEntries.erase(ip_address); + + if (entry.entry_type == "static") + { + if (totalStaticNatEntries) + { + totalStaticNatEntries--; + updateStaticNatCounters(totalStaticNatEntries); + } + } + else + { + if (totalDynamicNatEntries) + { + totalDynamicNatEntries--; + updateDynamicNatCounters(totalDynamicNatEntries); + } + else + { + SWSS_LOG_ERROR("Found the total number dynamic nat entries to be corrupt, when removing SNAT entry with ip %s, translated ip %s!!", + ip_address.to_string().c_str(), entry.translated_ip.to_string().c_str()); + } + } + + if (totalSnatEntries) + { + totalSnatEntries--; + updateSnatCounters(totalSnatEntries); + } + else + { + SWSS_LOG_ERROR("Found the total number dynamic snat entries to be corrupt, when removing SNAT entry with ip %s, translated ip %s!!", + ip_address.to_string().c_str(), entry.translated_ip.to_string().c_str()); + } + + if (totalEntries) + { + totalEntries--; + } + + return true; +} + +// Remove the SNAPT entry from the hardware +bool NatOrch::removeHwSnaptEntry(const NaptEntryKey &keyEntry) +{ + sai_nat_entry_t snat_entry; + sai_status_t status; + uint8_t ip_protocol = ((keyEntry.prototype == "TCP") ? IPPROTO_TCP : IPPROTO_UDP); + + SWSS_LOG_ENTER(); + SWSS_LOG_INFO("Delete SNAPT entry for proto %s, src-ip %s, l4-port %d", + keyEntry.prototype.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port); + + /* Check the entry is present in cache */ + if (m_naptEntries.find(keyEntry) == m_naptEntries.end()) + { + SWSS_LOG_ERROR("SNAPT entry isn't found for ip %s, l4-port %d", keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port); + + return false; + } + + NaptEntryValue entry = m_naptEntries[keyEntry]; + + memset(&snat_entry, 0, sizeof(snat_entry)); + + snat_entry.vr_id = gVirtualRouterId; + snat_entry.switch_id = gSwitchId; + snat_entry.data.key.src_ip = keyEntry.ip_address.getV4Addr(); + snat_entry.data.key.l4_src_port = (uint16_t)(keyEntry.l4_port); + snat_entry.data.mask.src_ip = 0xffffffff; + snat_entry.data.mask.l4_src_port = 0xffff; + snat_entry.data.key.proto = ip_protocol; + snat_entry.data.mask.proto = 0xff; + + status = sai_nat_api->remove_nat_entry(&snat_entry); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_INFO("Failed to removed %s SNAT NAPT entry with ip %s, port %d, prototype %s and it's translated ip %s, translated port %d", + entry.entry_type.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port, keyEntry.prototype.c_str(), + entry.translated_ip.to_string().c_str(), entry.translated_l4_port); + } + else + { + SWSS_LOG_NOTICE("Removed %s SNAT NAPT entry with ip %s, port %d, prototype %s and it's translated ip %s, translated port %d", + entry.entry_type.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port, keyEntry.prototype.c_str(), + entry.translated_ip.to_string().c_str(), entry.translated_l4_port); + } + deleteNaptCounters(keyEntry.prototype.c_str(), keyEntry.ip_address, keyEntry.l4_port); + m_naptEntries.erase(keyEntry); + + if (entry.entry_type == "static") + { + if (totalStaticNaptEntries) + { + totalStaticNaptEntries--; + updateStaticNaptCounters(totalStaticNaptEntries); + } + } + else + { + if (totalDynamicNaptEntries) + { + totalDynamicNaptEntries--; + updateDynamicNaptCounters(totalDynamicNaptEntries); + } + else + { + SWSS_LOG_ERROR("Found the total number dynamic napt entries to be corrupt, when removing SNAPT entry with proto %s, ip %s, port %d, translated ip %s, translated port %d!!", + keyEntry.prototype.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port, + entry.translated_ip.to_string().c_str(), entry.translated_l4_port); + } + } + + if (totalSnatEntries) + { + totalSnatEntries--; + updateSnatCounters(totalSnatEntries); + } + else + { + SWSS_LOG_ERROR("Found the total number dynamic snat entries to be corrupt, when removing SNAPT entry with proto %s, ip %s, port %d, translated ip %s, translated port %d!!", + keyEntry.prototype.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port, + entry.translated_ip.to_string().c_str(), entry.translated_l4_port); + } + + if (totalEntries) + totalEntries--; + + return true; +} + +bool NatOrch::addNatEntry(const IpAddress &ip_address, const NatEntryValue &entry) +{ + SWSS_LOG_ENTER(); + + /* Check the entry is present in cache */ + if (m_natEntries.find(ip_address) != m_natEntries.end()) + { + SWSS_LOG_INFO("Duplicate %s %s NAT entry with ip %s and it's translated ip %s, do nothing", + entry.entry_type.c_str(), entry.nat_type.c_str(), ip_address.to_string().c_str(), + entry.translated_ip.to_string().c_str()); + return true; + } + + if ((entry.nat_type == "snat") and + (entry.entry_type == "dynamic")) + { + if (totalSnatEntries == maxAllowedSNatEntries) + { + SWSS_LOG_INFO("Reached the max allowed NAT entries in the hardware, dropping new SNAT translation with ip %s and translated ip %s", + ip_address.to_string().c_str(), entry.translated_ip.to_string().c_str()); + deleteConnTrackEntry(ip_address); + return true; + } + + m_natEntries[ip_address] = entry; + m_natEntries[ip_address].addedToHw = false; + + updateConnTrackTimeout(ip_address); + } + else + { + m_natEntries[ip_address] = entry; + m_natEntries[ip_address].addedToHw = false; + } + + if (entry.nat_type == "snat") + { + totalSnatEntries++; + updateSnatCounters(totalSnatEntries); + } + + if (!isNatEnabled()) + { + SWSS_LOG_WARN("NAT Feature is not yet enabled, skipped adding %s %s NAT entry with ip %s and it's translated ip %s", + entry.entry_type.c_str(), entry.nat_type.c_str(), ip_address.to_string().c_str(), + entry.translated_ip.to_string().c_str()); + + return true; + } + + if (entry.nat_type == "snat") + { + /* Add SNAT entry to the hardware */ + addHwSnatEntry(ip_address); + } + else if (entry.nat_type == "dnat") + { + if (gNhTrackingSupported == true) + { + /* Cache the DNAT entry in the nexthop resolution cache */ + addDnatToNhCache(entry.translated_ip, ip_address); + } + else + { + /* Add DNAT entry to the hardware */ + addHwDnatEntry(ip_address); + } + } + + return true; +} + +bool NatOrch::removeNatEntry(const IpAddress &ip_address) +{ + SWSS_LOG_ENTER(); + + /* Check the entry is present in cache */ + if (m_natEntries.find(ip_address) == m_natEntries.end()) + { + SWSS_LOG_INFO("NAT entry isn't found for ip %s", ip_address.to_string().c_str()); + + return true; + } + + NatEntryValue entry = m_natEntries[ip_address]; + + if (entry.nat_type == "snat") + { + /* Remove SNAT entry from the hardware */ + removeHwSnatEntry(ip_address); + } + else if (entry.nat_type == "dnat") + { + if (gNhTrackingSupported == true) + { + /* Cache the DNAT entry in the nexthop resolution cache */ + removeDnatFromNhCache(entry.translated_ip, ip_address); + } + else + { + removeHwDnatEntry(ip_address); + m_natEntries.erase(ip_address); + } + } + else + { + SWSS_LOG_ERROR("Invalid NAT %s type for removing the %s NAT entry with ip %s and it's translated ip %s", + entry.nat_type.c_str(), entry.entry_type.c_str(), ip_address.to_string().c_str(), entry.translated_ip.to_string().c_str()); + + return false; + } + + return true; +} + +bool NatOrch::addTwiceNatEntry(const TwiceNatEntryKey &key, const TwiceNatEntryValue &value) +{ + SWSS_LOG_ENTER(); + + /* Check the entry is present in cache */ + if (m_twiceNatEntries.find(key) != m_twiceNatEntries.end()) + { + SWSS_LOG_INFO("Duplicate %s Twice NAT entry with src ip %s, dst ip %s and it's translated src ip %s, dst ip %s, do nothing", + value.entry_type.c_str(), key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str(), + value.translated_src_ip.to_string().c_str(), value.translated_dst_ip.to_string().c_str()); + return true; + } + + if (value.entry_type == "dynamic") + { + if (totalSnatEntries == maxAllowedSNatEntries) + { + SWSS_LOG_INFO("Reached the max allowed NAT entries in the hardware, dropping new Twice NAT translation with src ip %s, dst ip %s and translated src ip %s, dst ip %s", + key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str(), value.translated_src_ip.to_string().c_str(), value.translated_dst_ip.to_string().c_str()); + deleteConnTrackEntry(key); + return true; + } + } + m_twiceNatEntries[key] = value; + m_twiceNatEntries[key].addedToHw = false; + + updateConnTrackTimeout(key); + + if (!isNatEnabled()) + { + SWSS_LOG_WARN("NAT Feature is not yet enabled, skipped adding %s Twice NAT entry with src ip %s, dst ip %s and it's translated src ip %s, dst ip %s", + value.entry_type.c_str(), key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str(), + value.translated_src_ip.to_string().c_str(), value.translated_dst_ip.to_string().c_str()); + return true; + } + + if (gNhTrackingSupported == true) + { + /* Cache the Twice NAT entry in the nexthop resolution cache */ + addTwiceNatToNhCache(value.translated_dst_ip, key); + } + else + { + /* Add Twice NAT entry to the hardware */ + addHwTwiceNatEntry(key); + } + + return true; +} + +bool NatOrch::removeTwiceNatEntry(const TwiceNatEntryKey &key) +{ + SWSS_LOG_ENTER(); + + /* Check the entry is present in cache */ + if (m_twiceNatEntries.find(key) == m_twiceNatEntries.end()) + { + SWSS_LOG_INFO("Twice NAT entry isn't found for src ip %s, dst ip %s", key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str()); + + return true; + } + + TwiceNatEntryValue value = m_twiceNatEntries[key]; + + if (gNhTrackingSupported == true) + { + removeTwiceNatFromNhCache(value.translated_dst_ip, key); + } + else + { + removeHwTwiceNatEntry(key); + m_twiceNatEntries.erase(key); + } + + return true; +} + +bool NatOrch::addNaptEntry(const NaptEntryKey &keyEntry, const NaptEntryValue &entry) +{ + SWSS_LOG_ENTER(); + + /* Check the entry is present in cache */ + if (m_naptEntries.find(keyEntry) != m_naptEntries.end()) + { + NaptEntryValue oldEntry = m_naptEntries[keyEntry]; + + /* Entry is exist*/ + if ((entry.translated_l4_port != oldEntry.translated_l4_port) or + (entry.translated_ip.to_string() != oldEntry.translated_ip.to_string()) or + (entry.nat_type != oldEntry.nat_type)) + { + SWSS_LOG_INFO("%s %s NAPT entry already exists with ip %s, port %d, prototype %s and it's translated ip %s, translated port %d", + oldEntry.entry_type.c_str(), oldEntry.nat_type.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port, + keyEntry.prototype.c_str(), oldEntry.translated_ip.to_string().c_str(), oldEntry.translated_l4_port); + + /* Removed the NAPT entry */ + SWSS_LOG_INFO("Removing %s %s NAPT entry with ip %s, port %d, prototype %s and it's translated ip %s, translated port %d", + oldEntry.entry_type.c_str(), oldEntry.nat_type.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port, + keyEntry.prototype.c_str(), oldEntry.translated_ip.to_string().c_str(), oldEntry.translated_l4_port); + + removeNaptEntry(keyEntry); + } + else if (entry.entry_type != oldEntry.entry_type) + { + SWSS_LOG_INFO("%s %s NAPT entry already exists with ip %s, port %d, prototype %s and it's translated ip %s, translated port %d, is moved to %s NAPT", + oldEntry.entry_type.c_str(), entry.nat_type.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port, + keyEntry.prototype.c_str(), entry.translated_ip.to_string().c_str(), entry.translated_l4_port, entry.entry_type.c_str()); + + m_naptEntries[keyEntry].entry_type = entry.entry_type; + if (entry.entry_type == "static") + { + totalStaticNaptEntries++; + totalDynamicNaptEntries--; + updateStaticNaptCounters(totalStaticNaptEntries); + updateDynamicNaptCounters(totalDynamicNaptEntries); + } + return true; + } + else + { + SWSS_LOG_INFO("Duplicate %s %s NAPT entry already exists with ip %s, port %d, prototype %s and it's translated ip %s, translated port %d, do nothing", + entry.entry_type.c_str(), entry.nat_type.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port, + keyEntry.prototype.c_str(), entry.translated_ip.to_string().c_str(), entry.translated_l4_port); + return true; + } + } + + if ((entry.nat_type == "snat") and + (entry.entry_type == "dynamic")) + { + if (totalSnatEntries == maxAllowedSNatEntries) + { + SWSS_LOG_INFO("Reached the max allowed NAT entries in the hardware, dropping new SNAPT translation with ip %s, port %d, prototype %s, translated ip %s, translated port %d", + keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port, + keyEntry.prototype.c_str(), entry.translated_ip.to_string().c_str(), entry.translated_l4_port); + deleteConnTrackEntry(keyEntry); + return true; + } + + m_naptEntries[keyEntry] = entry; + m_naptEntries[keyEntry].addedToHw = false; + + updateConnTrackTimeout(keyEntry); + } + else + { + m_naptEntries[keyEntry] = entry; + m_naptEntries[keyEntry].addedToHw = false; + } + + if (entry.nat_type == "snat") + { + totalSnatEntries++; + updateSnatCounters(totalSnatEntries); + } + + if (!isNatEnabled()) + { + SWSS_LOG_WARN("NAT feature is not yet enabled, skipped adding %s %s NAPT entry with ip %s, port %d, prototype %s and it's translated ip %s, translated port %d", + entry.entry_type.c_str(), entry.nat_type.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port, keyEntry.prototype.c_str(), + entry.translated_ip.to_string().c_str(), entry.translated_l4_port); + + return true; + } + + if (entry.nat_type == "snat") + { + /* Add SNAPT entry to the hardware */ + addHwSnaptEntry(keyEntry); + } + else if (entry.nat_type == "dnat") + { + if (gNhTrackingSupported == true) + { + /* Cache the DNAPT entry in the nexthop resolution cache */ + addDnaptToNhCache(entry.translated_ip, keyEntry); + } + else + { + /* Add DNAPT entry in the hardware */ + addHwDnaptEntry(keyEntry); + } + } + else + { + SWSS_LOG_ERROR("Invalid NAT %s type for adding the %s NAPT entry with ip %s, port %d, prototype %s and it's translated ip %s, translated port %d", + entry.nat_type.c_str(), entry.entry_type.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port, keyEntry.prototype.c_str(), + entry.translated_ip.to_string().c_str(), entry.translated_l4_port); + + return false; + } + + return true; +} + +bool NatOrch::removeNaptEntry(const NaptEntryKey &keyEntry) +{ + SWSS_LOG_ENTER(); + + if (m_naptEntries.find(keyEntry) == m_naptEntries.end()) + { + SWSS_LOG_INFO("NAPT entry isn't found for prototype - %s, ip - %s and port - %d", + keyEntry.prototype.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port); + + return true; + } + + NaptEntryValue entry = m_naptEntries[keyEntry]; + + if (entry.nat_type == "snat") + { + /* Remove SNAPT entry from the hardware */ + removeHwSnaptEntry(keyEntry); + } + else if (entry.nat_type == "dnat") + { + if (gNhTrackingSupported == true) + { + removeDnaptFromNhCache(entry.translated_ip, keyEntry); + } + else + { + removeHwDnaptEntry(keyEntry); + m_naptEntries.erase(keyEntry); + } + } + else + { + SWSS_LOG_ERROR("Invalid NAT %s type for removing the %s NAPT entry with ip %s, port %d, prototype %s and it's translated ip %s, translated port %d", + entry.nat_type.c_str(), entry.entry_type.c_str(), keyEntry.ip_address.to_string().c_str(), keyEntry.l4_port, keyEntry.prototype.c_str(), + entry.translated_ip.to_string().c_str(), entry.translated_l4_port); + + return false; + } + + return true; +} + +bool NatOrch::addTwiceNaptEntry(const TwiceNaptEntryKey &key, const TwiceNaptEntryValue &value) +{ + SWSS_LOG_ENTER(); + + /* Check the entry is present in the cache */ + if (m_twiceNaptEntries.find(key) != m_twiceNaptEntries.end()) + { + TwiceNaptEntryValue oldEntry = m_twiceNaptEntries[key]; + + /* Entry exists */ + if ((value.translated_src_l4_port != oldEntry.translated_src_l4_port) or + (value.translated_dst_l4_port != oldEntry.translated_dst_l4_port) or + (value.translated_src_ip.to_string() != oldEntry.translated_src_ip.to_string()) or + (value.translated_dst_ip.to_string() != oldEntry.translated_dst_ip.to_string())) + { + SWSS_LOG_INFO("Twice NAPT entry already exists with src ip %s, src port %d, dst ip %s, dst port %d, prototype %s and it's translated src ip %s, \ + translated src port %d, translated dst ip %s, translated dst port %d", + key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), key.dst_l4_port, + key.prototype.c_str(), oldEntry.translated_src_ip.to_string().c_str(), oldEntry.translated_src_l4_port, + oldEntry.translated_dst_ip.to_string().c_str(), oldEntry.translated_dst_l4_port); + + /* Removed the NAPT entry */ + SWSS_LOG_INFO("Removing %s Twice NAPT entry with src ip %s, src port %d, dst ip %s, dst port %d, prototype %s", + oldEntry.entry_type.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, + key.dst_ip.to_string().c_str(), key.dst_l4_port, key.prototype.c_str()); + + removeTwiceNaptEntry(key); + } + else if (value.entry_type != oldEntry.entry_type) + { + SWSS_LOG_INFO("Entry type change, %s Twice NAPT entry already exists with src ip %s, src port %d, dst ip %s, dst port %d, prototype %s and it's translated src ip %s, \ + translated src port %d, translated dst ip %s, translated dst port %d", + oldEntry.entry_type.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), key.dst_l4_port, + key.prototype.c_str(), oldEntry.translated_src_ip.to_string().c_str(), oldEntry.translated_src_l4_port, + oldEntry.translated_dst_ip.to_string().c_str(), oldEntry.translated_dst_l4_port); + + m_twiceNaptEntries[key].entry_type = value.entry_type; + + if (value.entry_type == "static") + { + totalStaticTwiceNaptEntries++; + totalDynamicTwiceNaptEntries--; + updateStaticTwiceNaptCounters(totalStaticTwiceNaptEntries); + updateDynamicTwiceNaptCounters(totalDynamicTwiceNaptEntries); + } + return true; + } + else + { + SWSS_LOG_INFO("Duplicate %s Twice NAPT entry already exists with src ip %s, src port %d, dst ip %s, dst port %d, prototype %s and \ + it's translated src ip %s, translated src port %d, translated dst ip %s, translated dst port %d, do nothing", + value.entry_type.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), + key.dst_l4_port, key.prototype.c_str(), value.translated_src_ip.to_string().c_str(), + value.translated_src_l4_port, value.translated_dst_ip.to_string().c_str(), value.translated_dst_l4_port); + return true; + } + } + + if (value.entry_type == "dynamic") + { + if (totalSnatEntries == maxAllowedSNatEntries) + { + SWSS_LOG_INFO("Reached the max allowed NAT entries in the hardware, dropping new Twice SNAPT translation with src ip %s, src port %d, prototype %s, \ + dst ip %s, dst port %d", + key.src_ip.to_string().c_str(), key.src_l4_port, key.prototype.c_str(), key.dst_ip.to_string().c_str(), key.dst_l4_port); + deleteConnTrackEntry(key); + return true; + } + } + m_twiceNaptEntries[key] = value; + m_twiceNaptEntries[key].addedToHw = false; + + updateConnTrackTimeout(key); + + if (!isNatEnabled()) + { + SWSS_LOG_WARN("NAT feature is not yet enabled, skipped adding %s Twice NAPT entry with src ip %s, src port %d, prototype %s, dst ip %s, dst port %d", + value.entry_type.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, key.prototype.c_str(), + key.dst_ip.to_string().c_str(), key.dst_l4_port); + + return true; + } + + if (gNhTrackingSupported == true) + { + /* Add Twice NAPT entry to the NH resolv cache */ + addTwiceNaptToNhCache(value.translated_dst_ip, key); + } + else + { + /* Add Twice NAPT entry to the hardware */ + addHwTwiceNaptEntry(key); + } + + return true; +} + +bool NatOrch::removeTwiceNaptEntry(const TwiceNaptEntryKey &key) +{ + SWSS_LOG_ENTER(); + + if (m_twiceNaptEntries.find(key) == m_twiceNaptEntries.end()) + { + SWSS_LOG_INFO("Twice NAPT entry isn't found for prototype - %s, src ip %s src port %d dst ip %s dst port %d", + key.prototype.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, + key.dst_ip.to_string().c_str(), key.dst_l4_port); + return true; + } + + TwiceNaptEntryValue value = m_twiceNaptEntries[key]; + + if (gNhTrackingSupported == true) + { + /* Remove Twice NAPT entry from the NH resolv cache */ + removeTwiceNaptFromNhCache(value.translated_dst_ip, key); + } + else + { + removeHwTwiceNaptEntry(key); + m_twiceNaptEntries.erase(key); + } + + return true; +} + +bool NatOrch::isNatEnabled(void) +{ + if (admin_mode == "enabled") + { + return true; + } + + return false; +} + +void NatOrch::flushAllNatEntries(void) +{ + std::string res; + const std::string cmds = std::string("") + CONNTRACK + FLUSH; + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Cleared the All NAT Entries"); + } +} + +void NatOrch::clearAllDnatEntries(void) +{ + IpAddress dstIp; + NaptEntryKey keyEntry; + NatEntryValue natEntry; + NaptEntryValue naptEntry; + TwiceNatEntryKey twiceNatKey; + TwiceNatEntryValue twiceNatValue; + TwiceNaptEntryKey twiceNaptKey; + TwiceNaptEntryValue twiceNaptValue; + + NatEntry::iterator natIter = m_natEntries.begin(); + while (natIter != m_natEntries.end()) + { + natEntry = (*natIter).second; + dstIp = (*natIter).first; + natIter++; + + if (natEntry.addedToHw == true) + { + if (natEntry.nat_type == "dnat") + { + if (gNhTrackingSupported == true) + { + removeDnatFromNhCache(natEntry.translated_ip, dstIp); + } + else + { + removeHwDnatEntry(dstIp); + m_natEntries.erase(dstIp); + } + } + } + } + + NaptEntry::iterator naptIter = m_naptEntries.begin(); + while (naptIter != m_naptEntries.end()) + { + naptEntry = (*naptIter).second; + keyEntry = (*naptIter).first; + naptIter++; + + if (naptEntry.addedToHw == true) + { + if (naptEntry.nat_type == "dnat") + { + if (gNhTrackingSupported == true) + { + removeDnaptFromNhCache(naptEntry.translated_ip, keyEntry); + } + else + { + removeHwDnaptEntry(keyEntry); + m_naptEntries.erase(keyEntry); + } + } + } + } + + TwiceNatEntry::iterator twiceNatIter = m_twiceNatEntries.begin(); + while (twiceNatIter != m_twiceNatEntries.end()) + { + twiceNatValue = (*twiceNatIter).second; + twiceNatKey = (*twiceNatIter).first; + twiceNatIter++; + + if (twiceNatValue.addedToHw == true) + { + if (gNhTrackingSupported == true) + { + removeTwiceNatFromNhCache(twiceNatValue.translated_dst_ip, twiceNatKey); + } + else + { + removeHwTwiceNatEntry(twiceNatKey); + m_twiceNatEntries.erase(twiceNatKey); + } + } + } + + TwiceNaptEntry::iterator twiceNaptIter = m_twiceNaptEntries.begin(); + while (twiceNaptIter != m_twiceNaptEntries.end()) + { + twiceNaptValue = (*twiceNaptIter).second; + twiceNaptKey = (*twiceNaptIter).first; + twiceNaptIter++; + + if (twiceNaptValue.addedToHw == true) + { + if (gNhTrackingSupported == true) + { + removeTwiceNaptFromNhCache(twiceNaptValue.translated_dst_ip, twiceNaptKey); + } + else + { + removeHwTwiceNaptEntry(twiceNaptKey); + m_twiceNaptEntries.erase(twiceNaptKey); + } + } + } +} + +void NatOrch::cleanupAppDbEntries(void) +{ + /* Iterate over all the entries clean them up from the APP_DB + * and from the hardware */ + string appDbKey; + IpAddress ip; + NaptEntryKey keyEntry; + TwiceNatEntryKey twiceNatKey; + TwiceNaptEntryKey twiceNaptKey; + + NatEntry::iterator natIter = m_natEntries.begin(); + while (natIter != m_natEntries.end()) + { + ip = (*natIter).first; + natIter++; + + /* Remove from APP_DB */ + appDbKey = ip.to_string(); + m_natQueryTable.del(appDbKey); + + /* Remove from ASIC */ + removeNatEntry(ip); + + SWSS_LOG_INFO("Removed NAT entry from APP_DB and ASIC - %s", appDbKey.c_str()); + } + + NaptEntry::iterator naptIter = m_naptEntries.begin(); + while (naptIter != m_naptEntries.end()) + { + keyEntry = (*naptIter).first; + naptIter++; + + /* Remove from APP_DB */ + appDbKey = keyEntry.prototype + ":" + keyEntry.ip_address.to_string() + ":" + std::to_string(keyEntry.l4_port); + m_naptQueryTable.del(appDbKey); + + /* Remove from ASIC */ + removeNaptEntry(keyEntry); + + SWSS_LOG_INFO("Removed NAPT entry from APP_DB and ASIC - %s", appDbKey.c_str()); + } + + TwiceNatEntry::iterator twiceNatIter = m_twiceNatEntries.begin(); + while (twiceNatIter != m_twiceNatEntries.end()) + { + twiceNatKey = (*twiceNatIter).first; + twiceNatIter++; + + /* Remove from APP_DB */ + appDbKey = twiceNatKey.src_ip.to_string() + ":" + twiceNatKey.dst_ip.to_string(); + m_twiceNatQueryTable.del(appDbKey); + + /* Remove from ASIC */ + removeTwiceNatEntry(twiceNatKey); + + SWSS_LOG_INFO("Removed Twice NAT entry from APP_DB and ASIC - %s", appDbKey.c_str()); + } + + TwiceNaptEntry::iterator twiceNaptIter = m_twiceNaptEntries.begin(); + while (twiceNaptIter != m_twiceNaptEntries.end()) + { + twiceNaptKey = (*twiceNaptIter).first; + twiceNaptIter++; + + /* Remove from APP_DB */ + appDbKey = twiceNaptKey.prototype + ":" + twiceNaptKey.src_ip.to_string() + ":" + + std::to_string(twiceNaptKey.src_l4_port) + ":" + twiceNaptKey.dst_ip.to_string() + + ":" + std::to_string(twiceNaptKey.dst_l4_port); + m_twiceNaptQueryTable.del(appDbKey); + + /* Remove from ASIC */ + removeTwiceNaptEntry(twiceNaptKey); + + SWSS_LOG_INFO("Removed Twice NAPT entry from APP_DB and ASIC - %s", appDbKey.c_str()); + } +} + +bool NatOrch::warmBootingInProgress(void) +{ + std::string value; + + m_stateWarmRestartEnableTable.hget("system", "enable", value); + if (value == "true") + { + SWSS_LOG_INFO("Warm reboot enabled"); + m_stateWarmRestartTable.hget("natsyncd", "state", value); + if (value != "reconciled") + { + SWSS_LOG_NOTICE("Nat conntrack state reconciliation not completed yet"); + return true; + } + return false; + } + else + { + SWSS_LOG_INFO("Warm reboot not enabled"); + } + return false; +} + +void NatOrch::enableNatFeature(void) +{ + sai_status_t status; + sai_attribute_t attr; + + SWSS_LOG_INFO("Verify NAT is supported or not"); + + if (gIsNatSupported == false) + { + SWSS_LOG_NOTICE("NAT Feature is not supported in this Platform"); + return; + } + else + { + admin_mode = "enabled"; + SWSS_LOG_INFO("NAT Feature is supported with available limit : %d", maxAllowedSNatEntries); + } + + SWSS_LOG_INFO("Enabling NAT "); + + memset(&attr, 0, sizeof(attr)); + attr.id = SAI_SWITCH_ATTR_NAT_ENABLE; + attr.value.booldata = true; + + status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to enable NAT: %d", status); + } + + SWSS_LOG_INFO("NAT Query timer start "); + m_natQueryTimer->start(); + + if (gNhTrackingSupported == true) + { + SWSS_LOG_INFO("Attach to Neighbor Orch "); + m_neighOrch->attach(this); + } + if (! warmBootingInProgress()) + { + SWSS_LOG_NOTICE("Not warm rebooting, so clearing all conntrack Entries on nat feature enable"); + flushAllNatEntries(); + } + + SWSS_LOG_INFO("Adding NAT Entries "); + addAllNatEntries(); + + if (! warmBootingInProgress()) + { + SWSS_LOG_NOTICE("Not warm rebooting, so adding static conntrack Entries"); + addAllStaticConntrackEntries(); + } + +} + +void NatOrch::disableNatFeature(void) +{ + sai_status_t status; + sai_attribute_t attr; + + SWSS_LOG_INFO("Disabling NAT "); + + memset(&attr, 0, sizeof(attr)); + + admin_mode = "disabled"; + attr.id = SAI_SWITCH_ATTR_NAT_ENABLE; + attr.value.booldata = false; + + status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to disable NAT: %d", status); + } + + SWSS_LOG_INFO("NAT Query timer stop "); + m_natQueryTimer->stop(); + + if (gNhTrackingSupported == true) + { + SWSS_LOG_INFO("Detach to Neighbor Orch "); + m_neighOrch->detach(this); + } + + SWSS_LOG_INFO("Clear all dynamic NAT Entries "); + flushAllNatEntries(); + + SWSS_LOG_INFO("Clear all DNAT Entries "); + clearAllDnatEntries(); +} + +void NatOrch::doNatTableTask(Consumer& consumer) +{ + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + string op = kfvOp(t); + vector keys = tokenize(key, ':'); + IpAddress global_address; + /* Example : APPL_DB + * NAT_TABLE:65.55.45.1 + * translated_ip: 10.0.0.1 + * nat_type: dnat + * entry_type: static + */ + + /* Ensure the key size is 1 otherwise ignore */ + if (keys.size() != 1) + { + SWSS_LOG_ERROR("Invalid key size, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + IpAddress ip_address = IpAddress(key); + + if (op == SET_COMMAND) + { + NatEntryValue entry; + string type; + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "entry_type") + type = fvValue(i); + else if (fvField(i) == "translated_ip") + entry.translated_ip = IpAddress(fvValue(i)); + else if (fvField(i) == "nat_type") + entry.nat_type = fvValue(i); + } + + /* NAT type is either dynamic or static */ + assert(type == "dynamic" || type == "static"); + entry.entry_type = type; + entry.addedToHw = false; + + if (addNatEntry(ip_address, entry)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else if (op == DEL_COMMAND) + { + if (removeNatEntry(ip_address)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +void NatOrch::doNaptTableTask(Consumer& consumer) +{ + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + string op = kfvOp(t); + vector keys = tokenize(key, ':'); + + /* Example : APPL_DB + * NAPT_TABLE:TCP:65.55.42.1:1024 + * translated_ip: 10.0.0.1 + * translated_l4_port: 6000 + * nat_type: snat + * entry_type: static + */ + + /* Ensure the key size is 5 otherwise ignore */ + if (keys.size() != 3) + { + SWSS_LOG_ERROR("Invalid key size, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + NaptEntryKey keyEntry; + + keyEntry.ip_address = IpAddress(keys[1]); + keyEntry.l4_port = stoi(keys[2]); + keyEntry.prototype = keys[0]; + + if (op == SET_COMMAND) + { + NaptEntryValue entry; + string type; + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "entry_type") + type = fvValue(i); + else if (fvField(i) == "translated_ip") + entry.translated_ip = IpAddress(fvValue(i)); + else if (fvField(i) == "translated_l4_port") + entry.translated_l4_port = stoi(fvValue(i)); + else if (fvField(i) == "nat_type") + entry.nat_type = fvValue(i); + } + + /* NAT type is either dynamic or static */ + assert(type == "dynamic" || type == "static"); + entry.entry_type = type; + entry.addedToHw = false; + + if (addNaptEntry(keyEntry, entry)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else if (op == DEL_COMMAND) + { + if (removeNaptEntry(keyEntry)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +void NatOrch::doTwiceNatTableTask(Consumer& consumer) +{ + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + string op = kfvOp(t); + vector keys = tokenize(key, ':'); + IpAddress global_address; + /* Example : APPL_DB + * NAT_TWICE_TABLE:91.91.91.91:65.55.45.1 + * translated_src_ip: 14.14.14.14 + * translated_dst_ip: 12.12.12.12 + * entry_type: static + */ + + /* Ensure the key size is 2 otherwise ignore */ + if (keys.size() != 2) + { + SWSS_LOG_ERROR("Invalid key size, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + TwiceNatEntryKey keyEntry; + keyEntry.src_ip = IpAddress(keys[0]); + keyEntry.dst_ip = IpAddress(keys[1]); + + if (op == SET_COMMAND) + { + TwiceNatEntryValue entry; + string type; + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "entry_type") + type = fvValue(i); + else if (fvField(i) == "translated_src_ip") + entry.translated_src_ip = IpAddress(fvValue(i)); + else if (fvField(i) == "translated_dst_ip") + entry.translated_dst_ip = IpAddress(fvValue(i)); + } + + /* NAT type is either dynamic or static */ + assert(type == "dynamic" || type == "static"); + entry.entry_type = type; + entry.addedToHw = false; + + if (addTwiceNatEntry(keyEntry, entry)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else if (op == DEL_COMMAND) + { + if (removeTwiceNatEntry(keyEntry)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +void NatOrch::doTwiceNaptTableTask(Consumer& consumer) +{ + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + string op = kfvOp(t); + vector keys = tokenize(key, ':'); + + /* Example : APPL_DB + * NAPT_TWICE_TABLE:TCP:91.91.91.91:6363:165.55.42.1:1024 + * translated_src_ip: 14.14.14.14 + * translated_src_l4_port: 6000 + * translated_dst_ip: 12.12.12.12 + * translated_dst_l4_port: 8000 + * entry_type: static + */ + + /* Ensure the key size is 5 otherwise ignore */ + if (keys.size() != 5) + { + SWSS_LOG_ERROR("Invalid key size, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + TwiceNaptEntryKey keyEntry; + + keyEntry.src_ip = IpAddress(keys[1]); + keyEntry.src_l4_port = stoi(keys[2]); + keyEntry.dst_ip = IpAddress(keys[3]); + keyEntry.dst_l4_port = stoi(keys[4]); + keyEntry.prototype = keys[0]; + + if (op == SET_COMMAND) + { + TwiceNaptEntryValue entry; + string type; + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "entry_type") + type = fvValue(i); + else if (fvField(i) == "translated_src_ip") + entry.translated_src_ip = IpAddress(fvValue(i)); + else if (fvField(i) == "translated_src_l4_port") + entry.translated_src_l4_port = stoi(fvValue(i)); + else if (fvField(i) == "translated_dst_ip") + entry.translated_dst_ip = IpAddress(fvValue(i)); + else if (fvField(i) == "translated_dst_l4_port") + entry.translated_dst_l4_port = stoi(fvValue(i)); + } + + /* NAT type is either dynamic or static */ + assert(type == "dynamic" || type == "static"); + entry.entry_type = type; + entry.addedToHw = false; + + if (addTwiceNaptEntry(keyEntry, entry)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else if (op == DEL_COMMAND) + { + if (removeTwiceNaptEntry(keyEntry)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +void NatOrch::doNatGlobalTableTask(Consumer& consumer) +{ + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + string op = kfvOp(t); + string mode; + vector keys = tokenize(key, ':'); + + /* Example : APPL_DB + * NAT_GLOBAL_TABLE:Values + * admin_mode: disabled + * nat_timeout : 600 + * nat_tcp_timeout : 100 + * nat_udp_timeout : 500 + */ + + /* Ensure the key is "Values" otherwise ignore */ + if (strcmp(key.c_str(), VALUES)) + { + SWSS_LOG_ERROR("Invalid key format. No Values: %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "admin_mode") + { + mode = fvValue(i); + + /* NAT mode is either enabled or disabled */ + assert(mode == "enabled" || mode == "disabled"); + + if (mode != admin_mode) + { + if (mode == "enabled") + enableNatFeature(); + else + disableNatFeature(); + } + } + else if (fvField(i) == "nat_tcp_timeout") + { + tcp_timeout = stoi(fvValue(i)); + updateConnTrackTimeout("tcp"); + } + else if (fvField(i) == "nat_udp_timeout") + { + udp_timeout = stoi(fvValue(i)); + updateConnTrackTimeout("udp"); + } + else if (fvField(i) == "nat_timeout") + { + timeout = stoi(fvValue(i)); + updateConnTrackTimeout("all"); + } + } + + SWSS_LOG_INFO("Global Values - Admin mode - %s, TCP - %d, UDP - %d and Both - %d", admin_mode.c_str(), tcp_timeout, udp_timeout, timeout); + + it = consumer.m_toSync.erase(it); + } +} + +void NatOrch::doTask(Consumer& consumer) +{ + SWSS_LOG_ENTER(); + + string table_name = consumer.getTableName(); + + unique_lock lock(m_natMutex); + + if (table_name == APP_NAT_TABLE_NAME) + { + SWSS_LOG_INFO("Received APP_NAT_TABLE_NAME update"); + doNatTableTask(consumer); + } + else if (table_name == APP_NAPT_TABLE_NAME) + { + SWSS_LOG_INFO("Received APP_NAPT_TABLE_NAME update"); + doNaptTableTask(consumer); + } + if (table_name == APP_NAT_TWICE_TABLE_NAME) + { + SWSS_LOG_INFO("Received APP_NAT_TWICE_TABLE_NAME update"); + doTwiceNatTableTask(consumer); + } + else if (table_name == APP_NAPT_TWICE_TABLE_NAME) + { + SWSS_LOG_INFO("Received APP_NAPT_TWICE_TABLE_NAME update"); + doTwiceNaptTableTask(consumer); + } + else if (table_name == APP_NAT_GLOBAL_TABLE_NAME) + { + SWSS_LOG_INFO("Received APP_NAT_GLOBAL_TABLE_NAME update"); + doNatGlobalTableTask(consumer); + } + else + { + SWSS_LOG_INFO("Received unknown NAT Table - %s notification", table_name.c_str()); + return; + } +} + +struct timespec getTimeDiff(const struct timespec &begin, const struct timespec &end) +{ + struct timespec diff = {0, 0}; + + if (end.tv_nsec-begin.tv_nsec < 0) + { + diff.tv_sec = end.tv_sec - begin.tv_sec - 1; + diff.tv_nsec = end.tv_nsec - begin.tv_nsec + 1000000000UL; + } + else + { + diff.tv_sec = end.tv_sec - begin.tv_sec; + diff.tv_nsec = end.tv_nsec - begin.tv_nsec; + } + return diff; +} + +void NatOrch::doTask(SelectableTimer &timer) +{ + SWSS_LOG_ENTER(); + + if (((natTimerTickCntr++) % NAT_HITBIT_QUERY_MULTIPLE) == 0) + { + queryHitBits(); + } + queryCounters(); +} + +void NatOrch::queryCounters(void) +{ + SWSS_LOG_ENTER(); + + uint32_t queried_entries = 0; + struct timespec time_now, time_end, time_spent; + + if (clock_gettime (CLOCK_MONOTONIC, &time_now) < 0) + { + return; + } + + NatEntry::iterator natIter = m_natEntries.begin(); + while (natIter != m_natEntries.end()) + { + getNatCounters(natIter); + + queried_entries++; + natIter++; + } + + NaptEntry::iterator naptIter = m_naptEntries.begin(); + while (naptIter != m_naptEntries.end()) + { + getNaptCounters(naptIter); + + queried_entries++; + naptIter++; + } + TwiceNatEntry::iterator tnatIter = m_twiceNatEntries.begin(); + while (tnatIter != m_twiceNatEntries.end()) + { + getTwiceNatCounters(tnatIter); + + queried_entries++; + tnatIter++; + } + + TwiceNaptEntry::iterator tnaptIter = m_twiceNaptEntries.begin(); + while (tnaptIter != m_twiceNaptEntries.end()) + { + getTwiceNaptCounters(tnaptIter); + + queried_entries++; + tnaptIter++; + } + + if (clock_gettime (CLOCK_MONOTONIC, &time_end) < 0) + { + return; + } + time_spent = getTimeDiff(time_now, time_end); + + if (queried_entries) + { + SWSS_LOG_DEBUG("Time spent in querying counters for %u NAT/NAPT entries = %lu secs, %lu msecs", + queried_entries, time_spent.tv_sec, (time_spent.tv_nsec / 1000000UL)); + } +} + +void NatOrch::addAllNatEntries(void) +{ + SWSS_LOG_ENTER(); + + NatEntry::iterator natIter = m_natEntries.begin(); + while (natIter != m_natEntries.end()) + { + if ((*natIter).second.addedToHw == false) + { + if ((*natIter).second.nat_type == "snat") + { + /* Add SNAT entry to the hardware */ + addHwSnatEntry((*natIter).first); + } + else if ((*natIter).second.nat_type == "dnat") + { + if (gNhTrackingSupported == true) + { + addDnatToNhCache((*natIter).second.translated_ip, (*natIter).first); + } + else + { + addHwDnatEntry((*natIter).first); + } + } + } + natIter++; + } + + NaptEntry::iterator naptIter = m_naptEntries.begin(); + while (naptIter != m_naptEntries.end()) + { + if ((*naptIter).second.addedToHw == false) + { + if ((*naptIter).second.nat_type == "snat") + { + /* Add SNAPT entry to the hardware */ + addHwSnaptEntry((*naptIter).first); + } + else if ((*naptIter).second.nat_type == "dnat") + { + if (gNhTrackingSupported == true) + { + addDnaptToNhCache((*naptIter).second.translated_ip, (*naptIter).first); + } + else + { + addHwDnaptEntry((*naptIter).first); + } + } + } + naptIter++; + } + + TwiceNatEntry::iterator twiceNatIter = m_twiceNatEntries.begin(); + while (twiceNatIter != m_twiceNatEntries.end()) + { + if ((*twiceNatIter).second.addedToHw == false) + { + if (gNhTrackingSupported == true) + { + /* Cache the Twice NAT entry in the nexthop resolution cache */ + addTwiceNatToNhCache((*twiceNatIter).second.translated_dst_ip, (*twiceNatIter).first); + } + else + { + /* Add Twice NAT entry to the hardware */ + addHwTwiceNatEntry((*twiceNatIter).first); + } + } + twiceNatIter++; + } + + TwiceNaptEntry::iterator twiceNaptIter = m_twiceNaptEntries.begin(); + while (twiceNaptIter != m_twiceNaptEntries.end()) + { + if ((*twiceNaptIter).second.addedToHw == false) + { + if (gNhTrackingSupported == true) + { + /* Cache the Twice NAPT entry in the nexthop resolution cache */ + addTwiceNaptToNhCache((*twiceNaptIter).second.translated_dst_ip, (*twiceNaptIter).first); + } + else + { + /* Add Twice NAPT entry to the hardware */ + addHwTwiceNaptEntry((*twiceNaptIter).first); + } + } + twiceNaptIter++; + } +} + +void NatOrch::clearCounters(void) +{ + SWSS_LOG_ENTER(); + + NatEntry::iterator natIter = m_natEntries.begin(); + while (natIter != m_natEntries.end()) + { + setNatCounters(natIter); + natIter++; + } + + NaptEntry::iterator naptIter = m_naptEntries.begin(); + while (naptIter != m_naptEntries.end()) + { + setNaptCounters(naptIter); + naptIter++; + } + + TwiceNatEntry::iterator twiceNatIter = m_twiceNatEntries.begin(); + while (twiceNatIter != m_twiceNatEntries.end()) + { + setTwiceNatCounters(twiceNatIter); + twiceNatIter++; + } + + TwiceNaptEntry::iterator twiceNaptIter = m_twiceNaptEntries.begin(); + while (twiceNaptIter != m_twiceNaptEntries.end()) + { + setTwiceNaptCounters(twiceNaptIter); + twiceNaptIter++; + } +} + +void NatOrch::addAllStaticConntrackEntries(void) +{ + SWSS_LOG_ENTER(); + + NatEntry::iterator natIter = m_natEntries.begin(); + while (natIter != m_natEntries.end()) + { + if (((*natIter).second.entry_type == "static") and + ((*natIter).second.addedToHw == true) and + ((*natIter).second.nat_type == "snat")) + { + addConnTrackEntry((*natIter).first); + } + natIter++; + } + NaptEntry::iterator naptIter = m_naptEntries.begin(); + while (naptIter != m_naptEntries.end()) + { + if (((*naptIter).second.entry_type == "static") and + ((*naptIter).second.addedToHw == true) and + ((*naptIter).second.nat_type == "snat")) + { + addConnTrackEntry((*naptIter).first); + } + naptIter++; + } + TwiceNatEntry::iterator twiceNatIter = m_twiceNatEntries.begin(); + while (twiceNatIter != m_twiceNatEntries.end()) + { + if (((*twiceNatIter).second.entry_type == "static") and + ((*twiceNatIter).second.addedToHw == true)) + { + addConnTrackEntry((*twiceNatIter).first); + } + twiceNatIter++; + } + TwiceNaptEntry::iterator twiceNaptIter = m_twiceNaptEntries.begin(); + while (twiceNaptIter != m_twiceNaptEntries.end()) + { + if (((*twiceNaptIter).second.entry_type == "static") and + ((*twiceNaptIter).second.addedToHw == true)) + { + addConnTrackEntry((*twiceNaptIter).first); + } + twiceNaptIter++; + } +} + +void NatOrch::queryHitBits(void) +{ + SWSS_LOG_ENTER(); + + uint32_t queried_entries = 0; + struct timespec time_now, time_end, time_spent; + + if (clock_gettime (CLOCK_MONOTONIC, &time_now) < 0) + { + return; + } + + /* Remove the NAT entries that are aged out. + * Query the NAT entries for their activity in the hardware + * and update the aging timeout. */ + NatEntry::iterator natIter = m_natEntries.begin(); + while (natIter != m_natEntries.end()) + { + if (checkIfNatEntryIsActive(natIter, time_now.tv_sec)) + { + /* Since the entry is active in the hardware, reset + * the expiry time for the conntrack entry in the kernel. */ + updateConnTrackTimeout(natIter->first); + } + queried_entries++; + natIter++; + } + + /* Remove the NAPT entries that are aged out. + * Query the NAPT entries for their activity in the hardware + * and update the aging timeout. */ + NaptEntry::iterator naptIter = m_naptEntries.begin(); + while (naptIter != m_naptEntries.end()) + { + if (checkIfNaptEntryIsActive(naptIter, time_now.tv_sec)) + { + /* Since the entry is active in the hardware, reset + * the expiry time for the conntrack entry in the kernel. */ + updateConnTrackTimeout(naptIter->first); + } + queried_entries++; + naptIter++; + } + + /* Remove the Twice NAT entries that are aged out. + * Query the Twice NAT entries for their activity in the hardware + * and update the aging timeout. */ + TwiceNatEntry::iterator twiceNatIter = m_twiceNatEntries.begin(); + while (twiceNatIter != m_twiceNatEntries.end()) + { + if (checkIfTwiceNatEntryIsActive(twiceNatIter, time_now.tv_sec)) + { + /* Since the entry is active in the hardware, reset + * the expiry time for the conntrack entry in the kernel. */ + updateConnTrackTimeout(twiceNatIter->first); + } + queried_entries++; + twiceNatIter++; + } + + /* Remove the Twice NAPT entries that are aged out. + * Query the Twice NAPT entries for their activity in the hardware + * and update the aging timeout. */ + TwiceNaptEntry::iterator twiceNaptIter = m_twiceNaptEntries.begin(); + while (twiceNaptIter != m_twiceNaptEntries.end()) + { + if (checkIfTwiceNaptEntryIsActive(twiceNaptIter, time_now.tv_sec)) + { + /* Since the entry is active in the hardware, reset + * the expiry time for the conntrack entry in the kernel. */ + updateConnTrackTimeout(twiceNaptIter->first); + } + queried_entries++; + twiceNaptIter++; + } + if (clock_gettime (CLOCK_MONOTONIC, &time_end) < 0) + { + return; + } + time_spent = getTimeDiff(time_now, time_end); + + if (queried_entries) + { + SWSS_LOG_DEBUG("Time spent in querying hardware hit-bits for %u NAT/NAPT entries = %lu secs, %lu msecs", + queried_entries, time_spent.tv_sec, (time_spent.tv_nsec / 1000000UL)); + } +} + +bool NatOrch::getNatCounters(const NatEntry::iterator &iter) +{ + const IpAddress &ipAddr = iter->first; + NatEntryValue &entry = iter->second; + uint32_t attr_count; + sai_attribute_t nat_entry_attr[4]; + sai_nat_entry_t nat_entry; + sai_status_t status; + uint64_t nat_translations_pkts = 0, nat_translations_bytes = 0; + + if (entry.addedToHw == false) + { + SWSS_LOG_DEBUG("Skip get Counters for %s NAT entry [ip %s], as not yet added to HW", entry.nat_type.c_str(), ipAddr.to_string().c_str()); + return 0; + } + + memset(nat_entry_attr, 0, sizeof(nat_entry_attr)); + nat_entry_attr[0].id = SAI_NAT_ENTRY_ATTR_BYTE_COUNT; + nat_entry_attr[1].id = SAI_NAT_ENTRY_ATTR_PACKET_COUNT; + + attr_count = 2; + + memset(&nat_entry, 0, sizeof(nat_entry)); + + nat_entry.vr_id = gVirtualRouterId; + nat_entry.switch_id = gSwitchId; + + if (entry.nat_type == "dnat") + { + nat_entry.data.key.dst_ip = ipAddr.getV4Addr(); + nat_entry.data.mask.dst_ip = 0xffffffff; + } + else + { + nat_entry.data.key.src_ip = ipAddr.getV4Addr(); + nat_entry.data.mask.src_ip = 0xffffffff; + } + + status = sai_nat_api->get_nat_entry_attribute(&nat_entry, attr_count, nat_entry_attr); + if (entry.nat_type == "snat") + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get Counters for SNAT entry [src-ip %s], bytes = %lu, pkts = %lu", ipAddr.to_string().c_str(), + nat_entry_attr[0].value.u64, nat_entry_attr[1].value.u64); + } + else + { + nat_translations_bytes = nat_entry_attr[0].value.u64; + nat_translations_pkts = nat_entry_attr[1].value.u64; + } + } + else if (entry.nat_type == "dnat") + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get Counters for DNAT entry [dst-ip %s], bytes = %lu, pkts = %lu", ipAddr.to_string().c_str(), + nat_entry_attr[0].value.u64, nat_entry_attr[1].value.u64); + } + else + { + nat_translations_bytes = nat_entry_attr[0].value.u64; + nat_translations_pkts = nat_entry_attr[1].value.u64; + } + } + + /* Update the Counter values in the database */ + updateNatCounters(ipAddr, nat_translations_pkts, nat_translations_bytes); + + return 0; +} + +bool NatOrch::getTwiceNatCounters(const TwiceNatEntry::iterator &iter) +{ + const TwiceNatEntryKey &key = iter->first; + TwiceNatEntryValue &entry = iter->second; + uint32_t attr_count; + sai_attribute_t nat_entry_attr[4]; + sai_nat_entry_t dbl_nat_entry; + sai_status_t status; + uint64_t nat_translations_pkts = 0, nat_translations_bytes = 0; + + if (entry.addedToHw == false) + { + SWSS_LOG_DEBUG("Skip get Counters for Twice NAT entry [src ip %s, dst ip %s], as not yet added to HW", + key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str()); + return 0; + } + + memset(nat_entry_attr, 0, sizeof(nat_entry_attr)); + nat_entry_attr[0].id = SAI_NAT_ENTRY_ATTR_BYTE_COUNT; + nat_entry_attr[1].id = SAI_NAT_ENTRY_ATTR_PACKET_COUNT; + + attr_count = 2; + + memset(&dbl_nat_entry, 0, sizeof(dbl_nat_entry)); + + dbl_nat_entry.vr_id = gVirtualRouterId; + dbl_nat_entry.switch_id = gSwitchId; + dbl_nat_entry.data.key.src_ip = key.src_ip.getV4Addr(); + dbl_nat_entry.data.mask.src_ip = 0xffffffff; + dbl_nat_entry.data.key.dst_ip = key.dst_ip.getV4Addr(); + dbl_nat_entry.data.mask.dst_ip = 0xffffffff; + + status = sai_nat_api->get_nat_entry_attribute(&dbl_nat_entry, attr_count, nat_entry_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get Counters for Twice NAT entry [src-ip %s, dst-ip %s], bytes = %lu, pkts = %lu", + key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str(), + nat_entry_attr[0].value.u64, nat_entry_attr[1].value.u64); + } + else + { + nat_translations_bytes = nat_entry_attr[0].value.u64; + nat_translations_pkts = nat_entry_attr[1].value.u64; + } + + /* Update the Counter values in the database */ + updateTwiceNatCounters(key, nat_translations_pkts, nat_translations_bytes); + + return 0; +} + +bool NatOrch::setNatCounters(const NatEntry::iterator &iter) +{ + const IpAddress &ipAddr = iter->first; + NatEntryValue &entry = iter->second; + sai_attribute_t nat_entry_attr_packet; + sai_attribute_t nat_entry_attr_byte; + sai_nat_entry_t nat_entry; + sai_status_t status; + uint64_t nat_translations_pkts = 0, nat_translations_bytes = 0; + + if (entry.addedToHw == false) + { + SWSS_LOG_DEBUG("Skip set Counters for %s NAT entry [ip %s], as not yet added to HW", entry.nat_type.c_str(), ipAddr.to_string().c_str()); + return 0; + } + + memset(&nat_entry_attr_packet, 0, sizeof(nat_entry_attr_packet)); + memset(&nat_entry_attr_byte, 0, sizeof(nat_entry_attr_byte)); + nat_entry_attr_byte.id = SAI_NAT_ENTRY_ATTR_BYTE_COUNT; + nat_entry_attr_packet.id = SAI_NAT_ENTRY_ATTR_PACKET_COUNT; + + memset(&nat_entry, 0, sizeof(nat_entry)); + + nat_entry.vr_id = gVirtualRouterId; + nat_entry.switch_id = gSwitchId; + + if (entry.nat_type == "dnat") + { + nat_entry.data.key.dst_ip = ipAddr.getV4Addr(); + nat_entry.data.mask.dst_ip = 0xffffffff; + } + else + { + nat_entry.data.key.src_ip = ipAddr.getV4Addr(); + nat_entry.data.mask.src_ip = 0xffffffff; + } + + status = sai_nat_api->set_nat_entry_attribute(&nat_entry, &nat_entry_attr_packet); + + if (entry.nat_type == "snat") + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to clear packet counter for SNAT entry [src-ip %s]", ipAddr.to_string().c_str()); + } + } + else if (entry.nat_type == "dnat") + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to clear packet counter for DNAT entry [dst-ip %s]", ipAddr.to_string().c_str()); + } + } + + status = sai_nat_api->set_nat_entry_attribute(&nat_entry, &nat_entry_attr_byte); + + if (entry.nat_type == "snat") + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to clear byte counter for SNAT entry [src-ip %s]", ipAddr.to_string().c_str()); + } + } + else if (entry.nat_type == "dnat") + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to clear byte counter for DNAT entry [dst-ip %s]", ipAddr.to_string().c_str()); + } + } + /* Update the Counter values in the database */ + updateNatCounters(ipAddr, nat_translations_pkts, nat_translations_bytes); + + return 0; +} + +bool NatOrch::getNaptCounters(const NaptEntry::iterator &iter) +{ + const NaptEntryKey &naptKey = iter->first; + NaptEntryValue &entry = iter->second; + uint8_t protoType = ((naptKey.prototype == "TCP") ? IPPROTO_TCP : IPPROTO_UDP); + uint32_t attr_count; + sai_attribute_t nat_entry_attr[4]; + sai_nat_entry_t nat_entry; + sai_status_t status; + uint64_t nat_translations_pkts = 0, nat_translations_bytes = 0; + + if (entry.addedToHw == false) + { + SWSS_LOG_DEBUG("Skip get Counters for %s NAPT entry for [proto %s, ip %s, port %d], as not yet added to HW", + entry.nat_type.c_str(), naptKey.prototype.c_str(), naptKey.ip_address.to_string().c_str(), naptKey.l4_port); + return 0; + } + + memset(nat_entry_attr, 0, sizeof(nat_entry_attr)); + nat_entry_attr[0].id = SAI_NAT_ENTRY_ATTR_BYTE_COUNT; + nat_entry_attr[1].id = SAI_NAT_ENTRY_ATTR_PACKET_COUNT; + + attr_count = 2; + + memset(&nat_entry, 0, sizeof(nat_entry)); + + nat_entry.vr_id = gVirtualRouterId; + nat_entry.switch_id = gSwitchId; + + if (entry.nat_type == "dnat") + { + nat_entry.data.key.dst_ip = naptKey.ip_address.getV4Addr(); + nat_entry.data.key.l4_dst_port = (uint16_t)(naptKey.l4_port); + nat_entry.data.mask.dst_ip = 0xffffffff; + nat_entry.data.mask.l4_dst_port = 0xffff; + } + else if (entry.nat_type == "snat") + { + nat_entry.data.key.src_ip = naptKey.ip_address.getV4Addr(); + nat_entry.data.key.l4_src_port = (uint16_t)(naptKey.l4_port); + nat_entry.data.mask.src_ip = 0xffffffff; + nat_entry.data.mask.l4_src_port = 0xffff; + } + + nat_entry.data.key.proto = protoType; + nat_entry.data.mask.proto = 0xff; + + status = sai_nat_api->get_nat_entry_attribute(&nat_entry, attr_count, nat_entry_attr); + + if (entry.nat_type == "snat") + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get Counters for SNAPT entry for [proto %s, src-ip %s, src-port %d], bytes = %lu, pkts = %lu", + naptKey.prototype.c_str(), naptKey.ip_address.to_string().c_str(), naptKey.l4_port, + nat_entry_attr[0].value.u64, nat_entry_attr[1].value.u64); + } + else + { + nat_translations_bytes = nat_entry_attr[0].value.u64; + nat_translations_pkts = nat_entry_attr[1].value.u64; + } + } + else if (entry.nat_type == "dnat") + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get Counters for DNAPT entry for [proto %s, dst-ip %s, dst-port %d], bytes = %lu, pkts = %lu", + naptKey.prototype.c_str(), naptKey.ip_address.to_string().c_str(), naptKey.l4_port, + nat_entry_attr[0].value.u64, nat_entry_attr[1].value.u64); + } + else + { + nat_translations_bytes = nat_entry_attr[0].value.u64; + nat_translations_pkts = nat_entry_attr[1].value.u64; + } + } + + /* Update the Counter values in the database */ + updateNaptCounters(naptKey.prototype, naptKey.ip_address, naptKey.l4_port, + nat_translations_pkts, nat_translations_bytes); + return 0; +} + +bool NatOrch::getTwiceNaptCounters(const TwiceNaptEntry::iterator &iter) +{ + const TwiceNaptEntryKey &key = iter->first; + TwiceNaptEntryValue &entry = iter->second; + uint8_t protoType = ((key.prototype == "TCP") ? IPPROTO_TCP : IPPROTO_UDP); + uint32_t attr_count; + sai_attribute_t nat_entry_attr[4]; + sai_nat_entry_t dbl_nat_entry; + sai_status_t status; + uint64_t nat_translations_pkts = 0, nat_translations_bytes = 0; + + if (entry.addedToHw == false) + { + SWSS_LOG_DEBUG("Skip get Counters for Twice NAPT entry for [proto %s, src ip %s, src port %d, dst ip %s, dst port %d], as not yet added to HW", + key.prototype.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), + key.dst_l4_port); + return 0; + } + + memset(nat_entry_attr, 0, sizeof(nat_entry_attr)); + nat_entry_attr[0].id = SAI_NAT_ENTRY_ATTR_BYTE_COUNT; + nat_entry_attr[1].id = SAI_NAT_ENTRY_ATTR_PACKET_COUNT; + + attr_count = 2; + + memset(&dbl_nat_entry, 0, sizeof(dbl_nat_entry)); + + dbl_nat_entry.vr_id = gVirtualRouterId; + dbl_nat_entry.switch_id = gSwitchId; + dbl_nat_entry.data.key.src_ip = key.src_ip.getV4Addr(); + dbl_nat_entry.data.mask.src_ip = 0xffffffff; + dbl_nat_entry.data.key.l4_src_port = (uint16_t)(key.src_l4_port); + dbl_nat_entry.data.mask.l4_src_port = 0xffff; + dbl_nat_entry.data.key.dst_ip = key.dst_ip.getV4Addr(); + dbl_nat_entry.data.mask.dst_ip = 0xffffffff; + dbl_nat_entry.data.key.l4_dst_port = (uint16_t)(key.dst_l4_port); + dbl_nat_entry.data.mask.l4_dst_port = 0xffff; + dbl_nat_entry.data.key.proto = protoType; + dbl_nat_entry.data.mask.proto = 0xff; + + status = sai_nat_api->get_nat_entry_attribute(&dbl_nat_entry, attr_count, nat_entry_attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_DEBUG("Failed to get Counters for Twice NAPT entry for [proto %s, src ip %s, src port %d, dst ip %s, dst port %d], as not yet added to HW", + key.prototype.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), + key.dst_l4_port); + } + else + { + nat_translations_bytes = nat_entry_attr[0].value.u64; + nat_translations_pkts = nat_entry_attr[1].value.u64; + } + + /* Update the Counter values in the database */ + updateTwiceNaptCounters(key, nat_translations_pkts, nat_translations_bytes); + return 0; +} + +bool NatOrch::setNaptCounters(const NaptEntry::iterator &iter) +{ + const NaptEntryKey &naptKey = iter->first; + NaptEntryValue &entry = iter->second; + uint8_t protoType = ((naptKey.prototype == "TCP") ? IPPROTO_TCP : IPPROTO_UDP); + sai_attribute_t nat_entry_attr_packet; + sai_attribute_t nat_entry_attr_byte; + sai_nat_entry_t nat_entry; + sai_status_t status; + uint64_t nat_translations_pkts = 0, nat_translations_bytes = 0; + + if (entry.addedToHw == false) + { + SWSS_LOG_DEBUG("Skip set Counters for %s NAPT entry for [proto %s, ip %s, port %d], as not yet added to HW", + entry.nat_type.c_str(), naptKey.prototype.c_str(), naptKey.ip_address.to_string().c_str(), naptKey.l4_port); + return 0; + } + + memset(&nat_entry_attr_packet, 0, sizeof(nat_entry_attr_packet)); + memset(&nat_entry_attr_byte, 0, sizeof(nat_entry_attr_byte)); + nat_entry_attr_packet.id = SAI_NAT_ENTRY_ATTR_PACKET_COUNT; + nat_entry_attr_byte.id = SAI_NAT_ENTRY_ATTR_BYTE_COUNT; + + memset(&nat_entry, 0, sizeof(nat_entry)); + + nat_entry.vr_id = gVirtualRouterId; + nat_entry.switch_id = gSwitchId; + + if (entry.nat_type == "dnat") + { + nat_entry.data.key.dst_ip = naptKey.ip_address.getV4Addr(); + nat_entry.data.key.l4_dst_port = (uint16_t)(naptKey.l4_port); + nat_entry.data.mask.dst_ip = 0xffffffff; + nat_entry.data.mask.l4_dst_port = 0xffff; + } + else if (entry.nat_type == "snat") + { + nat_entry.data.key.src_ip = naptKey.ip_address.getV4Addr(); + nat_entry.data.key.l4_src_port = (uint16_t)(naptKey.l4_port); + nat_entry.data.mask.src_ip = 0xffffffff; + nat_entry.data.mask.l4_src_port = 0xffff; + } + + nat_entry.data.key.proto = protoType; + nat_entry.data.mask.proto = 0xff; + + status = sai_nat_api->set_nat_entry_attribute(&nat_entry, &nat_entry_attr_packet); + + if (entry.nat_type == "snat") + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to clear packet counter for SNAPT entry for [proto %s, src-ip %s, src-port %d", + naptKey.prototype.c_str(), naptKey.ip_address.to_string().c_str(), naptKey.l4_port); + } + } + else if (entry.nat_type == "dnat") + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to clear packet counter for DNAPT entry for [proto %s, dst-ip %s, dst-port %d]", + naptKey.prototype.c_str(), naptKey.ip_address.to_string().c_str(), naptKey.l4_port); + } + } + + status = sai_nat_api->set_nat_entry_attribute(&nat_entry, &nat_entry_attr_byte); + + if (entry.nat_type == "snat") + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to clear byte counter for SNAPT entry for [proto %s, src-ip %s, src-port %d", + naptKey.prototype.c_str(), naptKey.ip_address.to_string().c_str(), naptKey.l4_port); + } + } + else if (entry.nat_type == "dnat") + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to clear byte counter for DNAPT entry for [proto %s, dst-ip %s, dst-port %d]", + naptKey.prototype.c_str(), naptKey.ip_address.to_string().c_str(), naptKey.l4_port); + } + } + + /* Update the Counter values in the database */ + updateNaptCounters(naptKey.prototype, naptKey.ip_address, naptKey.l4_port, + nat_translations_pkts, nat_translations_bytes); + return 0; +} + +bool NatOrch::setTwiceNatCounters(const TwiceNatEntry::iterator &iter) +{ + const TwiceNatEntryKey &key = iter->first; + TwiceNatEntryValue &entry = iter->second; + sai_attribute_t nat_entry_attr_packet; + sai_attribute_t nat_entry_attr_byte; + sai_nat_entry_t dbl_nat_entry; + sai_status_t status; + uint64_t nat_translations_pkts = 0, nat_translations_bytes = 0; + + if (entry.addedToHw == false) + { + SWSS_LOG_DEBUG("Skip set Counters for Twice NAT entry [src ip %s, dst ip %s], as not yet added to HW", + key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str()); + return 0; + } + + memset(&nat_entry_attr_packet, 0, sizeof(nat_entry_attr_packet)); + memset(&nat_entry_attr_byte, 0, sizeof(nat_entry_attr_byte)); + nat_entry_attr_packet.id = SAI_NAT_ENTRY_ATTR_PACKET_COUNT; + nat_entry_attr_byte.id = SAI_NAT_ENTRY_ATTR_BYTE_COUNT; + + memset(&dbl_nat_entry, 0, sizeof(dbl_nat_entry)); + + dbl_nat_entry.vr_id = gVirtualRouterId; + dbl_nat_entry.switch_id = gSwitchId; + dbl_nat_entry.data.key.src_ip = key.src_ip.getV4Addr(); + dbl_nat_entry.data.mask.src_ip = 0xffffffff; + dbl_nat_entry.data.key.dst_ip = key.dst_ip.getV4Addr(); + dbl_nat_entry.data.mask.dst_ip = 0xffffffff; + + status = sai_nat_api->set_nat_entry_attribute(&dbl_nat_entry, &nat_entry_attr_packet); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to clear packet counters for Twice NAT entry [src-ip %s, dst-ip %s]", + key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str()); + } + + status = sai_nat_api->set_nat_entry_attribute(&dbl_nat_entry, &nat_entry_attr_byte); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to clear byte counters for Twice NAT entry [src-ip %s, dst-ip %s]", + key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str()); + } + + /* Update the Counter values in the database */ + updateTwiceNatCounters(key, nat_translations_pkts, nat_translations_bytes); + + return 0; +} + +bool NatOrch::setTwiceNaptCounters(const TwiceNaptEntry::iterator &iter) +{ + const TwiceNaptEntryKey &key = iter->first; + TwiceNaptEntryValue &entry = iter->second; + uint8_t protoType = ((key.prototype == "TCP") ? IPPROTO_TCP : IPPROTO_UDP); + sai_attribute_t nat_entry_attr_packet; + sai_attribute_t nat_entry_attr_byte; + sai_nat_entry_t dbl_nat_entry; + sai_status_t status; + uint64_t nat_translations_pkts = 0, nat_translations_bytes = 0; + + if (entry.addedToHw == false) + { + SWSS_LOG_DEBUG("Skip set Counters for Twice NAPT entry [src ip %s, src port %d, dst ip %s, dst port %d], as not yet added to HW", + key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), key.dst_l4_port); + return 0; + } + + memset(&nat_entry_attr_packet, 0, sizeof(nat_entry_attr_packet)); + memset(&nat_entry_attr_byte, 0, sizeof(nat_entry_attr_byte)); + nat_entry_attr_packet.id = SAI_NAT_ENTRY_ATTR_PACKET_COUNT; + nat_entry_attr_byte.id = SAI_NAT_ENTRY_ATTR_BYTE_COUNT; + + memset(&dbl_nat_entry, 0, sizeof(dbl_nat_entry)); + + dbl_nat_entry.vr_id = gVirtualRouterId; + dbl_nat_entry.switch_id = gSwitchId; + dbl_nat_entry.data.key.src_ip = key.src_ip.getV4Addr(); + dbl_nat_entry.data.mask.src_ip = 0xffffffff; + dbl_nat_entry.data.key.l4_src_port = (uint16_t)(key.src_l4_port); + dbl_nat_entry.data.mask.l4_src_port = 0xffff; + dbl_nat_entry.data.key.dst_ip = key.dst_ip.getV4Addr(); + dbl_nat_entry.data.mask.dst_ip = 0xffffffff; + dbl_nat_entry.data.key.l4_dst_port = (uint16_t)(key.dst_l4_port); + dbl_nat_entry.data.mask.l4_dst_port = 0xffff; + dbl_nat_entry.data.key.proto = protoType; + dbl_nat_entry.data.mask.proto = 0xff; + + status = sai_nat_api->set_nat_entry_attribute(&dbl_nat_entry, &nat_entry_attr_packet); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to clear packet counters for Twice NAPT entry [src-ip %s, src port %d, dst-ip %s, dst port %d]", + key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), key.dst_l4_port); + } + + status = sai_nat_api->set_nat_entry_attribute(&dbl_nat_entry, &nat_entry_attr_byte); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to clear byte counters for Twice NAPT entry [src-ip %s, src port %d, dst-ip %s, dst port %d]", + key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), key.dst_l4_port); + } + + /* Update the Counter values in the database */ + updateTwiceNaptCounters(key, nat_translations_pkts, nat_translations_bytes); + + return 0; +} + +void NatOrch::deleteConnTrackEntry(const IpAddress &ipAddr) +{ + std::string res; + std::string cmds = std::string("") + CONNTRACK + DELETE; + + cmds += (" -s " + ipAddr.to_string()); + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Deleted the NAT conntrack entry"); + } +} + +void NatOrch::deleteConnTrackEntry(const NaptEntryKey &key) +{ + std::string res; + std::string prototype = ((key.prototype == string("TCP")) ? "tcp" : "udp"); + std::string cmds = std::string("") + CONNTRACK + DELETE; + + cmds += (" -s " + key.ip_address.to_string() + " -p " + prototype + " --orig-port-src " + to_string(key.l4_port)); + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Deleted the NAPT conntrack entry"); + } +} + +void NatOrch::deleteConnTrackEntry(const TwiceNatEntryKey &key) +{ + std::string res; + std::string cmds = std::string("") + CONNTRACK + DELETE; + + cmds += (" -s " + key.src_ip.to_string()); + cmds += (" -d " + key.dst_ip.to_string()); + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Deleted the Twice NAT conntrack entry"); + } +} + +void NatOrch::deleteConnTrackEntry(const TwiceNaptEntryKey &key) +{ + std::string res; + std::string prototype = ((key.prototype == string("TCP")) ? "tcp" : "udp"); + std::string cmds = std::string("") + CONNTRACK + DELETE; + + cmds += (" -s " + key.src_ip.to_string() + " -p " + prototype + " --orig-port-src " + to_string(key.src_l4_port)); + cmds += (" -d " + key.dst_ip.to_string() + " --orig-port-dst " + to_string(key.dst_l4_port)); + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Deleted the Twice NAPT conntrack entry"); + } +} + +void NatOrch::updateNatCounters(const IpAddress &ipAddr, + uint64_t nat_translations_pkts, uint64_t nat_translations_bytes) +{ + vector values; + string key = ipAddr.to_string().c_str(); + + swss::FieldValueTuple p("NAT_TRANSLATIONS_PKTS", std::to_string(nat_translations_pkts)); + values.push_back(p); + swss::FieldValueTuple q("NAT_TRANSLATIONS_BYTES", std::to_string(nat_translations_bytes)); + values.push_back(q); + + m_countersNatTable.set(key, values); +} + +void NatOrch::deleteNatCounters(const IpAddress &ipAddr) +{ + string key = ipAddr.to_string(); + + m_countersNatTable.del(key); +} + +void NatOrch::deleteTwiceNatCounters(const TwiceNatEntryKey &key) +{ + string natKey = key.src_ip.to_string() + ":" + key.dst_ip.to_string(); + + m_countersTwiceNatTable.del(natKey); +} + +void NatOrch::updateNaptCounters(const string &protocol, const IpAddress &ipAddr, int l4_port, + uint64_t nat_translations_pkts, uint64_t nat_translations_bytes) +{ + vector values; + string protoStr = protocol.c_str(), ipStr = ipAddr.to_string().c_str(), portStr = std::to_string(l4_port); + string key = (protoStr + ":" + ipStr + ":" + portStr); + + swss::FieldValueTuple p("NAT_TRANSLATIONS_PKTS", to_string(nat_translations_pkts)); + values.push_back(p); + swss::FieldValueTuple q("NAT_TRANSLATIONS_BYTES", to_string(nat_translations_bytes)); + values.push_back(q); + + m_countersNaptTable.set(key, values); +} + +void NatOrch::deleteNaptCounters(const string &protocol, const IpAddress &ipAddr, int l4_port) +{ + string protoStr = protocol.c_str(), ipStr = ipAddr.to_string().c_str(), portStr = std::to_string(l4_port); + string key = (protoStr + ":" + ipStr + ":" + portStr); + + m_countersNaptTable.del(key); +} + +void NatOrch::deleteTwiceNaptCounters(const TwiceNaptEntryKey &key) +{ + string naptKey = (key.prototype + ":" + key.src_ip.to_string() + ":" + std::to_string(key.src_l4_port) + + ":" + key.dst_ip.to_string() + ":" + std::to_string(key.dst_l4_port)); + + m_countersTwiceNaptTable.del(naptKey); +} + +void NatOrch::updateTwiceNatCounters(const TwiceNatEntryKey &key, + uint64_t nat_translations_pkts, uint64_t nat_translations_bytes) +{ + vector values; + string natKey = key.src_ip.to_string() + ":" + key.dst_ip.to_string(); + + swss::FieldValueTuple p("NAT_TRANSLATIONS_PKTS", to_string(nat_translations_pkts)); + values.push_back(p); + swss::FieldValueTuple q("NAT_TRANSLATIONS_BYTES", to_string(nat_translations_bytes)); + values.push_back(q); + + m_countersTwiceNatTable.set(natKey, values); +} + +void NatOrch::updateTwiceNaptCounters(const TwiceNaptEntryKey &key, + uint64_t nat_translations_pkts, uint64_t nat_translations_bytes) +{ + vector values; + string naptKey = (key.prototype + ":" + key.src_ip.to_string() + ":" + std::to_string(key.src_l4_port) + + ":" + key.dst_ip.to_string() + ":" + std::to_string(key.dst_l4_port)); + + swss::FieldValueTuple p("NAT_TRANSLATIONS_PKTS", to_string(nat_translations_pkts)); + values.push_back(p); + swss::FieldValueTuple q("NAT_TRANSLATIONS_BYTES", to_string(nat_translations_bytes)); + values.push_back(q); + + m_countersTwiceNaptTable.set(naptKey, values); +} + +bool NatOrch::checkIfNatEntryIsActive(const NatEntry::iterator &iter, time_t now) +{ + const IpAddress &ipAddr = iter->first; + NatEntryValue &entry = iter->second; + uint32_t attr_count; + IpAddress srcIp; + sai_attribute_t nat_entry_attr[4]; + sai_nat_entry_t snat_entry, dnat_entry; + sai_status_t status; + + if (entry.nat_type == "dnat") + { + /* Hitbits are queried for both directions when SNAT entry is checked */ + return 0; + } + + if (entry.addedToHw == false) + { + SWSS_LOG_DEBUG("Skip get hitbits for %s NAT entry [ip %s], as not yet added to HW", entry.nat_type.c_str(), ipAddr.to_string().c_str()); + return 0; + } + + if (entry.entry_type == "static") + { + /* Static NAT entries are always treated active */ + return 1; + } + + memset(nat_entry_attr, 0, sizeof(nat_entry_attr)); + nat_entry_attr[0].id = SAI_NAT_ENTRY_ATTR_HIT_BIT; /* Get the Hit bit */ + nat_entry_attr[0].value.booldata = 0; + nat_entry_attr[1].id = SAI_NAT_ENTRY_ATTR_HIT_BIT_COR; /* clear the hit bit after returning the value */ + nat_entry_attr[1].value.booldata = 1; + + attr_count = 2; + + memset(&snat_entry, 0, sizeof(snat_entry)); + + snat_entry.vr_id = gVirtualRouterId; + snat_entry.switch_id = gSwitchId; + srcIp = ipAddr; + snat_entry.data.key.src_ip = srcIp.getV4Addr(); + snat_entry.data.mask.src_ip = 0xffffffff; + + status = sai_nat_api->get_nat_entry_attribute(&snat_entry, attr_count, nat_entry_attr); + if (status == SAI_STATUS_SUCCESS) + { + SWSS_LOG_DEBUG("SNAT HIT BIT for src-ip %s = %d", srcIp.to_string().c_str(), + nat_entry_attr[0].value.booldata); + + if (nat_entry_attr[0].value.booldata) + { + entry.ageOutTime = now + timeout; + return 1; + } + else + { + auto dnatIter = m_natEntries.find(entry.translated_ip); + if (dnatIter == m_natEntries.end()) + { + return 0; + } + if ((dnatIter->second).addedToHw == false) + { + return 0; + } + + /* If SNAT HitBit is not set, check for the HitBit in the reverse direction */ + memset(&dnat_entry, 0, sizeof(dnat_entry)); + + dnat_entry.vr_id = gVirtualRouterId; + dnat_entry.switch_id = gSwitchId; + dnat_entry.data.key.dst_ip = entry.translated_ip.getV4Addr(); + dnat_entry.data.mask.dst_ip = 0xffffffff; + + status = sai_nat_api->get_nat_entry_attribute(&dnat_entry, attr_count, nat_entry_attr); + if (status == SAI_STATUS_SUCCESS) + { + SWSS_LOG_DEBUG("DNAT HIT BIT for dst-ip %s = %d", entry.translated_ip.to_string().c_str(), + nat_entry_attr[0].value.booldata); + if (nat_entry_attr[0].value.booldata) + { + entry.ageOutTime = now + timeout; + return 1; + } + } + } + } + return 0; +} + +bool NatOrch::checkIfNaptEntryIsActive(const NaptEntry::iterator &iter, time_t now) +{ + const NaptEntryKey &naptKey = iter->first; + NaptEntryValue &entry = iter->second; + int protoType = ((naptKey.prototype == "TCP") ? IPPROTO_TCP : IPPROTO_UDP); + uint32_t attr_count; + IpAddress srcIp; + uint16_t srcPort; + sai_attribute_t nat_entry_attr[4]; + sai_nat_entry_t snat_entry, dnat_entry; + sai_status_t status; + + if (entry.nat_type == "dnat") + { + /* Hitbits are queried for both directions when SNAT entry is checked */ + return 0; + } + + if (entry.addedToHw == false) + { + SWSS_LOG_DEBUG("Skip get hitbits for %s NAPT entry for [proto %s, ip %s, port %d], as not yet added to HW", + entry.nat_type.c_str(), naptKey.prototype.c_str(), naptKey.ip_address.to_string().c_str(), naptKey.l4_port); + return 0; + } + + if (entry.entry_type == "static") + { + /* Static NAPT entries are always treated active */ + return 1; + } + + memset(nat_entry_attr, 0, sizeof(nat_entry_attr)); + nat_entry_attr[0].id = SAI_NAT_ENTRY_ATTR_HIT_BIT; /* Get the Hit bit */ + nat_entry_attr[0].value.booldata = 0; + nat_entry_attr[1].id = SAI_NAT_ENTRY_ATTR_HIT_BIT_COR; /* clear the hit bit after returning the value */ + nat_entry_attr[1].value.booldata = 1; + + attr_count = 2; + + memset(&snat_entry, 0, sizeof(snat_entry)); + + snat_entry.vr_id = gVirtualRouterId; + snat_entry.switch_id = gSwitchId; + + srcIp = naptKey.ip_address; + srcPort = (uint16_t)(naptKey.l4_port); + snat_entry.data.key.src_ip = srcIp.getV4Addr(); + snat_entry.data.key.l4_src_port = srcPort; + + snat_entry.data.mask.src_ip = 0xffffffff; + snat_entry.data.mask.l4_src_port = 0xffff; + snat_entry.data.key.proto = (uint8_t)protoType; + snat_entry.data.mask.proto = 0xff; + + status = sai_nat_api->get_nat_entry_attribute(&snat_entry, attr_count, nat_entry_attr); + if (status == SAI_STATUS_SUCCESS) + { + SWSS_LOG_DEBUG("SNAPT HIT BIT for proto %s, src-ip %s, src-port %d = %d", naptKey.prototype.c_str(), + srcIp.to_string().c_str(), srcPort, nat_entry_attr[0].value.booldata); + if (nat_entry_attr[0].value.booldata) + { + entry.ageOutTime = now + ((protoType == IPPROTO_TCP) ? tcp_timeout : udp_timeout); + return 1; + } + else + { + NaptEntryKey dnaptKey; + dnaptKey.ip_address = entry.translated_ip; + dnaptKey.l4_port = entry.translated_l4_port; + dnaptKey.prototype = naptKey.prototype; + + auto dnaptIter = m_naptEntries.find(dnaptKey); + if (dnaptIter == m_naptEntries.end()) + { + return 0; + } + if ((dnaptIter->second).addedToHw == false) + { + return 0; + } + + + /* If SNAPT HitBit is not set, check for the HitBit in the reverse direction */ + memset(&dnat_entry, 0, sizeof(dnat_entry)); + + dnat_entry.vr_id = gVirtualRouterId; + dnat_entry.switch_id = gSwitchId; + + dnat_entry.data.key.dst_ip = entry.translated_ip.getV4Addr(); + dnat_entry.data.key.l4_dst_port = (uint16_t)(entry.translated_l4_port); + + dnat_entry.data.mask.dst_ip = 0xffffffff; + dnat_entry.data.mask.l4_dst_port = 0xffff; + dnat_entry.data.key.proto = (uint8_t)protoType; + dnat_entry.data.mask.proto = 0xff; + + status = sai_nat_api->get_nat_entry_attribute(&dnat_entry, attr_count, nat_entry_attr); + if (status == SAI_STATUS_SUCCESS) + { + SWSS_LOG_DEBUG("DNAPT HIT BIT for proto %s, dst-ip %s, dst-port %d = %d", naptKey.prototype.c_str(), + entry.translated_ip.to_string().c_str(), entry.translated_l4_port, nat_entry_attr[0].value.booldata); + if (nat_entry_attr[0].value.booldata) + { + entry.ageOutTime = now + ((protoType == IPPROTO_TCP) ? tcp_timeout : udp_timeout); + return 1; + } + } + } + } + return 0; +} + +bool NatOrch::checkIfTwiceNatEntryIsActive(const TwiceNatEntry::iterator &iter, time_t now) +{ + const TwiceNatEntryKey &key = iter->first; + TwiceNatEntryValue &entry = iter->second; + uint32_t attr_count; + sai_attribute_t nat_entry_attr[4]; + sai_nat_entry_t dbl_nat_entry; + sai_status_t status; + + if (entry.entry_type == "static") + { + /* Static NAPT entries are always treated active */ + return 1; + } + + if (entry.addedToHw == false) + { + SWSS_LOG_DEBUG("Skip get hitbits for Twice NAPT entry for [src ip %s, dst-ip %s], as not yet added to HW", + key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str()); + return 0; + } + + memset(nat_entry_attr, 0, sizeof(nat_entry_attr)); + nat_entry_attr[0].id = SAI_NAT_ENTRY_ATTR_HIT_BIT; /* Get the Hit bit */ + nat_entry_attr[0].value.booldata = 0; + nat_entry_attr[1].id = SAI_NAT_ENTRY_ATTR_HIT_BIT_COR; /* clear the hit bit after returning the value */ + nat_entry_attr[1].value.booldata = 1; + + attr_count = 2; + + memset(&dbl_nat_entry, 0, sizeof(dbl_nat_entry)); + + dbl_nat_entry.vr_id = gVirtualRouterId; + dbl_nat_entry.switch_id = gSwitchId; + dbl_nat_entry.data.key.src_ip = key.src_ip.getV4Addr(); + dbl_nat_entry.data.mask.src_ip = 0xffffffff; + dbl_nat_entry.data.key.dst_ip = key.dst_ip.getV4Addr(); + dbl_nat_entry.data.mask.dst_ip = 0xffffffff; + + status = sai_nat_api->get_nat_entry_attribute(&dbl_nat_entry, attr_count, nat_entry_attr); + if (status == SAI_STATUS_SUCCESS) + { + SWSS_LOG_DEBUG("Twice NAT HIT BIT for src-ip %s, dst-ip %s = %d", + key.src_ip.to_string().c_str(), key.dst_ip.to_string().c_str(), nat_entry_attr[0].value.booldata); + if (nat_entry_attr[0].value.booldata) + { + entry.ageOutTime = now + timeout; + return 1; + } + } + return 0; +} + +bool NatOrch::checkIfTwiceNaptEntryIsActive(const TwiceNaptEntry::iterator &iter, time_t now) +{ + const TwiceNaptEntryKey &key = iter->first; + TwiceNaptEntryValue &entry = iter->second; + uint8_t protoType = ((key.prototype == "TCP") ? IPPROTO_TCP : IPPROTO_UDP); + uint32_t attr_count; + sai_attribute_t nat_entry_attr[4]; + sai_nat_entry_t dbl_nat_entry; + sai_status_t status; + + if (entry.addedToHw == false) + { + SWSS_LOG_DEBUG("Skip get hitbits for Twice NAPT entry for [proto %s, src ip %s, src port %d, dst ip %s, dst port %d], as not yet added to HW", + key.prototype.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), key.dst_l4_port); + return 0; + } + + if (entry.entry_type == "static") + { + /* Static NAPT entries are always treated active */ + return 1; + } + + memset(nat_entry_attr, 0, sizeof(nat_entry_attr)); + nat_entry_attr[0].id = SAI_NAT_ENTRY_ATTR_HIT_BIT; /* Get the Hit bit */ + nat_entry_attr[0].value.booldata = 0; + nat_entry_attr[1].id = SAI_NAT_ENTRY_ATTR_HIT_BIT_COR; /* clear the hit bit after returning the value */ + nat_entry_attr[1].value.booldata = 1; + + attr_count = 2; + + memset(&dbl_nat_entry, 0, sizeof(dbl_nat_entry)); + + dbl_nat_entry.vr_id = gVirtualRouterId; + dbl_nat_entry.switch_id = gSwitchId; + dbl_nat_entry.data.key.src_ip = key.src_ip.getV4Addr(); + dbl_nat_entry.data.mask.src_ip = 0xffffffff; + dbl_nat_entry.data.key.l4_src_port = (uint16_t)(key.src_l4_port); + dbl_nat_entry.data.mask.l4_src_port = 0xffff; + dbl_nat_entry.data.key.dst_ip = key.dst_ip.getV4Addr(); + dbl_nat_entry.data.mask.dst_ip = 0xffffffff; + dbl_nat_entry.data.key.l4_dst_port = (uint16_t)(key.dst_l4_port); + dbl_nat_entry.data.mask.l4_dst_port = 0xffff; + dbl_nat_entry.data.key.proto = protoType; + dbl_nat_entry.data.mask.proto = 0xff; + + status = sai_nat_api->get_nat_entry_attribute(&dbl_nat_entry, attr_count, nat_entry_attr); + if (status == SAI_STATUS_SUCCESS) + { + SWSS_LOG_DEBUG("Twice NAPT HIT BIT for [proto %s, src ip %s, src port %d, dst ip %s, dst port %d] = %d", + key.prototype.c_str(), key.src_ip.to_string().c_str(), key.src_l4_port, key.dst_ip.to_string().c_str(), key.dst_l4_port, + nat_entry_attr[0].value.booldata); + if (nat_entry_attr[0].value.booldata) + { + entry.ageOutTime = now + ((protoType == IPPROTO_TCP) ? tcp_timeout : udp_timeout); + return 1; + } + } + return 0; +} + +void NatOrch::updateConnTrackTimeout(string prototype) +{ + if (prototype == "all") + { + NatEntry::iterator natIter = m_natEntries.begin(); + while (natIter != m_natEntries.end()) + { + if (((*natIter).second.addedToHw == true) and + ((*natIter).second.nat_type == "snat")) + { + updateConnTrackTimeout((*natIter).first); + } + natIter++; + } + TwiceNatEntry::iterator tnatIter = m_twiceNatEntries.begin(); + while (tnatIter != m_twiceNatEntries.end()) + { + if ((*tnatIter).second.addedToHw == true) + { + updateConnTrackTimeout((*tnatIter).first); + } + tnatIter++; + } + } + else + { + std::string res; + std::string cmds = std::string("") + CONNTRACK + UPDATE; + int timeout = ((prototype == "tcp") ? tcp_timeout : udp_timeout); + + cmds += (" -p " + prototype + " -t " + to_string(timeout) + REDIRECT_TO_DEV_NULL); + swss::exec(cmds, res); + + SWSS_LOG_INFO("Updated the %s NAT conntrack entries timeout to %d", prototype.c_str(), timeout); + } +} + +void NatOrch::updateConnTrackTimeout(const IpAddress &sourceIpAddr) +{ + std::string res; + std::string cmds = std::string("") + CONNTRACK + UPDATE; + + cmds += (" -s " + sourceIpAddr.to_string() + " -t " + to_string(timeout) + REDIRECT_TO_DEV_NULL); + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Updated the active NAT conntrack entry with src-ip %s, timeout %u", + sourceIpAddr.to_string().c_str(), timeout); + } +} + +void NatOrch::updateConnTrackTimeout(const NaptEntryKey &entry) +{ + uint8_t protoType = ((entry.prototype == string("TCP")) ? IPPROTO_TCP : IPPROTO_UDP); + + std::string res; + std::string prototype = ((entry.prototype == string("TCP")) ? "tcp" : "udp"); + int timeout = ((protoType == IPPROTO_TCP) ? tcp_timeout : udp_timeout); + std::string cmds = std::string("") + CONNTRACK + UPDATE; + + cmds += (" -s " + entry.ip_address.to_string() + " -p " + prototype + " --orig-port-src " + to_string(entry.l4_port) + " -t " + to_string(timeout) + REDIRECT_TO_DEV_NULL); + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Updated active NAPT conntrack entry with protocol %s, src-ip %s, src-port %d, timeout %u", + entry.prototype.c_str(), entry.ip_address.to_string().c_str(), + entry.l4_port, ((protoType == IPPROTO_TCP) ? tcp_timeout : udp_timeout)); + } +} + +void NatOrch::updateConnTrackTimeout(const TwiceNatEntryKey &entry) +{ + std::string res; + std::string cmd = std::string("") + CONNTRACK + UPDATE; + + TwiceNatEntryValue value = m_twiceNatEntries[entry]; + + cmd += (" -s " + entry.src_ip.to_string() + " -d " + entry.dst_ip.to_string() + " -t " + std::to_string(timeout) + REDIRECT_TO_DEV_NULL); + + swss::exec(cmd, res); + + SWSS_LOG_INFO("Updated active Twice NAT conntrack entry with src-ip %s, dst-ip %s, timeout %u", + entry.src_ip.to_string().c_str(), entry.dst_ip.to_string().c_str(), timeout); +} + +void NatOrch::updateConnTrackTimeout(const TwiceNaptEntryKey &entry) +{ + uint8_t protoType = ((entry.prototype == string("TCP")) ? IPPROTO_TCP : IPPROTO_UDP); + + std::string res; + std::string prototype = ((entry.prototype == string("TCP")) ? "tcp" : "udp"); + int timeout = ((protoType == IPPROTO_TCP) ? tcp_timeout : udp_timeout); + std::string cmd = std::string("") + CONNTRACK + UPDATE; + + TwiceNaptEntryValue value = m_twiceNaptEntries[entry]; + + cmd += (" -s " + entry.src_ip.to_string() + " -p " + prototype + " --orig-port-src " + to_string(entry.src_l4_port) + + " -d " + entry.dst_ip.to_string() + " --orig-port-dst " + std::to_string(entry.dst_l4_port) + + " -t " + std::to_string(timeout) + REDIRECT_TO_DEV_NULL); + + swss::exec(cmd, res); + + SWSS_LOG_INFO("Updated active Twice NAPT conntrack entry with protocol %s, src-ip %s, src-port %d, dst-ip %s, dst-port %d, timeout %u", + entry.prototype.c_str(), entry.src_ip.to_string().c_str(), entry.src_l4_port, + entry.dst_ip.to_string().c_str(), entry.dst_l4_port, ((protoType == IPPROTO_TCP) ? tcp_timeout : udp_timeout)); +} + +void NatOrch::addConnTrackEntry(const IpAddress &ipAddr) +{ + std::string res; + std::string cmds = std::string("") + CONNTRACK + ADD; + NatEntryValue entry = m_natEntries[ipAddr]; + + cmds += (" -n " + entry.translated_ip.to_string() + ":1 -g 127.0.0.1:127" + " -p udp -t " + to_string(timeout) + + " --src " + ipAddr.to_string() + " --sport 1 --dst 127.0.0.1 --dport 127 -u ASSURED "); + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Added static NAT conntrack entry with src-ip %s, timeout %u", + ipAddr.to_string().c_str(), timeout); + } +} + +void NatOrch::addConnTrackEntry(const NaptEntryKey &keyEntry) +{ + std::string cmds = std::string("") + CONNTRACK + ADD; + std::string res, prototype, state; + int timeout = 0; + NaptEntryValue entry = m_naptEntries[keyEntry]; + + if (keyEntry.prototype == string("TCP")) + { + prototype = "tcp"; + timeout = tcp_timeout; + state = " --state ESTABLISHED "; + } + else + { + prototype = "udp"; + timeout = udp_timeout; + state = ""; + } + + cmds += (" -n " + entry.translated_ip.to_string() + ":" + to_string(entry.translated_l4_port) + " -g 127.0.0.1:127" + " -p " + prototype + " -t " + to_string(timeout) + + " --src " + keyEntry.ip_address.to_string() + " --sport " + to_string(keyEntry.l4_port) + " --dst 127.0.0.1 --dport 127 -u ASSURED " + state); + int ret = swss::exec(cmds, res); + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmds.c_str(), ret); + } + else + { + SWSS_LOG_INFO("Added static NAPT conntrack entry with protocol %s, src-ip %s, src-port %d, timeout %u", + keyEntry.prototype.c_str(), keyEntry.ip_address.to_string().c_str(), + keyEntry.l4_port, (keyEntry.prototype == string("TCP") ? tcp_timeout : udp_timeout)); + } +} + +void NatOrch::addConnTrackEntry(const TwiceNatEntryKey &keyEntry) +{ + std::string res; + std::string cmds = std::string("") + CONNTRACK + ADD; + TwiceNatEntryValue entry = m_twiceNatEntries[keyEntry]; + + cmds += (" -n " + entry.translated_src_ip.to_string() + ":1" + " -g " + entry.translated_dst_ip.to_string() + + ":1" + " -p udp" + " -t " + to_string(timeout) + + " --src " + keyEntry.src_ip.to_string() + " --sport 1" + " --dst " + keyEntry.dst_ip.to_string() + + " --dport 1" + " -u ASSURED " + REDIRECT_TO_DEV_NULL); + + swss::exec(cmds, res); + + SWSS_LOG_INFO("Added Static Twice NAT conntrack entry with src-ip %s, dst-ip %s, timeout %u", + keyEntry.src_ip.to_string().c_str(), keyEntry.dst_ip.to_string().c_str(), timeout); +} + +void NatOrch::addConnTrackEntry(const TwiceNaptEntryKey &keyEntry) +{ + std::string cmds = std::string("") + CONNTRACK + ADD; + std::string res, prototype, state; + int timeout = 0; + TwiceNaptEntryValue entry = m_twiceNaptEntries[keyEntry]; + + if (keyEntry.prototype == string("TCP")) + { + prototype = "tcp"; + timeout = tcp_timeout; + state = " --state ESTABLISHED "; + } + else + { + prototype = "udp"; + timeout = udp_timeout; + state = ""; + } + + cmds += (" -n " + entry.translated_src_ip.to_string() + ":" + to_string(entry.translated_src_l4_port) + " -g " + entry.translated_dst_ip.to_string() + + ":" + to_string(entry.translated_dst_l4_port) + " -p " + prototype + " -t " + to_string(timeout) + + " --src " + keyEntry.src_ip.to_string() + " --sport " + to_string(keyEntry.src_l4_port) + " --dst " + keyEntry.dst_ip.to_string() + + " --dport " + to_string(keyEntry.dst_l4_port) + " -u ASSURED " + state + REDIRECT_TO_DEV_NULL); + + swss::exec(cmds, res); + + SWSS_LOG_INFO("Added static Twice NAPT conntrack entry with protocol %s, src-ip %s, src-port %d, dst-ip %s, dst-port %d, timeout %u", + keyEntry.prototype.c_str(), keyEntry.src_ip.to_string().c_str(), keyEntry.src_l4_port, + keyEntry.dst_ip.to_string().c_str(), keyEntry.dst_l4_port, (keyEntry.prototype == string("TCP") ? tcp_timeout : udp_timeout)); + +} + +void NatOrch::doTask(NotificationConsumer& consumer) +{ + SWSS_LOG_ENTER(); + + std::string op; + std::string data; + std::vector values; + + unique_lock lock(m_natMutex); + + consumer.pop(op, data, values); + + if (&consumer == m_flushNotificationsConsumer) + { + if ((op == "ENTRIES") and (data == "ALL")) + { + SWSS_LOG_INFO("Received All Entries notification"); + flushAllNatEntries(); + addAllStaticConntrackEntries(); + } + else if ((op == "STATISTICS") and (data == "ALL")) + { + SWSS_LOG_INFO("Received All Statistics notification"); + clearCounters(); + } + else + { + SWSS_LOG_ERROR("Received unknown flush nat request"); + } + } + else if (&consumer == m_cleanupNotificationConsumer) + { + SWSS_LOG_NOTICE("Received RedisDB and ASIC cleanup notification on NAT docker stop"); + cleanupAppDbEntries(); + } +} + +void NatOrch::updateStaticNatCounters(int count) +{ + std::vector values; + std::string key = "Values"; + + swss::FieldValueTuple p("STATIC_NAT_ENTRIES", to_string(count)); + values.push_back(p); + + m_countersGlobalNatTable.set(key, values); +} + +void NatOrch::updateStaticNaptCounters(int count) +{ + std::vector values; + std::string key = "Values"; + + swss::FieldValueTuple p("STATIC_NAPT_ENTRIES", to_string(count)); + values.push_back(p); + + m_countersGlobalNatTable.set(key, values); +} + +void NatOrch::updateStaticTwiceNatCounters(int count) +{ + std::vector values; + std::string key = "Values"; + + swss::FieldValueTuple p("STATIC_TWICE_NAT_ENTRIES", to_string(count)); + values.push_back(p); + + m_countersGlobalNatTable.set(key, values); +} + +void NatOrch::updateStaticTwiceNaptCounters(int count) +{ + std::vector values; + std::string key = "Values"; + + swss::FieldValueTuple p("STATIC_TWICE_NAPT_ENTRIES", to_string(count)); + values.push_back(p); + + m_countersGlobalNatTable.set(key, values); +} + +void NatOrch::updateDynamicNatCounters(int count) +{ + std::vector values; + std::string key = "Values"; + + swss::FieldValueTuple p("DYNAMIC_NAT_ENTRIES", to_string(count)); + values.push_back(p); + + m_countersGlobalNatTable.set(key, values); +} + +void NatOrch::updateDynamicNaptCounters(int count) +{ + std::vector values; + std::string key = "Values"; + + swss::FieldValueTuple p("DYNAMIC_NAPT_ENTRIES", to_string(count)); + values.push_back(p); + + m_countersGlobalNatTable.set(key, values); +} + +void NatOrch::updateDynamicTwiceNatCounters(int count) +{ + std::vector values; + std::string key = "Values"; + + swss::FieldValueTuple p("DYNAMIC_TWICE_NAT_ENTRIES", to_string(count)); + values.push_back(p); + + m_countersGlobalNatTable.set(key, values); +} + +void NatOrch::updateDynamicTwiceNaptCounters(int count) +{ + std::vector values; + std::string key = "Values"; + + swss::FieldValueTuple p("DYNAMIC_TWICE_NAPT_ENTRIES", to_string(count)); + values.push_back(p); + + m_countersGlobalNatTable.set(key, values); +} + +void NatOrch::updateSnatCounters(int count) +{ + std::vector values; + std::string key = "Values"; + + swss::FieldValueTuple p("SNAT_ENTRIES", to_string(count)); + values.push_back(p); + + m_countersGlobalNatTable.set(key, values); +} + +void NatOrch::updateDnatCounters(int count) +{ + std::vector values; + std::string key = "Values"; + + swss::FieldValueTuple p("DNAT_ENTRIES", to_string(count)); + values.push_back(p); + + m_countersGlobalNatTable.set(key, values); +} + +#ifdef DEBUG_FRAMEWORK +/* Dump all internal operational information */ +bool NatOrch::debugdumpCLI(KeyOpFieldsValuesTuple t) +{ + SWSS_LOG_NOTICE("debugdumpcli called"); + debugdumpALL(); + + return true; +} + +void NatOrch::debugdumpALL() +{ + int count = 0; + IpAddress ipAddr; + NatEntryValue value; + NaptEntryKey naptKey; + NaptEntryValue naptValue; + TwiceNatEntryKey twiceNatKey; + TwiceNatEntryValue twiceNatValue; + TwiceNaptEntryKey twiceNaptKey; + TwiceNaptEntryValue twiceNaptValue; + struct timespec time_now; + + SWSS_LOG_ENTER(); + + unique_lock lock(m_natMutex); + + if (clock_gettime (CLOCK_MONOTONIC, &time_now) < 0) + { + return; + } + + SWSS_LOG_NOTICE("debugdumpall called"); + SWSS_DEBUG_PRINT(m_dbgCompName, "--- NatOrch Dump All Start --->"); + + SWSS_DEBUG_PRINT(m_dbgCompName, "\nNatOrch Internal values"); + SWSS_DEBUG_PRINT(m_dbgCompName, "-----------------------"); + SWSS_DEBUG_PRINT(m_dbgCompName, " Admin Mode : %s", admin_mode.c_str()); + SWSS_DEBUG_PRINT(m_dbgCompName, " Timeout : %d", timeout); + SWSS_DEBUG_PRINT(m_dbgCompName, " TCP timeout : %d", tcp_timeout); + SWSS_DEBUG_PRINT(m_dbgCompName, " UDP timeout : %d", udp_timeout); + SWSS_DEBUG_PRINT(m_dbgCompName, " Total Entries : %d", totalEntries); + SWSS_DEBUG_PRINT(m_dbgCompName, " Total Static Nat Entries : %d", totalStaticNatEntries); + SWSS_DEBUG_PRINT(m_dbgCompName, " Total Dynamic Nat Entries : %d", totalDynamicNatEntries); + SWSS_DEBUG_PRINT(m_dbgCompName, " Total Static Napt Entries : %d", totalStaticNaptEntries); + SWSS_DEBUG_PRINT(m_dbgCompName, " Total Dynamic Napt Entries : %d", totalDynamicNaptEntries); + SWSS_DEBUG_PRINT(m_dbgCompName, " Total Static Twice Nat Entries : %d", totalStaticTwiceNatEntries); + SWSS_DEBUG_PRINT(m_dbgCompName, " Total Dynamic Twice Nat Entries : %d", totalDynamicTwiceNatEntries); + SWSS_DEBUG_PRINT(m_dbgCompName, " Total Static Twice Napt Entries : %d", totalStaticTwiceNaptEntries); + SWSS_DEBUG_PRINT(m_dbgCompName, " Total Dynamic Twice Napt Entries: %d", totalDynamicTwiceNaptEntries); + SWSS_DEBUG_PRINT(m_dbgCompName, " Total Snat Entries : %d", totalSnatEntries); + SWSS_DEBUG_PRINT(m_dbgCompName, " Total Dnat Entries : %d", totalDnatEntries); + SWSS_DEBUG_PRINT(m_dbgCompName, " Max allowed NAT entries : %d", maxAllowedSNatEntries); + + SWSS_DEBUG_PRINT(m_dbgCompName, "\n\nNatOrch NAT entries Cache"); + SWSS_DEBUG_PRINT(m_dbgCompName, "--------------------------"); + + auto natIter = m_natEntries.begin(); + while (natIter != m_natEntries.end()) + { + ipAddr = natIter->first; + value = natIter->second; + count++; + SWSS_DEBUG_PRINT(m_dbgCompName, "%8d. IP: %s", count, ipAddr.to_string().c_str()); + SWSS_DEBUG_PRINT(m_dbgCompName, " Translated IP: %s, NAT Type: %s, Entry Type: %s", + value.translated_ip.to_string().c_str(), value.nat_type.c_str(), value.entry_type.c_str()); + SWSS_DEBUG_PRINT(m_dbgCompName, " Age-out time: %ld secs, Added-to-Hw: %s", + (value.ageOutTime - time_now.tv_sec), ((value.addedToHw) ? "Yes" : "No")); + natIter++; + } + count = 0; + SWSS_DEBUG_PRINT(m_dbgCompName, "\n\nNatOrch NAPT entries Cache"); + SWSS_DEBUG_PRINT(m_dbgCompName, "--------------------------"); + + auto naptIter = m_naptEntries.begin(); + while (naptIter != m_naptEntries.end()) + { + naptKey = naptIter->first; + naptValue = naptIter->second; + count++; + SWSS_DEBUG_PRINT(m_dbgCompName, "%8d. IP: %s, L4 Port: %d, Proto: %s", count, + naptKey.ip_address.to_string().c_str(), naptKey.l4_port, naptKey.prototype.c_str()); + SWSS_DEBUG_PRINT(m_dbgCompName, " Translated IP: %s, L4 Port: %d, NAT Type: %s, Entry Type: %s", + naptValue.translated_ip.to_string().c_str(), naptValue.translated_l4_port, + naptValue.nat_type.c_str(), naptValue.entry_type.c_str()); + SWSS_DEBUG_PRINT(m_dbgCompName, " Age-out time: %ld secs, Added-to-Hw: %s", + (naptValue.ageOutTime - time_now.tv_sec), ((naptValue.addedToHw) ? "Yes" : "No")); + naptIter++; + } + count = 0; + + SWSS_DEBUG_PRINT(m_dbgCompName, "\n\nNatOrch Twice NAT entries Cache"); + SWSS_DEBUG_PRINT(m_dbgCompName, "-------------------------------"); + + auto twiceNatIter = m_twiceNatEntries.begin(); + while (twiceNatIter != m_twiceNatEntries.end()) + { + twiceNatKey = twiceNatIter->first; + twiceNatValue = twiceNatIter->second; + count++; + SWSS_DEBUG_PRINT(m_dbgCompName, "%8d. Src IP: %s, Dst IP: %s", count, + twiceNatKey.src_ip.to_string().c_str(), twiceNatKey.dst_ip.to_string().c_str()); + SWSS_DEBUG_PRINT(m_dbgCompName, " Translated Src IP: %s, Dst IP: %s, Entry Type: %s", + twiceNatValue.translated_src_ip.to_string().c_str(), twiceNatValue.translated_dst_ip.to_string().c_str(), + twiceNatValue.entry_type.c_str()); + SWSS_DEBUG_PRINT(m_dbgCompName, " Age-out time: %ld secs, Added-to-Hw: %s", + (twiceNatValue.ageOutTime - time_now.tv_sec), ((twiceNatValue.addedToHw) ? "Yes" : "No")); + twiceNatIter++; + } + count = 0; + + SWSS_DEBUG_PRINT(m_dbgCompName, "\n\nNatOrch Twice NAPT entries Cache"); + SWSS_DEBUG_PRINT(m_dbgCompName, "--------------------------------"); + + auto twiceNaptIter = m_twiceNaptEntries.begin(); + while (twiceNaptIter != m_twiceNaptEntries.end()) + { + twiceNaptKey = twiceNaptIter->first; + twiceNaptValue = twiceNaptIter->second; + count++; + SWSS_DEBUG_PRINT(m_dbgCompName, "%8d. Src IP: %s, L4 Port: %d, Dst IP: %s, L4 Port: %d, Proto: %s", count, + twiceNaptKey.src_ip.to_string().c_str(), twiceNaptKey.src_l4_port, twiceNaptKey.dst_ip.to_string().c_str(), + twiceNaptKey.dst_l4_port, twiceNaptKey.prototype.c_str()); + SWSS_DEBUG_PRINT(m_dbgCompName, " Translated Src IP: %s, L4 Port: %d, Dst IP: %s, L4 Port: %d, Entry Type: %s", + twiceNaptValue.translated_src_ip.to_string().c_str(), twiceNaptValue.translated_src_l4_port, + twiceNaptValue.translated_dst_ip.to_string().c_str(), twiceNaptValue.translated_dst_l4_port, + twiceNaptValue.entry_type.c_str()); + SWSS_DEBUG_PRINT(m_dbgCompName, " Age-out time: %ld secs, Added-to-Hw: %s", + (twiceNaptValue.ageOutTime - time_now.tv_sec), ((twiceNaptValue.addedToHw) ? "Yes" : "No")); + twiceNaptIter++; + } + count = 0; + + if (gNhTrackingSupported == true) + { + SWSS_DEBUG_PRINT(m_dbgCompName, "\n\nNatOrch Dump NextHop resolution entries Cache"); + SWSS_DEBUG_PRINT(m_dbgCompName, "---------------------------------------------"); + + auto dnatNhIter = m_nhResolvCache.begin(); + while (dnatNhIter != m_nhResolvCache.end()) + { + ipAddr = dnatNhIter->first; + DnatEntries &dnatEntries = dnatNhIter->second; + count++; + SWSS_DEBUG_PRINT(m_dbgCompName, "%8d. Translated DNAT IP: %s, neighResolved: %d", count, + ipAddr.to_string().c_str(), dnatEntries.neighResolved); + if (dnatEntries.nextHopGroup != NextHopGroupKey()) + { + SWSS_DEBUG_PRINT(m_dbgCompName, " NextHop Group: %s", dnatEntries.nextHopGroup.to_string().c_str()); + } + if (dnatEntries.dnatIp != nullIpv4Addr) + { + SWSS_DEBUG_PRINT(m_dbgCompName, " DNAT Entry Key: DIP %s", dnatEntries.dnatIp.to_string().c_str()); + } + if (! dnatEntries.dnapt.empty()) + { + auto iter1 = dnatEntries.dnapt.begin(); + while (iter1 != dnatEntries.dnapt.end()) + { + SWSS_DEBUG_PRINT(m_dbgCompName, " DNAPT entry Key: DIP %s, Port %d, Proto %s", + (*iter1).ip_address.to_string().c_str(), (*iter1).l4_port, (*iter1).prototype.c_str()); + iter1++; + } + } + if (! dnatEntries.twiceNat.empty()) + { + auto iter2 = dnatEntries.twiceNat.begin(); + while (iter2 != dnatEntries.twiceNat.end()) + { + SWSS_DEBUG_PRINT(m_dbgCompName, " Twice NAT entry key: Src IP: %s, Dst IP: %s", + (*iter2).src_ip.to_string().c_str(), (*iter2).dst_ip.to_string().c_str()); + iter2++; + } + } + if (! dnatEntries.twiceNapt.empty()) + { + auto iter3 = dnatEntries.twiceNapt.begin(); + while (iter3 != dnatEntries.twiceNapt.end()) + { + SWSS_DEBUG_PRINT(m_dbgCompName, " Twice NAPT entry key: Src IP: %s, L4 Port: %d, Dst IP: %s, L4 Port: %d, Proto: %s", + (*iter3).src_ip.to_string().c_str(), (*iter3).src_l4_port, + (*iter3).dst_ip.to_string().c_str(), (*iter3).dst_l4_port, (*iter3).prototype.c_str()); + iter3++; + } + } + dnatNhIter++; + } + } +} +#endif diff --git a/orchagent/natorch.h b/orchagent/natorch.h new file mode 100644 index 0000000000..b0fb88cd40 --- /dev/null +++ b/orchagent/natorch.h @@ -0,0 +1,349 @@ +/* + * Copyright 2019 Broadcom Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SWSS_NATORCH_H +#define SWSS_NATORCH_H + +#include "orch.h" +#include "observer.h" +#include "portsorch.h" +#include "intfsorch.h" +#include "ipaddress.h" +#include "ipaddresses.h" +#include "ipprefix.h" +#include "nfnetlink.h" +#include "timer.h" +#include "routeorch.h" +#include "nexthopgroupkey.h" +#ifdef DEBUG_FRAMEWORK +#include "debugdumporch.h" +#endif + +#define VALUES "Values" // Global Values Key +#define NAT_HITBIT_N_CNTRS_QUERY_PERIOD 5 // 5 secs +#define NAT_HITBIT_QUERY_MULTIPLE 6 // Hit bits are queried every 30 secs +#define CONNTRACK "/usr/sbin/conntrack" +#define REDIRECT_TO_DEV_NULL " &> /dev/null" +#define FLUSH " -F" +#define UPDATE " -U" +#define DELETE " -D" +#define ADD " -I" + +struct NatEntryValue +{ + IpAddress translated_ip; // Translated IP address + string nat_type; // Nat Type - SNAT or DNAT + string entry_type; // Entry type - Static or Dynamic + time_t ageOutTime; // Timestamp in secs when the entry expires + bool addedToHw; // Boolean to represent added to hardware + + bool operator<(const NatEntryValue& other) const + { + return tie(translated_ip, nat_type, entry_type) < tie(other.translated_ip, other.nat_type, other.entry_type); + } +}; + +struct NaptEntryKey +{ + IpAddress ip_address; // IP address + int l4_port; // Port address + string prototype; // Prototype - TCP or UDP + + bool operator<(const NaptEntryKey& other) const + { + return tie(ip_address, l4_port, prototype) < tie(other.ip_address, other.l4_port, other.prototype); + } +}; + +struct NaptEntryValue +{ + IpAddress translated_ip; // Translated IP address + int translated_l4_port; // Translated port address + string nat_type; // Nat Type - SNAT or DNAT + string entry_type; // Entry type - Static or Dynamic + time_t ageOutTime; // Timestamp in secs when the entry expires + bool addedToHw; // Boolean to represent added to hardware + + bool operator<(const NaptEntryValue& other) const + { + return tie(translated_ip, translated_l4_port, nat_type, entry_type) < tie(other.translated_ip, other.translated_l4_port, other.nat_type, other.entry_type); + } +}; + +struct TwiceNatEntryKey +{ + IpAddress src_ip; + IpAddress dst_ip; + + bool operator<(const TwiceNatEntryKey& other) const + { + return tie(src_ip, dst_ip) < tie(other.src_ip, other.dst_ip); + } +}; + +struct TwiceNatEntryValue +{ + IpAddress translated_src_ip; + IpAddress translated_dst_ip; + string entry_type; // Entry type - Static or Dynamic + time_t ageOutTime; // Timestamp in secs when the entry expires + bool addedToHw; // Boolean to represent added to hardware + + bool operator<(const TwiceNatEntryValue& other) const + { + return tie(translated_src_ip, translated_dst_ip, entry_type) < tie(other.translated_src_ip, other.translated_dst_ip, other.entry_type); + } +}; + +struct TwiceNaptEntryKey +{ + IpAddress src_ip; + int src_l4_port; + IpAddress dst_ip; + int dst_l4_port; + string prototype; + + bool operator<(const TwiceNaptEntryKey& other) const + { + return tie(src_ip, src_l4_port, dst_ip, dst_l4_port, prototype) < tie(other.src_ip, other.src_l4_port, other.dst_ip, other.dst_l4_port, other.prototype); + } +}; + +struct TwiceNaptEntryValue +{ + IpAddress translated_src_ip; + int translated_src_l4_port; + IpAddress translated_dst_ip; + int translated_dst_l4_port; + string entry_type; // Entry type - Static or Dynamic + time_t ageOutTime; // Timestamp in secs when the entry expires + bool addedToHw; // Boolean to represent added to hardware + + bool operator<(const TwiceNaptEntryValue& other) const + { + return tie(translated_src_ip, translated_src_l4_port, translated_dst_ip, translated_dst_l4_port, entry_type) < + tie(other.translated_src_ip, other.translated_src_l4_port, other.translated_dst_ip, other.translated_dst_l4_port, other.entry_type); + } +}; + +/* Here Key is IpAddress (global address) and + * NatEntryValue contains translated values (local address, nat_type, entry_type) + */ +typedef std::map NatEntry; + +/* Here Key is NaptEntryKey (global address, global port and prototype) + * NaptEntryValue contains translated values (local address, local port, nat_type and entry_type) + */ +typedef std::map NaptEntry; + +typedef std::map TwiceNatEntry; + +typedef std::map TwiceNaptEntry; + +/* Cache of DNAT entries that are dependent on the + * nexthop resolution of the translated destination ip address. + */ +typedef std::set DnaptCache; +typedef std::set TwiceNatCache; +typedef std::set TwiceNaptCache; + +struct DnatEntries +{ + IpAddress dnatIp; /* NAT entry cache */ + DnaptCache dnapt; /* NAPT entries cache */ + TwiceNatCache twiceNat; /* Twice NAT entries cache */ + TwiceNaptCache twiceNapt; /* Twice NAPT entries cache */ + + NextHopGroupKey nextHopGroup; + bool neighResolved; +}; + +typedef std::map DnatNhResolvCache; + +class NatOrch: public Orch, public Subject, public Observer +{ +public: + + NatOrch(DBConnector *appDb, DBConnector *stateDb, vector &tableNames, RouteOrch *routeOrch, NeighOrch *neighOrch); + + ~NatOrch() + { + // do nothing + } + + void update(SubjectType, void *); + bool debugdumpCLI(KeyOpFieldsValuesTuple t); + void debugdumpALL(); + + NeighOrch *m_neighOrch; + RouteOrch *m_routeOrch; + +private: + + /* Netfilter socket to delete conntrack entries corresponding to aged out NAT entries */ + NfNetlink nfnl; + NatEntry m_natEntries; + NaptEntry m_naptEntries; + TwiceNatEntry m_twiceNatEntries; + TwiceNaptEntry m_twiceNaptEntries; + SelectableTimer *m_natQueryTimer; + DBConnector m_countersDb; + Table m_countersNatTable; + Table m_countersNaptTable; + Table m_countersTwiceNatTable; + Table m_countersTwiceNaptTable; + Table m_countersGlobalNatTable; + Table m_stateWarmRestartEnableTable; + Table m_stateWarmRestartTable; + Table m_natQueryTable; + Table m_naptQueryTable; + Table m_twiceNatQueryTable; + Table m_twiceNaptQueryTable; + NotificationConsumer *m_flushNotificationsConsumer; + NotificationConsumer *m_cleanupNotificationConsumer; + mutex m_natMutex; + string m_dbgCompName; + IpAddress nullIpv4Addr; + + /* DNAT/DNAPT entry is cached, to delete and re-add it whenever the direct NextHop (connected neighbor) + * or indirect NextHop (via route) to reach the DNAT IP is changed. */ + DnatNhResolvCache m_nhResolvCache; + + int timeout; + int tcp_timeout; + int udp_timeout; + int totalEntries; + int totalStaticNatEntries; + int totalDynamicNatEntries; + int totalStaticTwiceNatEntries; + int totalDynamicTwiceNatEntries; + int totalStaticNaptEntries; + int totalDynamicNaptEntries; + int totalStaticTwiceNaptEntries; + int totalDynamicTwiceNaptEntries; + int totalSnatEntries; + int totalDnatEntries; + int maxAllowedSNatEntries; + string admin_mode; + + void doTask(Consumer& consumer); + void doTask(SelectableTimer &timer); + void doTask(NotificationConsumer& consumer); + void doNatTableTask(Consumer& consumer); + void doNaptTableTask(Consumer& consumer); + void doTwiceNatTableTask(Consumer& consumer); + void doTwiceNaptTableTask(Consumer& consumer); + void doNatGlobalTableTask(Consumer& consumer); + + bool addNatEntry(const IpAddress &ip_address, const NatEntryValue &entry); + bool removeNatEntry(const IpAddress &ip_address); + bool addNaptEntry(const NaptEntryKey &keyEntry, const NaptEntryValue &entry); + bool removeNaptEntry(const NaptEntryKey &keyEntry); + + bool addTwiceNatEntry(const TwiceNatEntryKey &key, const TwiceNatEntryValue &value); + bool removeTwiceNatEntry(const TwiceNatEntryKey &key); + bool addTwiceNaptEntry(const TwiceNaptEntryKey &key, const TwiceNaptEntryValue &value); + bool removeTwiceNaptEntry(const TwiceNaptEntryKey &key); + + void updateNextHop(const NextHopUpdate& update); + void updateNeighbor(const NeighborUpdate& update); + bool isNextHopResolved(const NextHopUpdate &update); + void addNhCacheDnatEntries(const IpAddress &nhIp, bool add); + void addDnatToNhCache(const IpAddress &translatedIp, const IpAddress &dstIp); + void removeDnatFromNhCache(const IpAddress &translatedIp, const IpAddress &dstIp); + void addDnaptToNhCache(const IpAddress &translatedIp, const NaptEntryKey &key); + void removeDnaptFromNhCache(const IpAddress &translatedIp, const NaptEntryKey &key); + void addTwiceNatToNhCache(const IpAddress &translatedIp, const TwiceNatEntryKey &key); + void addTwiceNaptToNhCache(const IpAddress &translatedIp, const TwiceNaptEntryKey &key); + void removeTwiceNatFromNhCache(const IpAddress &translatedIp, const TwiceNatEntryKey &key); + void removeTwiceNaptFromNhCache(const IpAddress &translatedIp, const TwiceNaptEntryKey &key); + bool addHwSnatEntry(const IpAddress &ip_address); + bool addHwSnaptEntry(const NaptEntryKey &key); + bool addHwTwiceNatEntry(const TwiceNatEntryKey &key); + bool addHwTwiceNaptEntry(const TwiceNaptEntryKey &key); + bool removeHwSnatEntry(const IpAddress &dstIp); + bool removeHwSnaptEntry(const NaptEntryKey &key); + bool removeHwTwiceNatEntry(const TwiceNatEntryKey &key); + bool removeHwTwiceNaptEntry(const TwiceNaptEntryKey &key); + bool addHwDnatEntry(const IpAddress &ip_address); + bool addHwDnaptEntry(const NaptEntryKey &key); + bool removeHwDnatEntry(const IpAddress &dstIp); + bool removeHwDnaptEntry(const NaptEntryKey &key); + + void addAllStaticConntrackEntries(void); + void addConnTrackEntry(const IpAddress &ipAddr); + void addConnTrackEntry(const NaptEntryKey &key); + void addConnTrackEntry(const TwiceNatEntryKey &key); + void addConnTrackEntry(const TwiceNaptEntryKey &key); + void updateConnTrackTimeout(string prototype); + void updateConnTrackTimeout(const IpAddress &sourceIpAddr); + void updateConnTrackTimeout(const NaptEntryKey &entry); + void updateConnTrackTimeout(const TwiceNatEntryKey &entry); + void updateConnTrackTimeout(const TwiceNaptEntryKey &entry); + void deleteConnTrackEntry(const IpAddress &ipAddr); + void deleteConnTrackEntry(const NaptEntryKey &key); + void deleteConnTrackEntry(const TwiceNatEntryKey &key); + void deleteConnTrackEntry(const TwiceNaptEntryKey &key); + + bool checkIfNatEntryIsActive(const NatEntry::iterator &iter, time_t now); + bool checkIfNaptEntryIsActive(const NaptEntry::iterator &iter, time_t now); + bool checkIfTwiceNatEntryIsActive(const TwiceNatEntry::iterator &iter, time_t now); + bool checkIfTwiceNaptEntryIsActive(const TwiceNaptEntry::iterator &iter, time_t now); + + bool warmBootingInProgress(void); + void enableNatFeature(void); + void disableNatFeature(void); + void addAllNatEntries(void); + void flushAllNatEntries(void); + void clearAllDnatEntries(void); + void cleanupAppDbEntries(void); + void clearCounters(void); + void queryCounters(void); + void queryHitBits(void); + bool isNatEnabled(void); + bool getNatCounters(const NatEntry::iterator &iter); + bool getTwiceNatCounters(const TwiceNatEntry::iterator &iter); + bool getNaptCounters(const NaptEntry::iterator &iter); + bool getTwiceNaptCounters(const TwiceNaptEntry::iterator &iter); + bool setNatCounters(const NatEntry::iterator &iter); + bool setTwiceNatCounters(const TwiceNatEntry::iterator &iter); + bool setNaptCounters(const NaptEntry::iterator &iter); + bool setTwiceNaptCounters(const TwiceNaptEntry::iterator &iter); + void updateStaticNatCounters(int count); + void updateDynamicNatCounters(int count); + void updateStaticNaptCounters(int count); + void updateDynamicNaptCounters(int count); + void updateStaticTwiceNatCounters(int count); + void updateDynamicTwiceNatCounters(int count); + void updateStaticTwiceNaptCounters(int count); + void updateDynamicTwiceNaptCounters(int count); + void updateSnatCounters(int count); + void updateDnatCounters(int count); + void updateNatCounters(const IpAddress &ipAddr, + uint64_t snat_translations_pkts, uint64_t snat_translations_bytes); + void updateNaptCounters(const string &protocol, const IpAddress &ipAddr, int l4_port, + uint64_t snat_translations_pkts, uint64_t snat_translations_bytes); + void deleteNatCounters(const IpAddress &ipAddr); + void deleteNaptCounters(const string &protocol, const IpAddress &ipAddr, int l4_port); + void deleteTwiceNatCounters(const TwiceNatEntryKey &key); + void deleteTwiceNaptCounters(const TwiceNaptEntryKey &key); + void updateTwiceNatCounters(const TwiceNatEntryKey &key, + uint64_t nat_translations_pkts, uint64_t nat_translations_bytes); + void updateTwiceNaptCounters(const TwiceNaptEntryKey &key, + uint64_t nat_translations_pkts, uint64_t nat_translations_bytes); +}; + +#endif /* SWSS_NATORCH_H */ diff --git a/orchagent/nexthopgroupkey.h b/orchagent/nexthopgroupkey.h index 6e90845d90..d60aee8a32 100644 --- a/orchagent/nexthopgroupkey.h +++ b/orchagent/nexthopgroupkey.h @@ -86,6 +86,18 @@ class NextHopGroupKey return true; } + bool hasIntfNextHop() const + { + for (const auto &nh : m_nexthops) + { + if (nh.isIntfNextHop()) + { + return true; + } + } + return false; + } + void remove(const std::string &ip, const std::string &alias) { NextHopKey nh(ip, alias); diff --git a/orchagent/nexthopkey.h b/orchagent/nexthopkey.h index a86aac2cc1..757436226b 100644 --- a/orchagent/nexthopkey.h +++ b/orchagent/nexthopkey.h @@ -64,6 +64,11 @@ struct NextHopKey { return !(*this == o); } + + bool isIntfNextHop() const + { + return (ip_address.getV4Addr() == 0); + } }; #endif /* SWSS_NEXTHOPKEY_H */ diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index d71bafab85..6782a16087 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -35,6 +35,9 @@ CrmOrch *gCrmOrch; BufferOrch *gBufferOrch; SwitchOrch *gSwitchOrch; Directory gDirectory; +NatOrch *gNatOrch; + +bool gIsNatSupported = false; OrchDaemon::OrchDaemon(DBConnector *applDb, DBConnector *configDb, DBConnector *stateDb) : m_applDb(applDb), @@ -208,6 +211,18 @@ bool OrchDaemon::init() DebugCounterOrch *debug_counter_orch = new DebugCounterOrch(m_configDb, debug_counter_tables, 1000); + const int natorch_base_pri = 50; + + vector nat_tables = { + { APP_NAT_TABLE_NAME, natorch_base_pri + 4 }, + { APP_NAPT_TABLE_NAME, natorch_base_pri + 3 }, + { APP_NAT_TWICE_TABLE_NAME, natorch_base_pri + 2 }, + { APP_NAPT_TWICE_TABLE_NAME, natorch_base_pri + 1 }, + { APP_NAT_GLOBAL_TABLE_NAME, natorch_base_pri } + }; + + gNatOrch = new NatOrch(m_applDb, m_stateDb, nat_tables, gRouteOrch, gNeighOrch); + /* * The order of the orch list is important for state restore of warm start and * the queued processing in m_toSync map after gPortsOrch->allPortsReady() is set. @@ -262,6 +277,7 @@ bool OrchDaemon::init() m_orchList.push_back(cfg_vnet_rt_orch); m_orchList.push_back(vnet_orch); m_orchList.push_back(vnet_rt_orch); + m_orchList.push_back(gNatOrch); m_select = new Select(); diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 5e9c9b914a..3094692df6 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -30,6 +30,7 @@ #include "sfloworch.h" #include "debugcounterorch.h" #include "directory.h" +#include "natorch.h" using namespace swss; diff --git a/orchagent/port.h b/orchagent/port.h index 2af0eb85db..d5b8827c51 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -96,6 +96,8 @@ class Port std::vector m_priority_group_ids; sai_port_priority_flow_control_mode_t m_pfc_asym = SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_COMBINED; uint8_t m_pfc_bitmask = 0; + uint32_t m_nat_zone_id = 0; + /* * Following two bit vectors are used to lock * the PG/queue from being changed in BufferOrch. diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index ab9c37b371..bba295eeee 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -11,6 +11,9 @@ #include #include #include +#include +#include +#include #include #include "net/if.h" @@ -41,10 +44,11 @@ extern BufferOrch *gBufferOrch; #define VLAN_PREFIX "Vlan" #define DEFAULT_VLAN_ID 1 #define MAX_VALID_VLAN_ID 4094 -#define PORT_FLEX_STAT_COUNTER_POLL_MSECS "1000" -#define QUEUE_FLEX_STAT_COUNTER_POLL_MSECS "10000" + +#define PORT_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS 1000 +#define QUEUE_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS 10000 #define QUEUE_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS "10000" -#define PG_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS "10000" +#define PG_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS "10000" static map fec_mode_map = @@ -70,7 +74,7 @@ static map learn_mode_map = { "notification", SAI_BRIDGE_PORT_FDB_LEARNING_MODE_FDB_NOTIFICATION} }; -const vector portStatIds = +const vector port_stat_ids = { SAI_PORT_STAT_IF_IN_OCTETS, SAI_PORT_STAT_IF_IN_UCAST_PKTS, @@ -113,7 +117,7 @@ const vector portStatIds = SAI_PORT_STAT_ETHER_IN_PKTS_128_TO_255_OCTETS, }; -static const vector queueStatIds = +static const vector queue_stat_ids = { SAI_QUEUE_STAT_PACKETS, SAI_QUEUE_STAT_BYTES, @@ -151,7 +155,9 @@ static char* hostif_vlan_tag[] = { * default VLAN and all ports removed from .1Q bridge. */ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) : - Orch(db, tableNames) + Orch(db, tableNames), + port_stat_manager(PORT_STAT_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, PORT_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS, true), + queue_stat_manager(QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, QUEUE_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS, true) { SWSS_LOG_ENTER(); @@ -177,15 +183,6 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) m_flexCounterTable = unique_ptr(new ProducerTable(m_flex_db.get(), FLEX_COUNTER_TABLE)); m_flexCounterGroupTable = unique_ptr(new ProducerTable(m_flex_db.get(), FLEX_COUNTER_GROUP_TABLE)); - vector fields; - fields.emplace_back(POLL_INTERVAL_FIELD, PORT_FLEX_STAT_COUNTER_POLL_MSECS); - fields.emplace_back(STATS_MODE_FIELD, STATS_MODE_READ); - m_flexCounterGroupTable->set(PORT_STAT_COUNTER_FLEX_COUNTER_GROUP, fields); - - fields.emplace_back(POLL_INTERVAL_FIELD, QUEUE_FLEX_STAT_COUNTER_POLL_MSECS); - fields.emplace_back(STATS_MODE_FIELD, STATS_MODE_READ); - m_flexCounterGroupTable->set(QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP, fields); - string queueWmSha, pgWmSha; string queueWmPluginName = "watermark_queue.lua"; string pgWmPluginName = "watermark_pg.lua"; @@ -1381,14 +1378,71 @@ bool PortsOrch::setHostIntfsOperStatus(const Port& port, bool isUp) const return true; } +void PortsOrch::updateDbPortFlapCounter(const Port& port, vector& old_tuples, vector& new_tuples) const +{ + SWSS_LOG_ENTER(); + + string flap_value; + bool check = false; + + /* Fetching current flap counters from redis DB and incrementing it by 1 under any UP or DOWN event */ + + for (auto const &i : old_tuples) + { + if (fvField(i) == "flap_counter") + { + flap_value = fvValue(i); + check = true; + break; + } + } + + if (!check) + { + return; + } + + long flap_val = stol(flap_value); + flap_val = flap_val + 1; + string flaps; + stringstream flap_stream; + flap_stream << flap_val; + flaps = flap_stream.str(); + FieldValueTuple flap_tuple("flap_counter", flaps); + new_tuples.push_back(flap_tuple); +} + +void PortsOrch::updateDbPortLastFlapTime(vector& new_tuples) const +{ + SWSS_LOG_ENTER(); + + time_t now = time(0); + string date = ctime(&now); + + // convert now to tm struct for UTC + tm *gmtm = gmtime(&now); + date = asctime(gmtm); + FieldValueTuple tuple("last_flap", date); + new_tuples.push_back(tuple); +} + void PortsOrch::updateDbPortOperStatus(const Port& port, sai_port_oper_status_t status) const { SWSS_LOG_ENTER(); - vector tuples; + vector old_tuples; + vector new_tuples; + + bool exist = m_portTable->get(port.m_alias, old_tuples); + if (!exist) + { + return; + } + updateDbPortLastFlapTime(new_tuples); + updateDbPortFlapCounter(port, old_tuples, new_tuples); FieldValueTuple tuple("oper_status", oper_status_strings.at(status)); - tuples.push_back(tuple); - m_portTable->set(port.m_alias, tuples); + new_tuples.push_back(tuple); + m_portTable->set(port.m_alias, new_tuples); } bool PortsOrch::addPort(const set &lane_set, uint32_t speed, int an, string fec_mode) @@ -1461,16 +1515,6 @@ bool PortsOrch::removePort(sai_object_id_t port_id) return true; } -string PortsOrch::getPortFlexCounterTableKey(string key) -{ - return string(PORT_STAT_COUNTER_FLEX_COUNTER_GROUP) + ":" + key; -} - -string PortsOrch::getQueueFlexCounterTableKey(string key) -{ - return string(QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP) + ":" + key; -} - string PortsOrch::getQueueWatermarkFlexCounterTableKey(string key) { return string(QUEUE_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP) + ":" + key; @@ -1514,22 +1558,15 @@ bool PortsOrch::initPort(const string &alias, const set &lane_set) fields.push_back(tuple); m_counterTable->set("", fields); - /* Add port to flex_counter for updating stat counters */ - string key = getPortFlexCounterTableKey(sai_serialize_object_id(p.m_port_id)); - std::string delimiter = ""; - std::ostringstream counters_stream; - for (const auto &id: portStatIds) + // Install a flex counter for this port to track stats + std::unordered_set counter_stats; + for (const auto& it: port_stat_ids) { - counters_stream << delimiter << sai_serialize_port_stat(id); - delimiter = comma; + counter_stats.emplace(sai_serialize_port_stat(it)); } + port_stat_manager.setCounterIdList(p.m_port_id, CounterType::PORT, counter_stats); - fields.clear(); - fields.emplace_back(PORT_COUNTER_ID_LIST, counters_stream.str()); - - m_flexCounterTable->set(key, fields); - - PortUpdate update = {p, true }; + PortUpdate update = { p, true }; notify(SUBJECT_TYPE_PORT_CHANGE, static_cast(&update)); SWSS_LOG_NOTICE("Initialized port %s", alias.c_str()); @@ -2494,39 +2531,51 @@ void PortsOrch::doLagMemberTask(Consumer &consumer) status = fvValue(i); } - /* Sync an enabled member */ - if (status == "enabled") + if (lag.m_members.find(port_alias) == lag.m_members.end()) { - /* Duplicate entry */ - if (lag.m_members.find(port_alias) != lag.m_members.end()) + /* Assert the port doesn't belong to any LAG already */ + assert(!port.m_lag_id && !port.m_lag_member_id); + + if (!addLagMember(lag, port)) { - it = consumer.m_toSync.erase(it); + it++; continue; } + } - /* Assert the port doesn't belong to any LAG */ - assert(!port.m_lag_id && !port.m_lag_member_id); - - if (addLagMember(lag, port)) + /* Sync an enabled member */ + if (status == "enabled") + { + /* enable collection first, distribution-only mode + * is not supported on Mellanox platform + */ + if (setCollectionOnLagMember(port, true) && + setDistributionOnLagMember(port, true)) + { it = consumer.m_toSync.erase(it); + } else + { it++; + continue; + } } /* Sync an disabled member */ else /* status == "disabled" */ { - /* "status" is "disabled" at start when m_lag_id and - * m_lag_member_id are absent */ - if (!port.m_lag_id || !port.m_lag_member_id) + /* disable distribution first, distribution-only mode + * is not supported on Mellanox platform + */ + if (setDistributionOnLagMember(port, false) && + setCollectionOnLagMember(port, false)) { it = consumer.m_toSync.erase(it); - continue; } - - if (removeLagMember(lag, port)) - it = consumer.m_toSync.erase(it); else + { it++; + continue; + } } } /* Remove a LAG member */ @@ -2544,9 +2593,13 @@ void PortsOrch::doLagMemberTask(Consumer &consumer) } if (removeLagMember(lag, port)) + { it = consumer.m_toSync.erase(it); + } else + { it++; + } } else { @@ -2742,6 +2795,8 @@ bool PortsOrch::initializePort(Port &port) { port.m_oper_status = SAI_PORT_OPER_STATUS_DOWN; } + m_portTable->hset(port.m_alias, "flap_counter", "0"); + m_portTable->hset(port.m_alias, "last_flap", "N/A"); /* initialize port admin status */ if (!getPortAdminStatus(port.m_port_id, port.m_admin_state_up)) @@ -3340,6 +3395,52 @@ bool PortsOrch::removeLagMember(Port &lag, Port &port) return true; } +bool PortsOrch::setCollectionOnLagMember(Port &lagMember, bool enableCollection) +{ + /* Port must be LAG member */ + assert(port.m_lag_member_id); + + sai_status_t status = SAI_STATUS_FAILURE; + sai_attribute_t attr {}; + + attr.id = SAI_LAG_MEMBER_ATTR_INGRESS_DISABLE; + attr.value.booldata = !enableCollection; + + status = sai_lag_api->set_lag_member_attribute(lagMember.m_lag_member_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to %s collection on LAG member %s", + enableCollection ? "enable" : "disable", + lagMember.m_alias.c_str()); + return false; + } + + return true; +} + +bool PortsOrch::setDistributionOnLagMember(Port &lagMember, bool enableDistribution) +{ + /* Port must be LAG member */ + assert(port.m_lag_member_id); + + sai_status_t status = SAI_STATUS_FAILURE; + sai_attribute_t attr {}; + + attr.id = SAI_LAG_MEMBER_ATTR_EGRESS_DISABLE; + attr.value.booldata = !enableDistribution; + + status = sai_lag_api->set_lag_member_attribute(lagMember.m_lag_member_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to %s distribution on LAG member %s", + enableDistribution ? "enable" : "disable", + lagMember.m_alias.c_str()); + return false; + } + + return true; +} + void PortsOrch::generateQueueMap() { if (m_isQueueMapGenerated) @@ -3385,34 +3486,26 @@ void PortsOrch::generateQueueMapPerPort(const Port& port) queueIndexVector.emplace_back(id, to_string(queueRealIndex)); } - /* add ordinary Queue stat counters */ - string key = getQueueFlexCounterTableKey(id); - - std::string delimiter = ""; - std::ostringstream counters_stream; - for (const auto& it: queueStatIds) + // Install a flex counter for this queue to track stats + std::unordered_set counter_stats; + for (const auto& it: queue_stat_ids) { - counters_stream << delimiter << sai_serialize_queue_stat(it); - delimiter = comma; + counter_stats.emplace(sai_serialize_queue_stat(it)); } - - vector fieldValues; - fieldValues.emplace_back(QUEUE_COUNTER_ID_LIST, counters_stream.str()); - - m_flexCounterTable->set(key, fieldValues); + queue_stat_manager.setCounterIdList(port.m_queue_ids[queueIndex], CounterType::QUEUE, counter_stats); /* add watermark queue counters */ - key = getQueueWatermarkFlexCounterTableKey(id); + string key = getQueueWatermarkFlexCounterTableKey(id); - delimiter = ""; - counters_stream.str(""); + string delimiter(""); + std::ostringstream counters_stream; for (const auto& it: queueWatermarkStatIds) { counters_stream << delimiter << sai_serialize_queue_stat(it); delimiter = comma; } - fieldValues.clear(); + vector fieldValues; fieldValues.emplace_back(QUEUE_COUNTER_ID_LIST, counters_stream.str()); m_flexCounterTable->set(key, fieldValues); diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index a1a99a9406..1c25e0da50 100644 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -9,6 +9,7 @@ #include "observer.h" #include "macaddress.h" #include "producertable.h" +#include "flex_counter_manager.h" #define FCS_LEN 4 #define VLAN_TAG_LEN 4 @@ -104,14 +105,15 @@ class PortsOrch : public Orch, public Subject unique_ptr m_flexCounterTable; unique_ptr m_flexCounterGroupTable; - std::string getQueueFlexCounterTableKey(std::string s); std::string getQueueWatermarkFlexCounterTableKey(std::string s); - std::string getPortFlexCounterTableKey(std::string s); std::string getPriorityGroupWatermarkFlexCounterTableKey(std::string s); shared_ptr m_counter_db; shared_ptr m_flex_db; + FlexCounterManager port_stat_manager; + FlexCounterManager queue_stat_manager; + std::map m_portSupportedSpeeds; bool m_initDone = false; @@ -170,6 +172,8 @@ class PortsOrch : public Orch, public Subject bool removeLag(Port lag); bool addLagMember(Port &lag, Port &port); bool removeLagMember(Port &lag, Port &port); + bool setCollectionOnLagMember(Port &lagMember, bool enableCollection); + bool setDistributionOnLagMember(Port &lagMember, bool enableDistribution); void getLagMember(Port &lag, vector &portv); bool addPort(const set &lane_set, uint32_t speed, int an=0, string fec=""); @@ -210,6 +214,9 @@ class PortsOrch : public Orch, public Subject bool setPortSerdesAttribute(sai_object_id_t port_id, sai_attr_id_t attr_id, vector &serdes_val); + + void updateDbPortFlapCounter(const Port& port, vector& old_tuples, vector& new_tuples) const; + void updateDbPortLastFlapTime(vector& new_tuples) const; + }; #endif /* SWSS_PORTSORCH_H */ - diff --git a/orchagent/qosorch.cpp b/orchagent/qosorch.cpp index a3a317a986..e7761cd5d6 100644 --- a/orchagent/qosorch.cpp +++ b/orchagent/qosorch.cpp @@ -729,20 +729,6 @@ QosOrch::QosOrch(DBConnector *db, vector &tableNames) : Orch(db, tableNa { SWSS_LOG_ENTER(); - // we should really introduce capability query in SAI so that we can first - // query the capability and then decide what to do instead of hardcoding the - // platform-specfic logic like this here, which is ugly and difficult to - // understand the underlying rationale. - - // Do not create color ACL on p4 platform as it does not support match dscp and ecn - char *platform = getenv("platform"); - if (!platform || - (platform && strcmp(platform, "x86_64-barefoot_p4-r0") != 0)) - { - // add ACLs to support Sonic WRED profile. - initColorAcl(); // FIXME: Should be removed as soon as we have ACL configuration support - } - initTableHandlers(); }; @@ -752,123 +738,6 @@ type_map& QosOrch::getTypeMap() return m_qos_maps; } -void QosOrch::initColorAcl() -{ - SWSS_LOG_ENTER(); - sai_object_id_t acl_table_id; - - // init ACL system table - acl_table_id = initSystemAclTable(); - - // Add entry to match packets with dscp=8, ecn=0 and set yellow color to them - initAclEntryForEcn(acl_table_id, 1000, 0x00, 0x08, SAI_PACKET_COLOR_YELLOW); - // Add entry to match packets with dscp=0, ecn=0 and set yellow color to them - initAclEntryForEcn(acl_table_id, 999, 0x00, 0x00, SAI_PACKET_COLOR_YELLOW); -} - -sai_object_id_t QosOrch::initSystemAclTable() -{ - SWSS_LOG_ENTER(); - vector attrs; - sai_attribute_t attr; - sai_object_id_t acl_table_id; - sai_status_t status; - - /* Create system ACL table */ - attr.id = SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST; - vector bpoint_list; - bpoint_list.push_back(SAI_ACL_BIND_POINT_TYPE_PORT); - attr.value.s32list.count = 1; - attr.value.s32list.list = bpoint_list.data(); - attrs.push_back(attr); - - attr.id = SAI_ACL_TABLE_ATTR_ACL_STAGE; - attr.value.s32 = SAI_ACL_STAGE_INGRESS; - attrs.push_back(attr); - - attr.id = SAI_ACL_TABLE_ATTR_FIELD_ECN; - attr.value.booldata = true; - attrs.push_back(attr); - - attr.id = SAI_ACL_TABLE_ATTR_FIELD_DSCP; - attr.value.booldata = true; - attrs.push_back(attr); - - status = sai_acl_api->create_acl_table(&acl_table_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to create a system ACL table for ECN coloring, rv:%d", status); - throw runtime_error("Failed to create a system ACL table for ECN coloring"); - } - SWSS_LOG_NOTICE("Create a system ACL table for ECN coloring"); - - gCrmOrch->incCrmAclUsedCounter(CrmResourceType::CRM_ACL_TABLE, SAI_ACL_STAGE_INGRESS, SAI_ACL_BIND_POINT_TYPE_PORT); - - for (auto& pair: gPortsOrch->getAllPorts()) - { - auto& port = pair.second; - if (port.m_type != Port::PHY) continue; - - sai_object_id_t group_member_oid; - // Note: group member OID is discarded - if (!gPortsOrch->bindAclTable(port.m_port_id, acl_table_id, group_member_oid)) - { - SWSS_LOG_ERROR("Failed to bind the system ACL table globally, rv:%d", status); - throw runtime_error("Failed to bind the system ACL table globally"); - } - } - - - SWSS_LOG_NOTICE("Bind the system ACL table globally"); - - return acl_table_id; -} - -void QosOrch::initAclEntryForEcn(sai_object_id_t acl_table_id, sai_uint32_t priority, - sai_uint8_t ecn_field, sai_uint8_t dscp_field, sai_int32_t color) -{ - SWSS_LOG_ENTER(); - vector attrs; - sai_attribute_t attr; - sai_object_id_t acl_entry_id; - sai_status_t status; - - attr.id = SAI_ACL_ENTRY_ATTR_TABLE_ID; - attr.value.oid = acl_table_id; - attrs.push_back(attr); - - attr.id = SAI_ACL_ENTRY_ATTR_PRIORITY; - attr.value.u32 = priority; - attrs.push_back(attr); - - attr.id = SAI_ACL_TABLE_ATTR_FIELD_ECN; - attr.value.aclfield.enable = true; - attr.value.aclfield.data.u8 = ecn_field; - attr.value.aclfield.mask.u8 = 0x3; - attrs.push_back(attr); - - attr.id = SAI_ACL_TABLE_ATTR_FIELD_DSCP; - attr.value.aclfield.enable = true; - attr.value.aclfield.data.u8 = dscp_field; - attr.value.aclfield.mask.u8 = 0x3f; - attrs.push_back(attr); - - attr.id = SAI_ACL_ENTRY_ATTR_ACTION_SET_PACKET_COLOR; - attr.value.aclaction.enable = true; - attr.value.aclaction.parameter.s32 = color; - attrs.push_back(attr); - - status = sai_acl_api->create_acl_entry(&acl_entry_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to create a system ACL entry for ECN coloring, rv=%d", status); - throw runtime_error("Failed to create a system ACL entry for ECN coloring"); - } - SWSS_LOG_INFO("Create a system ACL entry for ECN coloring"); - - gCrmOrch->incCrmAclTableUsedCounter(CrmResourceType::CRM_ACL_ENTRY, acl_table_id); -} - void QosOrch::initTableHandlers() { SWSS_LOG_ENTER(); diff --git a/orchagent/qosorch.h b/orchagent/qosorch.h index 88ed96d914..29101d9051 100644 --- a/orchagent/qosorch.h +++ b/orchagent/qosorch.h @@ -134,11 +134,6 @@ class QosOrch : public Orch typedef map qos_table_handler_map; typedef pair qos_handler_pair; - void initColorAcl(); - sai_object_id_t initSystemAclTable(); - void initAclEntryForEcn(sai_object_id_t acl_table_id, sai_uint32_t priority, - sai_uint8_t ecn_field, sai_uint8_t dscp_field, sai_int32_t color); - void initTableHandlers(); task_process_status handleDscpToTcTable(Consumer& consumer); diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index 9fbe449179..ec990d605e 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -43,6 +43,7 @@ sai_dtel_api_t* sai_dtel_api; sai_bmtor_api_t* sai_bmtor_api; sai_samplepacket_api_t* sai_samplepacket_api; sai_debug_counter_api_t* sai_debug_counter_api; +sai_nat_api_t* sai_nat_api; extern sai_object_id_t gSwitchId; extern bool gSairedisRecord; @@ -134,6 +135,7 @@ void initSaiApi() sai_api_query((sai_api_t)SAI_API_BMTOR, (void **)&sai_bmtor_api); sai_api_query(SAI_API_SAMPLEPACKET, (void **)&sai_samplepacket_api); sai_api_query(SAI_API_DEBUG_COUNTER, (void **)&sai_debug_counter_api); + sai_api_query(SAI_API_NAT, (void **)&sai_nat_api); sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BRIDGE, SAI_LOG_LEVEL_NOTICE); @@ -162,6 +164,7 @@ void initSaiApi() sai_log_set((sai_api_t)SAI_API_BMTOR, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_SAMPLEPACKET, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_DEBUG_COUNTER, SAI_LOG_LEVEL_NOTICE); + sai_log_set((sai_api_t)SAI_API_NAT, SAI_LOG_LEVEL_NOTICE); } void initSaiRedis(const string &record_location) diff --git a/swssconfig/sample/00-copp.config.json b/swssconfig/sample/00-copp.config.json index de3ec33c7f..997b73bdb3 100644 --- a/swssconfig/sample/00-copp.config.json +++ b/swssconfig/sample/00-copp.config.json @@ -43,8 +43,8 @@ "OP": "SET" }, { - "COPP_TABLE:trap.group.ip2me": { - "trap_ids": "ip2me", + "COPP_TABLE:trap.group.nat.ip2me": { + "trap_ids": "ip2me,src_nat_miss,dest_nat_miss", "trap_action":"trap", "trap_priority":"1", "queue": "1", diff --git a/tests/conftest.py b/tests/conftest.py index 5e1c496c71..16c0ad10b7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -66,13 +66,13 @@ def __init__(self, dvs): atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") keys = atbl.getKeys() - assert len(keys) >= 1 + assert len(keys) >= 0 self.default_acl_tables = keys atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") keys = atbl.getKeys() - assert len(keys) == 2 + assert len(keys) == 0 self.default_acl_entries = keys class ApplDbValidator(object): @@ -157,7 +157,10 @@ def __init__(self, name=None, imgname=None, keeptb=False, fakeplatform=None): self.syncd = ['syncd'] self.rtd = ['fpmsyncd', 'zebra'] self.teamd = ['teamsyncd', 'teammgrd'] - self.alld = self.basicd + self.swssd + self.syncd + self.rtd + self.teamd + # FIXME: We need to verify that NAT processes are running, once the + # appropriate changes are merged into sonic-buildimage + # self.natd = ['natsyncd', 'natmgrd'] + self.alld = self.basicd + self.swssd + self.syncd + self.rtd + self.teamd # + self.natd self.client = docker.from_env() if subprocess.check_call(["/sbin/modprobe", "team"]) != 0: diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 73574f5b62..4c0b5582ce 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -60,7 +60,8 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/orchagent/watermarkorch.cpp \ $(top_srcdir)/orchagent/chassisorch.cpp \ $(top_srcdir)/orchagent/sfloworch.cpp \ - $(top_srcdir)/orchagent/debugcounterorch.cpp + $(top_srcdir)/orchagent/debugcounterorch.cpp \ + $(top_srcdir)/orchagent/natorch.cpp tests_SOURCES += $(FLEX_CTR_DIR)/flex_counter_manager.cpp $(FLEX_CTR_DIR)/flex_counter_stat_manager.cpp tests_SOURCES += $(DEBUG_CTR_DIR)/debug_counter.cpp $(DEBUG_CTR_DIR)/drop_counter.cpp diff --git a/tests/mock_tests/aclorch_ut.cpp b/tests/mock_tests/aclorch_ut.cpp index 19d52cafbf..66b0090d23 100644 --- a/tests/mock_tests/aclorch_ut.cpp +++ b/tests/mock_tests/aclorch_ut.cpp @@ -628,16 +628,71 @@ namespace aclorch_test // consistency validation with CRM bool validateResourceCountWithCrm(const AclOrch *aclOrch, CrmOrch *crmOrch) { - auto resourceMap = Portal::CrmOrchInternal::getResourceMap(crmOrch); - auto ifpPortKey = Portal::CrmOrchInternal::getCrmAclKey(crmOrch, SAI_ACL_STAGE_INGRESS, SAI_ACL_BIND_POINT_TYPE_PORT); - auto efpPortKey = Portal::CrmOrchInternal::getCrmAclKey(crmOrch, SAI_ACL_STAGE_EGRESS, SAI_ACL_BIND_POINT_TYPE_PORT); + // Verify ACL Tables + auto const &resourceMap = Portal::CrmOrchInternal::getResourceMap(crmOrch); + uint32_t crm_acl_table_cnt = 0; + for (auto const &kv : resourceMap.at(CrmResourceType::CRM_ACL_TABLE).countersMap) + { + crm_acl_table_cnt += kv.second.usedCounter; + } + + if (crm_acl_table_cnt != Portal::AclOrchInternal::getAclTables(aclOrch).size()) + { + ADD_FAILURE() << "ACL table size is not consistent between CrmOrch (" << crm_acl_table_cnt + << ") and AclOrch " << Portal::AclOrchInternal::getAclTables(aclOrch).size(); + return false; + } + + + // Verify ACL Rules + // + // for each CRM_ACL_ENTRY and CRM_ACL_COUNTER's entry => the ACL TABLE should exist + // + for (auto acl_entry_or_counter : { CrmResourceType::CRM_ACL_ENTRY, CrmResourceType::CRM_ACL_COUNTER }) + { + auto const &resourceMap = Portal::CrmOrchInternal::getResourceMap(crmOrch); + for (auto const &kv : resourceMap.at(acl_entry_or_counter).countersMap) + { + auto acl_oid = kv.second.id; - auto ifpPortAclTableCount = resourceMap.at(CrmResourceType::CRM_ACL_TABLE).countersMap[ifpPortKey].usedCounter; - auto efpPortAclTableCount = resourceMap.at(CrmResourceType::CRM_ACL_TABLE).countersMap[efpPortKey].usedCounter; + const auto &aclTables = Portal::AclOrchInternal::getAclTables(aclOrch); + if (aclTables.find(acl_oid) == aclTables.end()) + { + ADD_FAILURE() << "Can't find ACL '" << sai_serialize_object_id(acl_oid) << "' in AclOrch"; + return false; + } + + if (kv.second.usedCounter != aclTables.at(acl_oid).rules.size()) + { + ADD_FAILURE() << "CRM usedCounter (" << kv.second.usedCounter + << ") is not equal rule in ACL (" << aclTables.at(acl_oid).rules.size() << ")"; + return false; + } + } + } - return ifpPortAclTableCount + efpPortAclTableCount == Portal::AclOrchInternal::getAclTables(aclOrch).size(); + // + // for each ACL TABLE with rule count larger than one => it shoule exist a corresponding entry in CRM_ACL_ENTRY and CRM_ACL_COUNTER + // + for (const auto &kv : Portal::AclOrchInternal::getAclTables(aclOrch)) + { + if (0 < kv.second.rules.size()) + { + auto key = Portal::CrmOrchInternal::getCrmAclTableKey(crmOrch, kv.first); + for (auto acl_entry_or_counter : { CrmResourceType::CRM_ACL_ENTRY, CrmResourceType::CRM_ACL_COUNTER }) + { + const auto &cntMap = Portal::CrmOrchInternal::getResourceMap(crmOrch).at(acl_entry_or_counter).countersMap; + if (cntMap.find(key) == cntMap.end()) + { + ADD_FAILURE() << "Can't find ACL (" << sai_serialize_object_id(kv.first) + << ") in " << (acl_entry_or_counter == CrmResourceType::CRM_ACL_ENTRY ? "CrmResourceType::CRM_ACL_ENTRY" : "CrmResourceType::CRM_ACL_COUNTER"); + return false; + } + } + } + } - // TODO: add rule check + return true; } // leakage check diff --git a/tests/test_acl.py b/tests/test_acl.py index 83909b7c81..43316bd34c 100644 --- a/tests/test_acl.py +++ b/tests/test_acl.py @@ -329,19 +329,85 @@ def test_AclRuleInOutPorts(self, dvs, testlog): (status, fvs) = atbl.get(acl_entry[0]) assert status == False - def test_AclTableDeletion(self, dvs, testlog): + def test_AclRuleInPortsNonExistingInterface(self, dvs, testlog): + self.setup_db(dvs) + + # Create ACL rule with a completely wrong interface + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "55"), + ("PACKET_ACTION", "FORWARD"), + ("IN_PORTS", "FOO_BAR_BAZ")]) + tbl.set("test|foo_bar_baz", fvs) + time.sleep(1) + + # Make sure no rules were created + atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 0 + + # Create ACL rule with a correct interface and a completely wrong interface + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "55"), + ("PACKET_ACTION", "FORWARD"), + ("IN_PORTS", "Ethernet0,FOO_BAR_BAZ")]) + tbl.set("test|foo_bar_baz", fvs) + time.sleep(1) + + # Make sure no rules were created + atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 0 + # Delete rule + tbl._del("test|foo_bar_baz") + time.sleep(1) + + def test_AclRuleOutPortsNonExistingInterface(self, dvs, testlog): self.setup_db(dvs) - db = swsscommon.DBConnector(4, dvs.redis_sock, 0) - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - # get ACL_TABLE in config db - tbl = swsscommon.Table(db, "ACL_TABLE") + # Create ACL rule with a completely wrong interface + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "55"), + ("PACKET_ACTION", "FORWARD"), + ("OUT_PORTS", "FOO_BAR_BAZ")]) + tbl.set("test|foo_bar_baz", fvs) + time.sleep(1) + + # Make sure no rules were created + atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 0 + + # Create ACL rule with a correct interface and a completely wrong interface + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "55"), + ("PACKET_ACTION", "FORWARD"), + ("OUT_PORTS", "Ethernet0,FOO_BAR_BAZ")]) + tbl.set("test|foo_bar_baz", fvs) + time.sleep(1) + + # Make sure no rules were created + atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 0 + + # Delete rule + tbl._del("test|foo_bar_baz") + time.sleep(1) + + def test_AclTableDeletion(self, dvs, testlog): + self.setup_db(dvs) + + tbl = swsscommon.Table(self.cdb, "ACL_TABLE") tbl._del("test") time.sleep(1) - atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") keys = atbl.getKeys() # only the default table was left along with DTel tables assert len(keys) >= 1 diff --git a/tests/test_fdb_update.py b/tests/test_fdb_update.py index d74c3f0c0f..1771ac5aea 100644 --- a/tests/test_fdb_update.py +++ b/tests/test_fdb_update.py @@ -5,261 +5,260 @@ import json from distutils.version import StrictVersion -def create_entry(tbl, key, pairs): - fvs = swsscommon.FieldValuePairs(pairs) - tbl.set(key, fvs) - - # FIXME: better to wait until DB create them - time.sleep(1) -def remove_entry(tbl, key): - tbl._del(key) - time.sleep(1) - -def create_entry_tbl(db, table, key, pairs): - tbl = swsscommon.Table(db, table) - create_entry(tbl, key, pairs) - -def remove_entry_tbl(db, table, key): - tbl = swsscommon.Table(db, table) - remove_entry(tbl, key) - -def create_entry_pst(db, table, key, pairs): - tbl = swsscommon.ProducerStateTable(db, table) - create_entry(tbl, key, pairs) - -def remove_entry_pst(db, table, key): - tbl = swsscommon.ProducerStateTable(db, table) - remove_entry(tbl, key) - -def how_many_entries_exist(db, table): - tbl = swsscommon.Table(db, table) - return len(tbl.getKeys()) - -def get_mac_by_bridge_id(dvs, bridge_id): - tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") - keys = tbl.getKeys() - - mac = [] - for key in keys: - (status, fvs) = tbl.get(key) - assert status - value = dict(fvs) - if value["SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID"] == bridge_id: - try: - d_key = json.loads(key) - except ValueError: - d_key = json.loads('{' + key + '}') - mac.append(d_key["mac"]) - return mac - -def test_FDBAddedAndUpdated(dvs, testlog): - dvs.setup_db() - - dvs.runcmd("sonic-clear fdb all") - time.sleep(2) - - # create a FDB entry in Application DB - create_entry_pst( - dvs.pdb, - "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", - [ - ("port", "Ethernet0"), - ("type", "dynamic"), - ] - ) - - # check that the FDB entry wasn't inserted into ASIC DB - assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" - - vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") - bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") - vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") - - # create vlan - create_entry_tbl( - dvs.cdb, - "VLAN", "Vlan2", - [ - ("vlanid", "2"), - ] - ) - - # create vlan member entry in Config db - create_entry_tbl( - dvs.cdb, - "VLAN_MEMBER", "Vlan2|Ethernet0", - [ - ("tagging_mode", "untagged"), - ] - ) - - # check that the vlan information was propagated - vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") - bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") - vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") - - assert vlan_after - vlan_before == 1, "The Vlan2 wasn't created" - assert bp_after - bp_before == 1, "The bridge port wasn't created" - assert vm_after - vm_before == 1, "The vlan member wasn't added" - - # Get bvid from vlanid - ok, bvid = dvs.get_vlan_oid(dvs.adb, "2") - assert ok, bvid - - # Get mapping between interface name and its bridge port_id - iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) - - # check that the FDB entry was inserted into ASIC DB - assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" - - ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", - [("mac", "52:54:00:25:06:E9"), ("bvid", bvid)], - [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), - ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"]), - ('SAI_FDB_ENTRY_ATTR_PACKET_ACTION', 'SAI_PACKET_ACTION_FORWARD')] - ) - assert ok, str(extra) - - # create vlan member entry in Config DB - create_entry_tbl( - dvs.cdb, - "VLAN_MEMBER", "Vlan2|Ethernet4", - [ - ("tagging_mode", "untagged"), - ] - ) - - # update FDB entry port in Application DB - create_entry_pst( - dvs.pdb, - "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", - [ - ("port", "Ethernet4"), - ("type", "dynamic"), - ] - ) - - # Get mapping between interface name and its bridge port_id - iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) - - ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", - [("mac", "52:54:00:25:06:E9"), ("bvid", bvid)], - [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), - ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet4"]), - ('SAI_FDB_ENTRY_ATTR_PACKET_ACTION', 'SAI_PACKET_ACTION_FORWARD')] - ) - assert ok, str(extra) - - # remove FDB entry from Application DB - remove_entry_pst( - dvs.pdb, - "FDB_TABLE", "Vlan2:52-54-00-25-06-E9" - ) - - # remove vlan member entry from Config DB - remove_entry_tbl( - dvs.cdb, - "VLAN_MEMBER", "Vlan2|Ethernet4" - ) - remove_entry_tbl( - dvs.cdb, - "VLAN_MEMBER", "Vlan2|Ethernet0" - ) - - #remove vlan entry from Config DB - remove_entry_tbl( - dvs.cdb, - "VLAN", "Vlan2" - ) - -def test_FDBLearnedAndUpdated(dvs, testlog): - dvs.setup_db() - - dvs.runcmd("sonic-clear fdb all") - - # create vlan; create vlan member - dvs.create_vlan("6") - dvs.create_vlan_member("6", "Ethernet64") - dvs.create_vlan_member("6", "Ethernet68") - dvs.create_vlan_member("6", "Ethernet72") - - # Get mapping between interface name and its bridge port_id - time.sleep(2) - iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) - - # bring up vlan and member - dvs.set_interface_status("Vlan6", "up") - - dvs.add_ip_address("Vlan6", "6.6.6.1/24") - - dvs.set_interface_status("Ethernet64", "up") - dvs.set_interface_status("Ethernet68", "up") - dvs.set_interface_status("Ethernet72", "up") - - dvs.servers[16].runcmd("ifconfig eth0 hw ether 00:00:00:00:00:11") - dvs.servers[16].runcmd("ifconfig eth0 6.6.6.6/24 up") - dvs.servers[16].runcmd("ip route add default via 6.6.6.1") - dvs.servers[17].runcmd("ifconfig eth0 6.6.6.7/24 up") - dvs.servers[17].runcmd("ip route add default via 6.6.6.1") - time.sleep(2) - - # get neighbor and arp entry - rc = dvs.servers[16].runcmd("ping -c 1 6.6.6.7") - assert rc == 0 - rc = dvs.servers[17].runcmd("ping -c 1 6.6.6.6") - assert rc == 0 - time.sleep(2) - - # check that the FDB entries were inserted into ASIC DB - Ethernet64_mac = get_mac_by_bridge_id(dvs, iface_2_bridge_port_id["Ethernet64"]) - assert "00:00:00:00:00:11" in Ethernet64_mac - - # update FDB entry port in Application DB - create_entry_pst( - dvs.pdb, - "FDB_TABLE", "Vlan6:00-00-00-00-00-11", - [ - ("port", "Ethernet72"), - ("type", "dynamic"), - ] - ) - - # check that the FDB entry was updated in ASIC DB - Ethernet72_mac = get_mac_by_bridge_id(dvs, iface_2_bridge_port_id["Ethernet72"]) - assert "00:00:00:00:00:11" in Ethernet72_mac, "Updating fdb entry to Ethernet72 failed" - - Ethernet64_mac = get_mac_by_bridge_id(dvs, iface_2_bridge_port_id["Ethernet64"]) - assert "00:00:00:00:00:11" not in Ethernet64_mac, "Updating fdb entry from Ethernet64 failed" - - # remove FDB entry from Application DB - remove_entry_pst( - dvs.pdb, - "FDB_TABLE", "Vlan6:00-00-00-00-00-11" - ) - - # restore the default value of the servers - dvs.servers[16].runcmd("ip route del default via 6.6.6.1") - dvs.servers[16].runcmd("ifconfig eth0 0") - dvs.servers[17].runcmd("ip route del default via 6.6.6.1") - dvs.servers[17].runcmd("ifconfig eth0 0") - - # bring down port - dvs.set_interface_status("Ethernet64", "down") - dvs.set_interface_status("Ethernet68", "down") - dvs.set_interface_status("Ethernet72", "down") - - # remove vlan ip - key = "Vlan6" + "|" + "6.6.6.1/24" - remove_entry_tbl(dvs.cdb, "VLAN_INTERFACE", key) - - # bring down vlan - dvs.set_interface_status("Vlan6", "down") - - # remove vlan member; remove vlan - dvs.remove_vlan_member("6", "Ethernet64") - dvs.remove_vlan_member("6", "Ethernet68") - dvs.remove_vlan_member("6", "Ethernet72") - dvs.remove_vlan("6") - - # clear fdb - dvs.runcmd("sonic-clear fdb all") + +class TestFdbUpdate(object): + def create_entry(self, tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + time.sleep(1) + + def remove_entry(self, tbl, key): + tbl._del(key) + time.sleep(1) + + def create_entry_tbl(self, db, table, key, pairs): + tbl = swsscommon.Table(db, table) + self.create_entry(tbl, key, pairs) + + def remove_entry_tbl(self, db, table, key): + tbl = swsscommon.Table(db, table) + self.remove_entry(tbl, key) + + def create_entry_pst(self, db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + self.create_entry(tbl, key, pairs) + + def remove_entry_pst(self, db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + self.remove_entry(tbl, key) + + def how_many_entries_exist(self, db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + + def get_mac_by_bridge_id(self, dvs, bridge_id): + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") + keys = tbl.getKeys() + + mac = [] + for key in keys: + (status, fvs) = tbl.get(key) + assert status + value = dict(fvs) + if value["SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID"] == bridge_id: + try: + d_key = json.loads(key) + except ValueError: + d_key = json.loads('{' + key + '}') + mac.append(d_key["mac"]) + return mac + + def test_FDBAddedAndUpdated(self, dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + # create a FDB entry in Application DB + self.create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + [ + ("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry wasn't inserted into ASIC DB + assert self.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" + + vlan_before = self.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_before = self.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = self.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + # create vlan + self.create_entry_tbl( + dvs.cdb, + "VLAN", "Vlan2", + [ + ("vlanid", "2"), + ] + ) + + # create vlan member entry in Config db + self.create_entry_tbl( + dvs.cdb, + "VLAN_MEMBER", "Vlan2|Ethernet0", + [ + ("tagging_mode", "untagged"), + ] + ) + + # check that the vlan information was propagated + vlan_after = self.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = self.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = self.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 1, "The Vlan2 wasn't created" + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + + # Get bvid from vlanid + ok, bvid = dvs.get_vlan_oid(dvs.adb, "2") + assert ok, bvid + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # check that the FDB entry was inserted into ASIC DB + assert self.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", bvid)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"]), + ('SAI_FDB_ENTRY_ATTR_PACKET_ACTION', 'SAI_PACKET_ACTION_FORWARD')]) + assert ok, str(extra) + + # create vlan member entry in Config DB + self.create_entry_tbl( + dvs.cdb, + "VLAN_MEMBER", "Vlan2|Ethernet4", + [ + ("tagging_mode", "untagged"), + ] + ) + + # update FDB entry port in Application DB + self.create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + [ + ("port", "Ethernet4"), + ("type", "dynamic"), + ] + ) + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", bvid)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet4"]), + ('SAI_FDB_ENTRY_ATTR_PACKET_ACTION', 'SAI_PACKET_ACTION_FORWARD')]) + assert ok, str(extra) + + # remove FDB entry from Application DB + self.remove_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9" + ) + + # remove vlan member entry from Config DB + self.remove_entry_tbl( + dvs.cdb, + "VLAN_MEMBER", "Vlan2|Ethernet4" + ) + self.remove_entry_tbl( + dvs.cdb, + "VLAN_MEMBER", "Vlan2|Ethernet0" + ) + + # remove vlan entry from Config DB + self.remove_entry_tbl( + dvs.cdb, + "VLAN", "Vlan2" + ) + + def test_FDBLearnedAndUpdated(self, dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + + # create vlan; create vlan member + dvs.create_vlan("6") + dvs.create_vlan_member("6", "Ethernet64") + dvs.create_vlan_member("6", "Ethernet68") + dvs.create_vlan_member("6", "Ethernet72") + + # Get mapping between interface name and its bridge port_id + time.sleep(2) + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # bring up vlan and member + dvs.set_interface_status("Vlan6", "up") + + dvs.add_ip_address("Vlan6", "6.6.6.1/24") + + dvs.set_interface_status("Ethernet64", "up") + dvs.set_interface_status("Ethernet68", "up") + dvs.set_interface_status("Ethernet72", "up") + + dvs.servers[16].runcmd("ifconfig eth0 hw ether 00:00:00:00:00:11") + dvs.servers[16].runcmd("ifconfig eth0 6.6.6.6/24 up") + dvs.servers[16].runcmd("ip route add default via 6.6.6.1") + dvs.servers[17].runcmd("ifconfig eth0 6.6.6.7/24 up") + dvs.servers[17].runcmd("ip route add default via 6.6.6.1") + time.sleep(2) + + # get neighbor and arp entry + rc = dvs.servers[16].runcmd("ping -c 1 6.6.6.7") + assert rc == 0 + rc = dvs.servers[17].runcmd("ping -c 1 6.6.6.6") + assert rc == 0 + time.sleep(2) + + # check that the FDB entries were inserted into ASIC DB + Ethernet64_mac = self.get_mac_by_bridge_id(dvs, iface_2_bridge_port_id["Ethernet64"]) + assert "00:00:00:00:00:11" in Ethernet64_mac + + # update FDB entry port in Application DB + self.create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan6:00-00-00-00-00-11", + [ + ("port", "Ethernet72"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry was updated in ASIC DB + Ethernet72_mac = self.get_mac_by_bridge_id(dvs, iface_2_bridge_port_id["Ethernet72"]) + assert "00:00:00:00:00:11" in Ethernet72_mac, "Updating fdb entry to Ethernet72 failed" + + Ethernet64_mac = self.get_mac_by_bridge_id(dvs, iface_2_bridge_port_id["Ethernet64"]) + assert "00:00:00:00:00:11" not in Ethernet64_mac, "Updating fdb entry from Ethernet64 failed" + + # remove FDB entry from Application DB + self.remove_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan6:00-00-00-00-00-11" + ) + + # restore the default value of the servers + dvs.servers[16].runcmd("ip route del default via 6.6.6.1") + dvs.servers[16].runcmd("ifconfig eth0 0") + dvs.servers[17].runcmd("ip route del default via 6.6.6.1") + dvs.servers[17].runcmd("ifconfig eth0 0") + + # bring down port + dvs.set_interface_status("Ethernet64", "down") + dvs.set_interface_status("Ethernet68", "down") + dvs.set_interface_status("Ethernet72", "down") + + # remove vlan ip + key = "Vlan6" + "|" + "6.6.6.1/24" + self.remove_entry_tbl(dvs.cdb, "VLAN_INTERFACE", key) + + # bring down vlan + dvs.set_interface_status("Vlan6", "down") + + # remove vlan member; remove vlan + dvs.remove_vlan_member("6", "Ethernet64") + dvs.remove_vlan_member("6", "Ethernet68") + dvs.remove_vlan_member("6", "Ethernet72") + dvs.remove_vlan("6") + + # clear fdb + dvs.runcmd("sonic-clear fdb all") diff --git a/tests/test_nat.py b/tests/test_nat.py new file mode 100644 index 0000000000..909d17a03e --- /dev/null +++ b/tests/test_nat.py @@ -0,0 +1,358 @@ +from swsscommon import swsscommon +import time +import re +import json +import pytest +import pdb +import os + +# FIXME: These tests depend on changes in sonic-buildimage, we need to reenable +# them once those changes are merged. +@pytest.mark.skip(reason="Depends on changes in sonic-buildimage") +class TestNatFeature(object): + def setup_db(self, dvs): + self.appdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + self.asicdb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + self.configdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + def set_interfaces(self, dvs): + intf_tbl = swsscommon.Table(self.configdb, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Ethernet0|67.66.65.1/24", fvs) + intf_tbl.set("Ethernet4|18.18.18.1/24", fvs) + intf_tbl.set("Ethernet0", fvs) + intf_tbl.set("Ethernet4", fvs) + dvs.runcmd("ifconfig Ethernet0 up") + dvs.runcmd("ifconfig Ethernet4 up") + + dvs.servers[0].runcmd("ip link set down dev eth0") == 0 + dvs.servers[0].runcmd("ip link set up dev eth0") == 0 + dvs.servers[0].runcmd("ifconfig eth0 67.66.65.2/24") + dvs.servers[0].runcmd("ip route add default via 67.66.65.1") + + dvs.servers[1].runcmd("ip link set down dev eth0") == 0 + dvs.servers[1].runcmd("ip link set up dev eth0") == 0 + dvs.servers[1].runcmd("ifconfig eth0 18.18.18.2/24") + dvs.servers[1].runcmd("ip route add default via 18.18.18.1") + + ps = swsscommon.ProducerStateTable(self.appdb, "ROUTE_TABLE") + fvs = swsscommon.FieldValuePairs([("nexthop","18.18.18.2"), \ + ("ifname", "Ethernet0")]) + + pubsub = dvs.SubscribeAsicDbObject("SAI_OBJECT_TYPE_ROUTE_ENTRY") + + dvs.runcmd("config nat add interface Ethernet0 -nat_zone 1") + + time.sleep(1) + + def clear_interfaces(self, dvs): + dvs.servers[0].runcmd("ifconfig eth0 0.0.0.0") + + dvs.servers[1].runcmd("ifconfig eth0 0.0.0.0") + # dvs.servers[1].runcmd("ip route del default") + + time.sleep(1) + + def test_NatGlobalTable(self, dvs, testlog): + # initialize + self.setup_db(dvs) + + # enable NAT feature + dvs.runcmd("config nat feature enable") + dvs.runcmd("config nat set timeout 450") + dvs.runcmd("config nat set udp-timeout 360") + dvs.runcmd("config nat set tcp-timeout 900") + + # check NAT global values in appdb + tbl = swsscommon.Table(self.appdb, "NAT_GLOBAL_TABLE") + values = tbl.getKeys() + + assert len(values) == 1 + + (status, fvs) = tbl.get("Values") + + assert fvs==(('admin_mode', 'enabled'), ('nat_timeout', '450'), ('nat_udp_timeout', '360'), ('nat_tcp_timeout', '900')) + + def test_NatInterfaceZone(self, dvs, testlog): + # initialize + self.setup_db(dvs) + self.set_interfaces(dvs) + + # check NAT zone is set for interface in app db + tbl = swsscommon.Table(self.appdb, "INTF_TABLE") + keys = tbl.getKeys() + + (status, fvs) = tbl.get("Ethernet0") + + assert fvs==(('NULL', 'NULL'), ('nat_zone', '1')) + + + def test_AddNatStaticEntry(self, dvs, testlog): + # initialize + self.setup_db(dvs) + + # get neighbor and arp entry + dvs.servers[0].runcmd("ping -c 1 18.18.18.2") + + # add a static nat entry + dvs.runcmd("config nat add static basic 67.66.65.1 18.18.18.2") + + # check the entry in the config db + tbl = swsscommon.Table(self.configdb, "STATIC_NAT") + entry = tbl.getKeys() + assert len(entry) == 1 + + (status, fvs) = tbl.get("67.66.65.1") + + assert fvs==(('local_ip', '18.18.18.2'),) + + # check the entry in app db + tbl = swsscommon.Table(self.appdb, "NAT_TABLE") + entry = tbl.getKeys() + assert len(entry) == 2 + + (status, fvs) = tbl.get("67.66.65.1") + + assert fvs== (('translated_ip', '18.18.18.2'), ('nat_type', 'dnat'), ('entry_type', 'static')) + + #check the entry in asic db + tbl = swsscommon.Table(self.asicdb, "ASIC_STATE:SAI_OBJECT_TYPE_NAT_ENTRY") + keys = tbl.getKeys() + assert len(keys) == 2 + + for key in keys: + if (key.find("dst_ip:67.66.65.1")) or (key.find("src_ip:18.18.18.2")): + assert True + else: + assert False + + def test_DelNatStaticEntry(self, dvs, testlog): + # initialize + self.setup_db(dvs) + + # delete a static nat entry + dvs.runcmd("config nat remove static basic 67.66.65.1 18.18.18.2") + + # check the entry is no there in the config db + tbl = swsscommon.Table(self.configdb, "STATIC_NAT") + entry = tbl.getKeys() + assert entry == () + + # check the entry is not there in app db + tbl = swsscommon.Table(self.appdb, "NAT_TABLE") + entry = tbl.getKeys() + assert entry == () + + #check the entry is not there in asic db + tbl = swsscommon.Table(self.asicdb, "ASIC_STATE:SAI_OBJECT_TYPE_NAT_ENTRY") + key = tbl.getKeys() + assert key == () + + def test_AddNaPtStaticEntry(self, dvs, testlog): + # initialize + self.setup_db(dvs) + + # get neighbor and arp entry + dvs.servers[0].runcmd("ping -c 1 18.18.18.2") + + # add a static nat entry + dvs.runcmd("config nat add static udp 67.66.65.1 670 18.18.18.2 180") + + # check the entry in the config db + tbl = swsscommon.Table(self.configdb, "STATIC_NAPT") + entry = tbl.getKeys() + assert len(entry) == 1 + + (status, fvs) = tbl.get("67.66.65.1|UDP|670") + + assert fvs==(('local_ip', '18.18.18.2'),('local_port', '180')) + + # check the entry in app db + tbl = swsscommon.Table(self.appdb, "NAPT_TABLE:UDP") + entry = tbl.getKeys() + assert len(entry) == 2 + + (status, fvs) = tbl.get("67.66.65.1:670") + + assert fvs== (('translated_ip', '18.18.18.2'), ('translated_l4_port', '180'), ('nat_type', 'dnat'), ('entry_type', 'static')) + + #check the entry in asic db + tbl = swsscommon.Table(self.asicdb, "ASIC_STATE:SAI_OBJECT_TYPE_NAT_ENTRY") + keys = tbl.getKeys() + assert len(keys) == 2 + + for key in keys: + if (key.find("dst_ip:67.66.65.1")) and (key.find("key.l4_dst_port:670")): + assert True + if (key.find("src_ip:18.18.18.2")) or (key.find("key.l4_src_port:180")): + assert True + else: + assert False + + def test_DelNaPtStaticEntry(self, dvs, testlog): + # initialize + self.setup_db(dvs) + + # delete a static nat entry + dvs.runcmd("config nat remove static udp 67.66.65.1 670 18.18.18.2 180") + + # check the entry is no there in the config db + tbl = swsscommon.Table(self.configdb, "STATIC_NAPT") + entry = tbl.getKeys() + assert entry == () + + # check the entry is not there in app db + tbl = swsscommon.Table(self.appdb, "NAPT_TABLE") + entry = tbl.getKeys() + assert entry == () + + #check the entry is not there in asic db + tbl = swsscommon.Table(self.asicdb, "ASIC_STATE:SAI_OBJECT_TYPE_NAT_ENTRY") + key = tbl.getKeys() + assert key == () + + def test_AddTwiceNatEntry(self, dvs, testlog): + # initialize + self.setup_db(dvs) + + # get neighbor and arp entry + dvs.servers[0].runcmd("ping -c 1 18.18.18.2") + dvs.servers[1].runcmd("ping -c 1 67.66.65.2") + + # add a twice nat entry + dvs.runcmd("config nat add static basic 67.66.65.2 18.18.18.1 -nat_type snat -twice_nat_id 9") + dvs.runcmd("config nat add static basic 67.66.65.1 18.18.18.2 -nat_type dnat -twice_nat_id 9") + + # check the entry in the config db + tbl = swsscommon.Table(self.configdb, "STATIC_NAT") + entry = tbl.getKeys() + assert len(entry) == 2 + + (status, fvs) = tbl.get("67.66.65.1") + + assert fvs== (('nat_type', 'dnat'), ('twice_nat_id', '9'), ('local_ip', '18.18.18.2')) + + (status, fvs) = tbl.get("67.66.65.2") + + assert fvs== (('nat_type', 'snat'), ('twice_nat_id', '9'), ('local_ip', '18.18.18.1')) + + # check the entry in app db + tbl = swsscommon.Table(self.appdb, "NAT_TWICE_TABLE") + entry = tbl.getKeys() + assert len(entry) == 2 + + (status, fvs) = tbl.get("67.66.65.2:67.66.65.1") + + assert fvs== (('translated_src_ip', '18.18.18.1'), ('translated_dst_ip', '18.18.18.2'), ('entry_type', 'static')) + + (status, fvs) = tbl.get("18.18.18.2:18.18.18.1") + + assert fvs== (('translated_src_ip', '67.66.65.1'), ('translated_dst_ip', '67.66.65.2'), ('entry_type', 'static')) + + #check the entry in asic db + tbl = swsscommon.Table(self.asicdb, "ASIC_STATE:SAI_OBJECT_TYPE_NAT_ENTRY") + keys = tbl.getKeys() + assert len(keys) == 2 + for key in keys: + if (key.find("dst_ip:18.18.18.1")) or (key.find("src_ip:18.18.18.2")): + assert True + else: + assert False + + def test_DelTwiceNatStaticEntry(self, dvs, testlog): + # initialize + self.setup_db(dvs) + + # delete a static nat entry + dvs.runcmd("config nat remove static basic 67.66.65.2 18.18.18.1") + dvs.runcmd("config nat remove static basic 67.66.65.1 18.18.18.2") + + # check the entry is no there in the config db + tbl = swsscommon.Table(self.configdb, "STATIC_NAT") + entry = tbl.getKeys() + assert entry == () + + # check the entry is not there in app db + tbl = swsscommon.Table(self.appdb, "NAT_TWICE_TABLE") + entry = tbl.getKeys() + assert entry == () + + #check the entry is not there in asic db + tbl = swsscommon.Table(self.asicdb, "ASIC_STATE:SAI_OBJECT_TYPE_NAT_ENTRY") + key = tbl.getKeys() + assert key == () + + def test_AddTwiceNaPtEntry(self, dvs, testlog): + # initialize + self.setup_db(dvs) + + # get neighbor and arp entry + dvs.servers[0].runcmd("ping -c 1 18.18.18.2") + dvs.servers[1].runcmd("ping -c 1 67.66.65.2") + + # add a twice nat entry + dvs.runcmd("config nat add static udp 67.66.65.2 670 18.18.18.1 181 -nat_type snat -twice_nat_id 7") + dvs.runcmd("config nat add static udp 67.66.65.1 660 18.18.18.2 182 -nat_type dnat -twice_nat_id 7") + + # check the entry in the config db + tbl = swsscommon.Table(self.configdb, "STATIC_NAPT") + entry = tbl.getKeys() + assert len(entry) == 2 + + (status, fvs) = tbl.get("67.66.65.1|UDP|660") + + assert fvs== (('nat_type', 'dnat'), ('local_ip', '18.18.18.2'), ('twice_nat_id', '7'), ('local_port', '182')) + (status, fvs) = tbl.get("67.66.65.2|UDP|670") + + assert fvs== (('nat_type', 'snat'), ('local_ip', '18.18.18.1'),('twice_nat_id', '7'), ('local_port', '181')) + + # check the entry in app db + tbl = swsscommon.Table(self.appdb, "NAPT_TWICE_TABLE") + entry = tbl.getKeys() + assert len(entry) == 2 + + (status, fvs) = tbl.get("UDP:67.66.65.2:670:67.66.65.1:660") + + assert fvs== (('translated_src_ip', '18.18.18.1'), ('translated_src_l4_port', '181'), ('translated_dst_ip', '18.18.18.2'), ('translated_dst_l4_port', '182'), ('entry_type', 'static')) + + (status, fvs) = tbl.get("UDP:18.18.18.2:182:18.18.18.1:181") + + assert fvs== (('translated_src_ip', '67.66.65.1'), ('translated_src_l4_port', '660'),('translated_dst_ip', '67.66.65.2'),('translated_dst_l4_port', '670'), ('entry_type', 'static')) + + #check the entry in asic db + tbl = swsscommon.Table(self.asicdb, "ASIC_STATE:SAI_OBJECT_TYPE_NAT_ENTRY") + keys = tbl.getKeys() + assert len(keys) == 2 + for key in keys: + if (key.find("src_ip:18.18.18.2")) or (key.find("l4_src_port:182")): + assert True + if (key.find("dst_ip:18.18.18.1")) or (key.find("l4_dst_port:181")): + assert True + else: + assert False + + def test_DelTwiceNaPtStaticEntry(self, dvs, testlog): + # initialize + self.setup_db(dvs) + + # delete a static nat entry + dvs.runcmd("config nat remove static udp 67.66.65.2 670 18.18.18.1 181") + dvs.runcmd("config nat remove static udp 67.66.65.1 660 18.18.18.2 182") + + # check the entry is not there in the config db + tbl = swsscommon.Table(self.configdb, "STATIC_NAPT") + entry = tbl.getKeys() + assert entry == () + + # check the entry is not there in app db + tbl = swsscommon.Table(self.appdb, "NAPT_TWICE_TABLE") + entry = tbl.getKeys() + assert entry == () + + #check the entry is not there in asic db + tbl = swsscommon.Table(self.asicdb, "ASIC_STATE:SAI_OBJECT_TYPE_NAT_ENTRY") + key = tbl.getKeys() + assert key == () + + # clear interfaces + self.clear_interfaces(dvs) diff --git a/tests/test_portchannel.py b/tests/test_portchannel.py index 0bc6db1e2c..d95a2e5eed 100644 --- a/tests/test_portchannel.py +++ b/tests/test_portchannel.py @@ -33,13 +33,41 @@ def test_Portchannel(self, dvs, testlog): assert len(lagms) == 1 (status, fvs) = lagmtbl.get(lagms[0]) - for fv in fvs: - if fv[0] == "SAI_LAG_MEMBER_ATTR_LAG_ID": - assert fv[1] == lags[0] - elif fv[0] == "SAI_LAG_MEMBER_ATTR_PORT_ID": - assert dvs.asicdb.portoidmap[fv[1]] == "Ethernet0" - else: - assert False + fvs = dict(fvs) + assert status + assert "SAI_LAG_MEMBER_ATTR_LAG_ID" in fvs + assert fvs.pop("SAI_LAG_MEMBER_ATTR_LAG_ID") == lags[0] + assert "SAI_LAG_MEMBER_ATTR_PORT_ID" in fvs + assert dvs.asicdb.portoidmap[fvs.pop("SAI_LAG_MEMBER_ATTR_PORT_ID")] == "Ethernet0" + assert "SAI_LAG_MEMBER_ATTR_INGRESS_DISABLE" in fvs + assert fvs.pop("SAI_LAG_MEMBER_ATTR_INGRESS_DISABLE") == "false" + assert "SAI_LAG_MEMBER_ATTR_EGRESS_DISABLE" in fvs + assert fvs.pop("SAI_LAG_MEMBER_ATTR_EGRESS_DISABLE") == "false" + assert not fvs + + ps = swsscommon.ProducerStateTable(db, "LAG_MEMBER_TABLE") + fvs = swsscommon.FieldValuePairs([("status", "disabled")]) + + ps.set("PortChannel0001:Ethernet0", fvs) + + time.sleep(1) + + lagmtbl = swsscommon.Table(asicdb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG_MEMBER") + lagms = lagmtbl.getKeys() + assert len(lagms) == 1 + + (status, fvs) = lagmtbl.get(lagms[0]) + fvs = dict(fvs) + assert status + assert "SAI_LAG_MEMBER_ATTR_LAG_ID" in fvs + assert fvs.pop("SAI_LAG_MEMBER_ATTR_LAG_ID") == lags[0] + assert "SAI_LAG_MEMBER_ATTR_PORT_ID" in fvs + assert dvs.asicdb.portoidmap[fvs.pop("SAI_LAG_MEMBER_ATTR_PORT_ID")] == "Ethernet0" + assert "SAI_LAG_MEMBER_ATTR_INGRESS_DISABLE" in fvs + assert fvs.pop("SAI_LAG_MEMBER_ATTR_INGRESS_DISABLE") == "true" + assert "SAI_LAG_MEMBER_ATTR_EGRESS_DISABLE" in fvs + assert fvs.pop("SAI_LAG_MEMBER_ATTR_EGRESS_DISABLE") == "true" + assert not fvs # remove port channel member ps = swsscommon.ProducerStateTable(db, "LAG_MEMBER_TABLE") diff --git a/tests/test_vlan.py b/tests/test_vlan.py index 4a0202dd62..4b8bb59776 100644 --- a/tests/test_vlan.py +++ b/tests/test_vlan.py @@ -424,6 +424,10 @@ def test_AddVlanWithIncorrectValueType(self, dvs, testlog, test_input, expected) #remove vlan dvs.remove_vlan(vlan) + # FIXME: This test is extremely unstable and requires several retries + # for it to pass - we need to stabilize this test before putting it back + # into the pipeline. + @pytest.mark.xfail(reason="test case is unstable") def test_AddPortChannelToVlan(self, dvs, testlog): self.setup_db(dvs) marker = dvs.add_log_marker() @@ -644,6 +648,10 @@ def test_AddMaxVlan(self, dvs, testlog): vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] assert len(vlan_entries) == 0 + # FIXME: This test is extremely unstable and requires several retries + # for it to pass - we need to stabilize this test before putting it back + # into the pipeline. + @pytest.mark.xfail(reason="test case is unstable") def test_RemoveVlanWithRouterInterface(self, dvs, testlog): dvs.setup_db() marker = dvs.add_log_marker() diff --git a/tests/test_warm_reboot.py b/tests/test_warm_reboot.py index fc8ca3debd..4e4e65796a 100644 --- a/tests/test_warm_reboot.py +++ b/tests/test_warm_reboot.py @@ -234,6 +234,7 @@ def ping_new_ips(dvs): dvs.runcmd(['sh', '-c', "ping -c 1 -W 0 -q {}.0.0.{} > /dev/null 2>&1".format(i*4,j+NUM_NEIGH_PER_INTF+2)]) dvs.runcmd(['sh', '-c', "ping6 -c 1 -W 0 -q {}00::{} > /dev/null 2>&1".format(i*4,j+NUM_NEIGH_PER_INTF+2)]) + class TestWarmReboot(object): def test_PortSyncdWarmRestart(self, dvs, testlog): @@ -1794,6 +1795,10 @@ def test_routing_WarmRestart(self, dvs, testlog): intf_tbl._del("{}".format(intfs[2])) time.sleep(2) + # FIXME: This test is extremely unstable and requires several retries + # for it to pass - we need to stabilize this test before putting it back + # into the pipeline. + @pytest.mark.xfail(reason="test case is unstable") def test_system_warmreboot_neighbor_syncup(self, dvs, testlog): appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) diff --git a/warmrestart/warmRestartAssist.cpp b/warmrestart/warmRestartAssist.cpp index 03baa0df63..2a02e5ec95 100644 --- a/warmrestart/warmRestartAssist.cpp +++ b/warmrestart/warmRestartAssist.cpp @@ -17,20 +17,16 @@ const AppRestartAssist::cache_state_map AppRestartAssist::cacheStateMap = {DELETE, "DELETE"} }; -AppRestartAssist::AppRestartAssist(RedisPipeline *pipeline, - const std::string &appName, const std::string &dockerName, - ProducerStateTable *psTable, const uint32_t defaultWarmStartTimerValue): - m_appTable(pipeline, APP_NEIGH_TABLE_NAME, false), +AppRestartAssist::AppRestartAssist(RedisPipeline *pipelineAppDB, const std::string &appName, + const std::string &dockerName, const uint32_t defaultWarmStartTimerValue): + m_pipeLine(pipelineAppDB), m_appName(appName), m_dockerName(dockerName), - m_psTable(psTable), m_warmStartTimer(timespec{0, 0}) { WarmStart::initialize(m_appName, m_dockerName); WarmStart::checkWarmStart(m_appName, m_dockerName); - m_appTableName = m_appTable.getTableName(); - /* * set the default timer value. * If the application instance privides timer value, use it if valid. @@ -66,15 +62,25 @@ AppRestartAssist::AppRestartAssist(RedisPipeline *pipeline, m_warmStartTimer.setInterval(timespec{m_reconcileTimer, 0}); - // Clear the producerstate table to make sure no pending data for the AppTable - m_psTable->clear(); - WarmStart::setWarmStartState(m_appName, WarmStart::INITIALIZED); } } AppRestartAssist::~AppRestartAssist() { + for (auto it = m_appTables.begin(); it != m_appTables.end(); it++) + { + delete (it->second); + } +} + +void AppRestartAssist::registerAppTable(const std::string &tableName, ProducerStateTable *psTable) +{ + m_psTables[tableName] = psTable; + + // Clear the producerstate table to make sure no pending data for the AppTable + psTable->clear(); + m_appTables[tableName] = new Table(m_pipeLine, tableName, false); } // join the field-value strings for straight printing. @@ -108,40 +114,43 @@ AppRestartAssist::cache_state_t AppRestartAssist::getCacheEntryState(const std:: throw std::logic_error("cache entry state is invalid"); } -// Read table from APPDB and append stale flag then insert to cachemap -void AppRestartAssist::readTableToMap() +// Read table(s) from APPDB and append stale flag then insert to cachemap +void AppRestartAssist::readTablesToMap() { vector keys; - m_appTable.getKeys(keys); - FieldValueTuple state(CACHE_STATE_FIELD, ""); - - for (const auto &key: keys) + for (auto it = m_appTables.begin(); it != m_appTables.end(); it++) { - vector fv; + (it->second)->getKeys(keys); + FieldValueTuple state(CACHE_STATE_FIELD, ""); - // if the fieldvalue is empty, skip - if (!m_appTable.get(key, fv)) + for (const auto &key: keys) { - continue; - } + vector fv; + + // if the fieldvalue is empty, skip + if (!(it->second)->get(key, fv)) + { + continue; + } - fv.push_back(state); - setCacheEntryState(fv, STALE); + fv.push_back(state); + setCacheEntryState(fv, STALE); - string s = joinVectorString(fv); + string s = joinVectorString(fv); - SWSS_LOG_INFO("write to cachemap: %s, key: %s, " - "%s", m_appTableName.c_str(), key.c_str(), s.c_str()); + SWSS_LOG_INFO("write to cachemap: %s, key: %s, " + "%s", (it->first).c_str(), key.c_str(), s.c_str()); - // insert to the cache map - appTableCacheMap[key] = fv; + // insert to the cache map + appTableCacheMap[it->first][key] = fv; + } + WarmStart::setWarmStartState(m_appName, WarmStart::RESTORED); + SWSS_LOG_NOTICE("Restored appDB table to %s internal cache map", (it->first).c_str()); } - WarmStart::setWarmStartState(m_appName, WarmStart::RESTORED); - SWSS_LOG_NOTICE("Restored appDB table to internal cache map"); return; } - + /* * Check and insert to CacheMap Logic: * if delete_key: @@ -154,40 +163,39 @@ void AppRestartAssist::readTableToMap() * insert with "NEW" flag. * } */ -void AppRestartAssist::insertToMap(string key, vector fvVector, bool delete_key) +void AppRestartAssist::insertToMap(string tableName, string key, vector fvVector, bool delete_key) { SWSS_LOG_INFO("Received message %s, key: %s, " - "%s, delete = %d", m_appTableName.c_str(), key.c_str(), joinVectorString(fvVector).c_str(), delete_key); - + "%s, delete = %d", tableName.c_str(), key.c_str(), joinVectorString(fvVector).c_str(), delete_key); - auto found = appTableCacheMap.find(key); + auto found = appTableCacheMap[tableName].find(key); if (delete_key) { - SWSS_LOG_NOTICE("%s, delete key: %s, ", m_appTableName.c_str(), key.c_str()); + SWSS_LOG_NOTICE("%s, delete key: %s, ", tableName.c_str(), key.c_str()); /* mark it as DELETE if exist, otherwise, no-op */ - if (found != appTableCacheMap.end()) + if (found != appTableCacheMap[tableName].end()) { setCacheEntryState(found->second, DELETE); } } - else if (found != appTableCacheMap.end()) + else if (found != appTableCacheMap[tableName].end()) { // check only the original vector range (exclude cache-state field/value) if(! contains(found->second, fvVector)) { - SWSS_LOG_NOTICE("%s, found key: %s, new value ", m_appTableName.c_str(), key.c_str()); + SWSS_LOG_NOTICE("%s, found key: %s, new value ", tableName.c_str(), key.c_str()); FieldValueTuple state(CACHE_STATE_FIELD, ""); fvVector.push_back(state); // mark as NEW flag setCacheEntryState(fvVector, NEW); - appTableCacheMap[key] = fvVector; + appTableCacheMap[tableName][key] = fvVector; } else { - SWSS_LOG_INFO("%s, found key: %s, same value", m_appTableName.c_str(), key.c_str()); + SWSS_LOG_INFO("%s, found key: %s, same value", tableName.c_str(), key.c_str()); // mark as SAME flag setCacheEntryState(found->second, SAME); @@ -196,13 +204,12 @@ void AppRestartAssist::insertToMap(string key, vector fvVector, else { // not found, mark the entry as NEW and insert to map - SWSS_LOG_NOTICE("%s, not found key: %s, new", m_appTableName.c_str(), key.c_str()); + SWSS_LOG_NOTICE("%s, not found key: %s, new", tableName.c_str(), key.c_str()); FieldValueTuple state(CACHE_STATE_FIELD, ""); fvVector.push_back(state); setCacheEntryState(fvVector, NEW); - appTableCacheMap[key] = fvVector; + appTableCacheMap[tableName][key] = fvVector; } - return; } @@ -216,42 +223,48 @@ void AppRestartAssist::insertToMap(string key, vector fvVector, */ void AppRestartAssist::reconcile() { + std::string tableName; SWSS_LOG_ENTER(); - for (auto iter = appTableCacheMap.begin(); iter != appTableCacheMap.end(); ++iter ) + for (auto tableIter = appTableCacheMap.begin(); tableIter != appTableCacheMap.end(); ++tableIter) { - string s = joinVectorString(iter->second); - auto state = getCacheEntryState(iter->second); - - if (state == SAME) - { - SWSS_LOG_INFO("%s SAME, key: %s, %s", - m_appTableName.c_str(), iter->first.c_str(), s.c_str()); - continue; - } - else if (state == STALE || state == DELETE) - { - SWSS_LOG_NOTICE("%s STALE/DELETE, key: %s, %s", - m_appTableName.c_str(), iter->first.c_str(), s.c_str()); - - //delete from appDB - m_psTable->del(iter->first); - } - else if (state == NEW) - { - SWSS_LOG_NOTICE("%s NEW, key: %s, %s", - m_appTableName.c_str(), iter->first.c_str(), s.c_str()); - - //add to appDB, exclude the state - iter->second.pop_back(); - m_psTable->set(iter->first, iter->second); - } - else + tableName = tableIter->first; + for (auto it = (tableIter->second).begin(); it != (tableIter->second).end(); ++it) { - throw std::logic_error("cache entry state is invalid"); + string s = joinVectorString(it->second); + auto state = getCacheEntryState(it->second); + + if (state == SAME) + { + SWSS_LOG_INFO("%s SAME, key: %s, %s", + tableName.c_str(), it->first.c_str(), s.c_str()); + continue; + } + else if (state == STALE || state == DELETE) + { + SWSS_LOG_NOTICE("%s STALE/DELETE, key: %s, %s", + tableName.c_str(), it->first.c_str(), s.c_str()); + + //delete from appDB + m_psTables[tableName]->del(it->first); + } + else if (state == NEW) + { + SWSS_LOG_NOTICE("%s NEW, key: %s, %s", + tableName.c_str(), it->first.c_str(), s.c_str()); + + //add to appDB, exclude the state + it->second.pop_back(); + m_psTables[tableName]->set(it->first, it->second); + } + else + { + throw std::logic_error("cache entry state is invalid"); + } } + // reconcile finished, clear the map, mark the warmstart state + appTableCacheMap[tableName].clear(); } - // reconcile finished, clear the map, mark the warmstart state appTableCacheMap.clear(); WarmStart::setWarmStartState(m_appName, WarmStart::RECONCILED); m_warmStartInProgress = false; diff --git a/warmrestart/warmRestartAssist.h b/warmrestart/warmRestartAssist.h index 227815c264..8587d84d4a 100644 --- a/warmrestart/warmRestartAssist.h +++ b/warmrestart/warmRestartAssist.h @@ -50,12 +50,14 @@ namespace swss { * } * } */ +typedef std::map Tables; +typedef std::map ProducerStateTables; + class AppRestartAssist { public: - AppRestartAssist(RedisPipeline *pipeline, - const std::string &appName, const std::string &dockerName, - ProducerStateTable *psTable, const uint32_t defaultWarmStartTimerValue = 0); + AppRestartAssist(RedisPipeline *pipeline, const std::string &appName, + const std::string &dockerName, const uint32_t defaultWarmStartTimerValue = 0); virtual ~AppRestartAssist(); /* @@ -76,13 +78,14 @@ class AppRestartAssist void startReconcileTimer(Select &s); void stopReconcileTimer(Select &s); bool checkReconcileTimer(Selectable *s); - void readTableToMap(void); - void insertToMap(std::string key, std::vector fvVector, bool delete_key); + void readTablesToMap(void); + void insertToMap(std::string tableName, std::string key, std::vector fvVector, bool delete_key); void reconcile(void); bool isWarmStartInProgress(void) { return m_warmStartInProgress; } + void registerAppTable(const std::string &tableName, ProducerStateTable *psTable); private: typedef std::map cache_state_map; @@ -96,16 +99,16 @@ class AppRestartAssist * Precedence ascent order: Default -> loading class with value -> configuration */ static const uint32_t DEFAULT_INTERNAL_TIMER_VALUE = 5; - typedef std::unordered_map> AppTableMap; + typedef std::map>> AppTableMap; // cache map to store temperary application table AppTableMap appTableCacheMap; - Table m_appTable; // table handler - std::string m_dockerName; // docker name of the application - std::string m_appName; // application name - ProducerStateTable *m_psTable; // produce state table handler - std::string m_appTableName; // application table name + RedisPipeline *m_pipeLine; + Tables m_appTables; // app tables + std::string m_dockerName; // docker name of the application + std::string m_appName; // application name + ProducerStateTables m_psTables; // producer state tables bool m_warmStartInProgress; // indicate if warm start is in progress time_t m_reconcileTimer; // reconcile timer value