Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support ConfigDB neighbor configuration, introduce nbrmgr daemon #693

Merged
merged 6 commits into from
Nov 28, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ cfgmgr/intfmgrd
cfgmgr/vlanmgrd
cfgmgr/buffermanager
cfgmgr/vrfmgrd
cfgmgr/nbrmgrd
neighsyncd/neighsyncd
portsyncd/portsyncd
orchagent/orchagent
Expand Down
9 changes: 8 additions & 1 deletion cfgmgr/Makefile.am
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/orchagent -I $(top_srcdir)/warmrestart
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
bin_PROGRAMS = vlanmgrd teammgrd portmgrd intfmgrd buffermgrd vrfmgrd nbrmgrd

if DEBUG
DBGFLAGS = -ggdb -DDEBUG
Expand Down Expand Up @@ -38,3 +40,8 @@ vrfmgrd_SOURCES = vrfmgrd.cpp vrfmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_
vrfmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI)
vrfmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI)
vrfmgrd_LDADD = -lswsscommon

nbrmgrd_SOURCES = nbrmgrd.cpp nbrmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h
nbrmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(LIBNL_CFLAGS)
nbrmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(LIBNL_CPPFLAGS)
nbrmgrd_LDADD = -lswsscommon $(LIBNL_LIBS)
210 changes: 210 additions & 0 deletions cfgmgr/nbrmgr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/if.h>
#include <unistd.h>

#include "logger.h"
#include "tokenize.h"
#include "ipprefix.h"
#include "macaddress.h"
#include "nbrmgr.h"
#include "exec.h"
#include "shellcmd.h"

using namespace swss;

#define VLAN_PREFIX "Vlan"
#define LAG_PREFIX "PortChannel"

static bool send_message(struct nl_msg *msg)
{
struct nl_sock *sk = nl_socket_alloc();
if (!sk)
{
SWSS_LOG_ERROR("Netlink socket alloc failed");
return false;
}

nl_connect(sk, NETLINK_ROUTE);
prsunny marked this conversation as resolved.
Show resolved Hide resolved
bool rc = true;

if (nl_send_auto(sk, msg) < 0)
{
SWSS_LOG_ERROR("Netlink send message failed");
prsunny marked this conversation as resolved.
Show resolved Hide resolved
rc = false;
}

nlmsg_free(msg);
nl_socket_free(sk);

return rc;
}

NbrMgr::NbrMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector<string> &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_stateIntfTable(stateDb, STATE_INTERFACE_TABLE_NAME)
{

}

bool NbrMgr::isIntfStateOk(const string &alias)
{
vector<FieldValueTuple> temp;

if (!alias.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX))
{
if (m_stateVlanTable.get(alias, temp))
{
SWSS_LOG_DEBUG("Vlan %s is ready", alias.c_str());
return true;
}
}
else if (!alias.compare(0, strlen(LAG_PREFIX), LAG_PREFIX))
{
if (m_stateLagTable.get(alias, temp))
{
SWSS_LOG_DEBUG("Lag %s is ready", alias.c_str());
return true;
}
}
else if (m_statePortTable.get(alias, temp))
{
SWSS_LOG_DEBUG("Port %s is ready", alias.c_str());
return true;
}

return false;
}

bool NbrMgr::setNeighbor(const string& alias, const IpAddress& ip, const MacAddress& mac)
{
SWSS_LOG_ENTER();

struct nl_msg *msg = nlmsg_alloc();
if (!msg)
{
SWSS_LOG_ERROR("Netlink message alloc failed for '%s'", ip.to_string().c_str());
return false;
}

auto flags = (NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_REPLACE);

nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, RTM_NEWNEIGH, 0, flags);
prsunny marked this conversation as resolved.
Show resolved Hide resolved

struct ndmsg *nd_msg = static_cast<struct ndmsg *>
(nlmsg_reserve(msg, sizeof(struct ndmsg), NLMSG_ALIGNTO));
prsunny marked this conversation as resolved.
Show resolved Hide resolved
memset(nd_msg, 0, sizeof(struct ndmsg));

nd_msg->ndm_ifindex = if_nametoindex(alias.c_str());
prsunny marked this conversation as resolved.
Show resolved Hide resolved

auto addr_len = ip.isV4()? sizeof(struct in_addr) : sizeof(struct in6_addr);

struct rtattr *rta = static_cast<struct rtattr *>
(nlmsg_reserve(msg, sizeof(struct rtattr) + addr_len, NLMSG_ALIGNTO));
prsunny marked this conversation as resolved.
Show resolved Hide resolved

rta->rta_type = NDA_DST;
rta->rta_len = static_cast<short>(RTA_LENGTH(addr_len));

nd_msg->ndm_type = RTN_UNICAST;
auto ip_addr = ip.getIp();

if (ip.isV4())
{
nd_msg->ndm_family = AF_INET;
memcpy(RTA_DATA(rta), &ip_addr.ip_addr.ipv4_addr, addr_len);
}
else
{
nd_msg->ndm_family = AF_INET6;
memcpy(RTA_DATA(rta), &ip_addr.ip_addr.ipv6_addr, addr_len);
}

if (!mac)
{
/*
* If mac is not provided, expected to resolve the MAC
*/
nd_msg->ndm_state = NUD_DELAY;
nd_msg->ndm_flags = NTF_USE;
prsunny marked this conversation as resolved.
Show resolved Hide resolved

SWSS_LOG_INFO("Resolve request for '%s'", ip.to_string().c_str());
}
else
{
SWSS_LOG_INFO("Set mac address '%s'", mac.to_string().c_str());

nd_msg->ndm_state = NUD_PERMANENT;

auto mac_len = ETHER_ADDR_LEN;
auto mac_addr = mac.getMac();

struct rtattr *rta = static_cast<struct rtattr *>
(nlmsg_reserve(msg, sizeof(struct rtattr) + mac_len, NLMSG_ALIGNTO));
prsunny marked this conversation as resolved.
Show resolved Hide resolved

rta->rta_type = NDA_LLADDR;
rta->rta_len = static_cast<short>(RTA_LENGTH(mac_len));
memcpy(RTA_DATA(rta), mac_addr, mac_len);
}

return send_message(msg);
}

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

auto it = consumer.m_toSync.begin();
while (it != consumer.m_toSync.end())
{
KeyOpFieldsValuesTuple t = it->second;
vector<string> keys = tokenize(kfvKey(t), config_db_key_delimiter);
const vector<FieldValueTuple>& data = kfvFieldsValues(t);

string alias(keys[0]);
IpAddress ip(keys[1]);
string op = kfvOp(t);
MacAddress mac;

for (auto idx : data)
{
const auto &field = fvField(idx);
const auto &value = fvValue(idx);
if (field == "neigh")
{
mac = value;
prsunny marked this conversation as resolved.
Show resolved Hide resolved
}
}

if (op == SET_COMMAND)
{
if (!isIntfStateOk(alias))
{
SWSS_LOG_DEBUG("Interface is not yet ready, skipping '%s'", kfvKey(t).c_str());
it++;
continue;
}

if (!setNeighbor(alias, ip, mac))
{
SWSS_LOG_ERROR("Neigh entry add failed for '%s'", kfvKey(t).c_str());
}
else
{
SWSS_LOG_NOTICE("Neigh entry added for '%s'", kfvKey(t).c_str());
}
}
else if (op == DEL_COMMAND)
{
SWSS_LOG_NOTICE("Not yet implemented, key '%s'", kfvKey(t).c_str());
}
else
{
SWSS_LOG_ERROR("Unknown operation: '%s'", op.c_str());
}

it = consumer.m_toSync.erase(it);
}
}
34 changes: 34 additions & 0 deletions cfgmgr/nbrmgr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#ifndef __NBRMGR__
#define __NBRMGR__

#include <string>
#include <map>
#include <set>

#include "dbconnector.h"
#include "producerstatetable.h"
#include "orch.h"
#include "netmsg.h"

using namespace std;

namespace swss {

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

private:
bool isIntfStateOk(const string &alias);
bool setNeighbor(const string& alias, const IpAddress& ip, const MacAddress& mac);

void doTask(Consumer &consumer);

Table m_statePortTable, m_stateLagTable, m_stateVlanTable, m_stateIntfTable;
};

}

#endif // __NBRMGR__
88 changes: 88 additions & 0 deletions cfgmgr/nbrmgrd.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#include <unistd.h>
#include <vector>
#include <mutex>
#include <fstream>
#include <iostream>

#include "select.h"
#include "exec.h"
#include "schema.h"
#include "nbrmgr.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;
/* Global database mutex */
mutex gDbMutex;

int main(int argc, char **argv)
{
Logger::linkToDbNative("nbrmgrd");
SWSS_LOG_ENTER();

SWSS_LOG_NOTICE("--- Starting nbrmgrd ---");

try
{
vector<string> cfg_nbr_tables = {
CFG_NEIGH_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);

NbrMgr nbrmgr(&cfgDb, &appDb, &stateDb, cfg_nbr_tables);

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

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)
{
nbrmgr.doTask();
continue;
}

auto *c = (Executor *)sel;
c->execute();
}
}
catch(const std::exception &e)
{
SWSS_LOG_ERROR("Runtime error: %s", e.what());
}
return -1;
}
5 changes: 5 additions & 0 deletions doc/swss-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,11 @@ Status: ready
vni = uint24 ; vni id, defined for tunnel map
vlan = "Vlan"vlan_id ; name of the existing vlan interface

### NEIGH_TABLE
; Stores the neighbors. Defines static configuration of neighbor entries. If mac address is not specified, implementation shall resolve the mac-address for the neighbor IP.
key = NEIGH|PORT_TABLE.name / VLAN_INTF_TABLE.name / LAG_INTF_TABLE.name|prefix
neigh = 12HEXDIG ; mac address of the neighbor (optional)
family = "IPv4" / "IPv6" ; address family

## State DB schema

Expand Down