Skip to content

Commit

Permalink
Support ConfigDB neighbor configuration, introduce nbrmgr daemon (#693)
Browse files Browse the repository at this point in the history
* Introduce neighbor manager daemon

* Corrected the ConfigDB name

* Modified to use libnl3, address review comments

* Retain nl socket after use, address review comments

* Check invalid mac configuration, catch exception
  • Loading branch information
prsunny committed Nov 28, 2018
1 parent 8522390 commit 705b092
Show file tree
Hide file tree
Showing 6 changed files with 400 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ cfgmgr/portmgrd
cfgmgr/teammgrd
cfgmgr/vlanmgrd
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)
263 changes: 263 additions & 0 deletions cfgmgr/nbrmgr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
#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_sock *sk, struct nl_msg *msg)
{
bool rc = false;
int err = 0;

do
{
if (!sk)
{
SWSS_LOG_ERROR("Netlink socket null pointer");
break;
}

if ((err = nl_send_auto(sk, msg)) < 0)
{
SWSS_LOG_ERROR("Netlink send message failed, error '%s'", nl_geterror(err));
break;
}

rc = true;
} while(0);

nlmsg_free(msg);
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)
{
int err = 0;

m_nl_sock = nl_socket_alloc();
if (!m_nl_sock)
{
SWSS_LOG_ERROR("Netlink socket alloc failed");
}
else if ((err = nl_connect(m_nl_sock, NETLINK_ROUTE)) < 0)
{
SWSS_LOG_ERROR("Netlink socket connect failed, error '%s'", nl_geterror(err));
}
}

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);

struct nlmsghdr *hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, RTM_NEWNEIGH, 0, flags);
if (!hdr)
{
SWSS_LOG_ERROR("Netlink message header alloc failed for '%s'", ip.to_string().c_str());
nlmsg_free(msg);
return false;
}

struct ndmsg *nd_msg = static_cast<struct ndmsg *>
(nlmsg_reserve(msg, sizeof(struct ndmsg), NLMSG_ALIGNTO));
if (!nd_msg)
{
SWSS_LOG_ERROR("Netlink ndmsg reserve failed for '%s'", ip.to_string().c_str());
nlmsg_free(msg);
return false;
}

memset(nd_msg, 0, sizeof(struct ndmsg));

nd_msg->ndm_ifindex = if_nametoindex(alias.c_str());

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));
if (!rta)
{
SWSS_LOG_ERROR("Netlink rtattr (IP) failed for '%s'", ip.to_string().c_str());
nlmsg_free(msg);
return false;
}

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;

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));
if (!rta)
{
SWSS_LOG_ERROR("Netlink rtattr (MAC) failed for '%s'", ip.to_string().c_str());
nlmsg_free(msg);
return false;
}

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(m_nl_sock, 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;
bool invalid_mac = false;

for (auto idx : data)
{
const auto &field = fvField(idx);
const auto &value = fvValue(idx);
if (field == "neigh")
{
try
{
mac = value;
}
catch (const std::invalid_argument& e)
{
SWSS_LOG_ERROR("Invalid Mac addr '%s' for '%s'", value.c_str(), kfvKey(t).c_str());
invalid_mac = true;
break;
}
}
}

if (invalid_mac)
{
it = consumer.m_toSync.erase(it);
continue;
}

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);
}
}
35 changes: 35 additions & 0 deletions cfgmgr/nbrmgr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#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;
struct nl_sock *m_nl_sock;
};

}

#endif // __NBRMGR__
Loading

0 comments on commit 705b092

Please sign in to comment.