Skip to content

Commit

Permalink
[Mux] Route handling based on mux status, kernel tunnel support (#1615)
Browse files Browse the repository at this point in the history
Program/Reprogram routes to hardware based on Mux status
Create tunnel interface (tun0) in kernel
Add/remove tunnel routes in kernel
  • Loading branch information
prsunny authored and daall committed Feb 5, 2021
1 parent 0339ae2 commit fc06176
Show file tree
Hide file tree
Showing 9 changed files with 691 additions and 106 deletions.
295 changes: 272 additions & 23 deletions cfgmgr/tunnelmgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,154 @@

#include "logger.h"
#include "tunnelmgr.h"
#include "tokenize.h"
#include "shellcmd.h"
#include "exec.h"

using namespace std;
using namespace swss;

TunnelMgr::TunnelMgr(DBConnector *cfgDb, DBConnector *appDb, std::string tableName) :
Orch(cfgDb, tableName),
m_appIpInIpTunnelTable(appDb, APP_TUNNEL_DECAP_TABLE_NAME)
#define IPINIP "IPINIP"
#define TUNIF "tun0"
#define LOOPBACK_SRC "Loopback3"

static int cmdIpTunnelIfCreate(const swss::TunnelInfo & info, std::string & res)
{
// ip tunnel add {{tunnel intf}} mode ipip local {{dst ip}} remote {{remote ip}}
ostringstream cmd;
cmd << IP_CMD " tunnel add "
<< TUNIF << " mode ipip local "
<< shellquote(info.dst_ip)
<< " remote "
<< shellquote(info.remote_ip);
return swss::exec(cmd.str(), res);
}

static int cmdIpTunnelIfRemove(std::string & res)
{
// ip tunnel del {{tunnel intf}}
ostringstream cmd;
cmd << IP_CMD " tunnel del "
<< TUNIF;
return swss::exec(cmd.str(), res);
}

static int cmdIpTunnelIfUp(std::string & res)
{
// ip link set dev {{tunnel intf}} up
ostringstream cmd;
cmd << IP_CMD " link set dev "
<< TUNIF
<< " up";
return swss::exec(cmd.str(), res);
}

static int cmdIpTunnelIfAddress(const std::string& ip, std::string & res)
{
// ip addr add {{loopback3 ip}} dev {{tunnel intf}}
ostringstream cmd;
cmd << IP_CMD " addr add "
<< shellquote(ip)
<< " dev "
<< TUNIF;
return swss::exec(cmd.str(), res);
}

static int cmdIpTunnelRouteAdd(const std::string& pfx, std::string & res)
{
// ip route add/replace {{ip prefix}} dev {{tunnel intf}}
// Replace route if route already exists
ostringstream cmd;
cmd << IP_CMD " route replace "
<< shellquote(pfx)
<< " dev "
<< TUNIF;
return swss::exec(cmd.str(), res);
}

static int cmdIpTunnelRouteDel(const std::string& pfx, std::string & res)
{
// ip route del {{ip prefix}} dev {{tunnel intf}}
ostringstream cmd;
cmd << IP_CMD " route del "
<< shellquote(pfx)
<< " dev "
<< TUNIF;
return swss::exec(cmd.str(), res);
}

TunnelMgr::TunnelMgr(DBConnector *cfgDb, DBConnector *appDb, const std::vector<std::string> &tableNames) :
Orch(cfgDb, tableNames),
m_appIpInIpTunnelTable(appDb, APP_TUNNEL_DECAP_TABLE_NAME),
m_cfgPeerTable(cfgDb, CFG_PEER_SWITCH_TABLE_NAME)
{
std::vector<string> peer_keys;
m_cfgPeerTable.getKeys(peer_keys);

for (auto i: peer_keys)
{
std::vector<FieldValueTuple> fvs;
m_cfgPeerTable.get(i, fvs);

for (auto j: fvs)
{
if (fvField(j) == "address_ipv4")
{
m_peerIp = fvValue(j);
break;
}
}
}

auto consumerStateTable = new swss::ConsumerStateTable(appDb, APP_TUNNEL_ROUTE_TABLE_NAME,
TableConsumable::DEFAULT_POP_BATCH_SIZE, default_orch_pri);
auto consumer = new Consumer(consumerStateTable, this, APP_TUNNEL_ROUTE_TABLE_NAME);
Orch::addExecutor(consumer);

// Cleanup any existing tunnel intf
std::string res;
cmdIpTunnelIfRemove(res);
}

void TunnelMgr::doTask(Consumer &consumer)
{
SWSS_LOG_ENTER();

string table_name = consumer.getTableName();

auto it = consumer.m_toSync.begin();
while (it != consumer.m_toSync.end())
{
bool task_result = false;
bool task_result = true;

KeyOpFieldsValuesTuple t = it->second;
const vector<FieldValueTuple>& data = kfvFieldsValues(t);

const std::string & op = kfvOp(t);

if (op == SET_COMMAND)
{
for (auto idx : data)
if (table_name == CFG_LOOPBACK_INTERFACE_TABLE_NAME)
{
task_result = doLpbkIntfTask(t);
}
else if (table_name == CFG_TUNNEL_TABLE_NAME)
{
const auto &field = fvField(idx);
const auto &value = fvValue(idx);
if (field == "tunnel_type")
{
if (value == "IPINIP")
{
task_result = doIpInIpTunnelTask(t);
}
}
task_result = doTunnelTask(t);
}
else if (table_name == APP_TUNNEL_ROUTE_TABLE_NAME)
{
task_result = doTunnelRouteTask(t);
}
}
else if (op == DEL_COMMAND)
{
/* TODO: Handle Tunnel delete for other tunnel types */
task_result = doIpInIpTunnelTask(t);
if (table_name == CFG_TUNNEL_TABLE_NAME)
{
task_result = doTunnelTask(t);
}
else if (table_name == APP_TUNNEL_ROUTE_TABLE_NAME)
{
task_result = doTunnelRouteTask(t);
}
}
else
{
Expand All @@ -66,22 +171,166 @@ void TunnelMgr::doTask(Consumer &consumer)
}
}

bool TunnelMgr::doIpInIpTunnelTask(const KeyOpFieldsValuesTuple & t)
bool TunnelMgr::doTunnelTask(const KeyOpFieldsValuesTuple & t)
{
SWSS_LOG_ENTER();

const std::string & TunnelName = kfvKey(t);
const std::string & tunnelName = kfvKey(t);
const std::string & op = kfvOp(t);
TunnelInfo tunInfo;

for (auto fieldValue : kfvFieldsValues(t))
{
const std::string & field = fvField(fieldValue);
const std::string & value = fvValue(fieldValue);
if (field == "dst_ip")
{
tunInfo.dst_ip = value;
}
else if (field == "tunnel_type")
{
tunInfo.type = value;
}
}

if (op == SET_COMMAND)
{
m_appIpInIpTunnelTable.set(TunnelName, kfvFieldsValues(t));
if (tunInfo.type == IPINIP)
{
tunInfo.remote_ip = m_peerIp;

if (!m_peerIp.empty() && !configIpTunnel(tunInfo))
{
return false;
}
else if (m_peerIp.empty())
{
SWSS_LOG_NOTICE("Peer/Remote IP not configured");
}

m_appIpInIpTunnelTable.set(tunnelName, kfvFieldsValues(t));
}
m_tunnelCache[tunnelName] = tunInfo;
}
else
{
m_appIpInIpTunnelTable.del(TunnelName);
auto it = m_tunnelCache.find(tunnelName);

if (it == m_tunnelCache.end())
{
SWSS_LOG_ERROR("Tunnel %s not found", tunnelName.c_str());
return true;
}

tunInfo = it->second;
if (tunInfo.type == IPINIP)
{
m_appIpInIpTunnelTable.del(tunnelName);
}
else
{
SWSS_LOG_WARN("Tunnel %s type %s is not handled", tunnelName.c_str(), tunInfo.type.c_str());
}
m_tunnelCache.erase(tunnelName);
}

SWSS_LOG_NOTICE("Tunnel %s task, op %s", TunnelName.c_str(), op.c_str());
SWSS_LOG_NOTICE("Tunnel %s task, op %s", tunnelName.c_str(), op.c_str());
return true;
}

bool TunnelMgr::doLpbkIntfTask(const KeyOpFieldsValuesTuple & t)
{
SWSS_LOG_ENTER();

vector<string> keys = tokenize(kfvKey(t), config_db_key_delimiter);

/* Skip entry with just interface name. Need to handle only IP prefix*/
if (keys.size() == 1)
{
return true;
}

string alias(keys[0]);
IpPrefix ipPrefix(keys[1]);

m_intfCache[alias] = ipPrefix;

if (alias == LOOPBACK_SRC && !m_tunnelCache.empty())
{
int ret = 0;
std::string res;
ret = cmdIpTunnelIfAddress(ipPrefix.to_string(), res);
if (ret != 0)
{
SWSS_LOG_WARN("Failed to assign IP addr for tun if %s, res %s",
ipPrefix.to_string().c_str(), res.c_str());
}
}

SWSS_LOG_NOTICE("Loopback intf %s saved %s", alias.c_str(), ipPrefix.to_string().c_str());
return true;
}

bool TunnelMgr::doTunnelRouteTask(const KeyOpFieldsValuesTuple & t)
{
SWSS_LOG_ENTER();

const std::string & prefix = kfvKey(t);;
const std::string & op = kfvOp(t);

int ret = 0;
std::string res;
if (op == SET_COMMAND)
{
ret = cmdIpTunnelRouteAdd(prefix, res);
if (ret != 0)
{
SWSS_LOG_WARN("Failed to add route %s, res %s", prefix.c_str(), res.c_str());
}
}
else
{
ret = cmdIpTunnelRouteDel(prefix, res);
if (ret != 0)
{
SWSS_LOG_WARN("Failed to del route %s, res %s", prefix.c_str(), res.c_str());
}
}

SWSS_LOG_INFO("Route updated to kernel %s, op %s", prefix.c_str(), op.c_str());
return true;
}


bool TunnelMgr::configIpTunnel(const TunnelInfo& tunInfo)
{
int ret = 0;
std::string res;

ret = cmdIpTunnelIfCreate(tunInfo, res);
if (ret != 0)
{
SWSS_LOG_WARN("Failed to create IP tunnel if (dst ip: %s, peer ip %s), res %s",
tunInfo.dst_ip.c_str(),tunInfo.remote_ip.c_str(), res.c_str());
}

ret = cmdIpTunnelIfUp(res);
if (ret != 0)
{
SWSS_LOG_WARN("Failed to enable IP tunnel intf (dst ip: %s, peer ip %s), res %s",
tunInfo.dst_ip.c_str(),tunInfo.remote_ip.c_str(), res.c_str());
}

auto it = m_intfCache.find(LOOPBACK_SRC);
if (it != m_intfCache.end())
{
ret = cmdIpTunnelIfAddress(it->second.to_string(), res);
if (ret != 0)
{
SWSS_LOG_WARN("Failed to assign IP addr for tun if %s, res %s",
it->second.to_string().c_str(), res.c_str());
}
}

return true;
}
20 changes: 18 additions & 2 deletions cfgmgr/tunnelmgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,34 @@

namespace swss {

struct TunnelInfo
{
std::string type;
std::string dst_ip;
std::string remote_ip;
};

class TunnelMgr : public Orch
{
public:
TunnelMgr(DBConnector *cfgDb, DBConnector *appDb, std::string tableName);
TunnelMgr(DBConnector *cfgDb, DBConnector *appDb, const std::vector<std::string> &tableNames);
using Orch::doTask;

private:
void doTask(Consumer &consumer);

bool doIpInIpTunnelTask(const KeyOpFieldsValuesTuple & t);
bool doTunnelTask(const KeyOpFieldsValuesTuple & t);
bool doTunnelRouteTask(const KeyOpFieldsValuesTuple & t);
bool doLpbkIntfTask(const KeyOpFieldsValuesTuple & t);

bool configIpTunnel(const TunnelInfo& info);

ProducerStateTable m_appIpInIpTunnelTable;
Table m_cfgPeerTable;

std::map<std::string, TunnelInfo > m_tunnelCache;
std::map<std::string, IpPrefix> m_intfCache;
std::string m_peerIp;
};

}
6 changes: 5 additions & 1 deletion cfgmgr/tunnelmgrd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,15 @@ int main(int argc, char **argv)

try
{
vector<string> cfgTunTables = {
CFG_TUNNEL_TABLE_NAME,
CFG_LOOPBACK_INTERFACE_TABLE_NAME
};

DBConnector cfgDb("CONFIG_DB", 0);
DBConnector appDb("APPL_DB", 0);

TunnelMgr tunnelmgr(&cfgDb, &appDb, CFG_TUNNEL_TABLE_NAME);
TunnelMgr tunnelmgr(&cfgDb, &appDb, cfgTunTables);

std::vector<Orch *> cfgOrchList = {&tunnelmgr};

Expand Down
Loading

0 comments on commit fc06176

Please sign in to comment.