Skip to content

Commit

Permalink
portsyncd: Adding VLAN sync logic for portsyncd (sonic-net#36)
Browse files Browse the repository at this point in the history
- Add g_portSet and g_init global variable to indicate if all the
    host interfaces of the front panel ports are created
- Add VLAN logic to deal with netlink messages related to VLAN
- Bring up front panel port based host interface whenever it is
    created
- Bring up VLAN interfaces whenever vlan_interfaces file is provided
    after all front panel port based host interfaces are created
  • Loading branch information
stcheng committed Jul 11, 2016
1 parent 088a727 commit f00fad6
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 79 deletions.
120 changes: 90 additions & 30 deletions portsyncd/linksync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,17 @@
#include "portsyncd/linksync.h"

#include <iostream>
#include <set>

using namespace std;
using namespace swss;

#define VLAN_DRV_NAME "bridge"
#define TEAM_DRV_NAME "team"

extern set<string> g_portSet;
extern bool g_init;

LinkSync::LinkSync(DBConnector *db) :
m_portTableProducer(db, APP_PORT_TABLE_NAME),
m_vlanTableProducer(db, APP_VLAN_TABLE_NAME),
Expand All @@ -24,63 +31,116 @@ LinkSync::LinkSync(DBConnector *db) :
m_vlanTableConsumer(db, APP_VLAN_TABLE_NAME),
m_lagTableConsumer(db, APP_LAG_TABLE_NAME)
{
/* See the comments for g_portSet in linksync.h */
for (string port : g_portSet)
{
vector<FieldValueTuple> temp;
if (m_portTableConsumer.get(port, temp))
{
for (auto it : temp)
{
if (fvField(it) == "admin_status")
{
g_portSet.erase(port);
break;
}
}
}
}
}

void LinkSync::onMsg(int nlmsg_type, struct nl_object *obj)
{
if ((nlmsg_type != RTM_NEWLINK) && (nlmsg_type != RTM_GETLINK) &&
(nlmsg_type != RTM_DELLINK))
if ((nlmsg_type != RTM_NEWLINK) && (nlmsg_type != RTM_DELLINK))
return;

struct rtnl_link *link = (struct rtnl_link *)obj;
string key = rtnl_link_get_name(link);
if (nlmsg_type == RTM_DELLINK) /* Will be sync by other application */

if (key == "lo" || key == "eth0" || key == "docker0" || key == "bcm0")
return;

bool admin_state = rtnl_link_get_flags(link) & IFF_UP;
bool oper_state = rtnl_link_get_flags(link) & IFF_LOWER_UP;
unsigned int flags = rtnl_link_get_flags(link);
bool admin_state = flags & IFF_UP;
bool oper_state = flags & IFF_LOWER_UP;
unsigned int mtu = rtnl_link_get_mtu(link);

char addrStr[MAX_ADDR_SIZE+1] = {0};
nl_addr2str(rtnl_link_get_addr(link), addrStr, MAX_ADDR_SIZE);

unsigned int ifindex = rtnl_link_get_ifindex(link);
int master = rtnl_link_get_master(link);
char *type = rtnl_link_get_type(link);

cout << "Receive nlmsg from portsyncd: type:" << nlmsg_type << " key:" << key
<< " admin_state:" << admin_state << " oper_state:" << oper_state
<< " addr:" << addrStr << " ifindex:" << ifindex << " master:" << master;
if (type)
cout << " type:" << type;
cout << endl;

/* Insert or update the ifindex to key map */
m_ifindexNameMap[ifindex] = key;

/* Will be dealt by teamsyncd */
if (type && !strcmp(type, TEAM_DRV_NAME))
return;

vector<FieldValueTuple> fvVector;

/* VLAN member: A separate entry in VLAN_TABLE will be inserted */
if (master)
{
key = m_ifindexNameMap[master] + ":" + key;

if (nlmsg_type == RTM_DELLINK)
m_vlanTableProducer.del(key);
else
{
FieldValueTuple t("tagging_mode", "untagged");
fvVector.push_back(t);

m_vlanTableProducer.set(key, fvVector);
}
}

FieldValueTuple a("admin_status", admin_state ? "up" : "down");
FieldValueTuple o("oper_status", oper_state ? "up" : "down");
FieldValueTuple m("mtu", to_string(mtu));
fvVector.push_back(a);
fvVector.push_back(o);
fvVector.push_back(m);

/* VLAN interfaces: Check if the type is bridge */
if (type && !strcmp(type, VLAN_DRV_NAME))
{
if (nlmsg_type == RTM_DELLINK)
m_vlanTableProducer.del(key);
else
m_vlanTableProducer.set(key, fvVector);

return;
}

/* front panel interfaces: Check if the port is in the PORT_TABLE */
vector<FieldValueTuple> temp;
if (m_lagTableConsumer.get(key, temp))
m_lagTableProducer.set(key, fvVector);
else if (m_vlanTableConsumer.get(key, temp))
m_vlanTableProducer.set(key, fvVector);
else if (m_portTableConsumer.get(key, temp))
if (m_portTableConsumer.get(key, temp))
{
/*
* If the port entry is just initialized without having admin_status/oper_status field,
* we manually bring up the port using ifup --force command.
*/
bool init = true;
for (auto it : temp)
{
if (fvField(it) == "admin_status")
{
init = false;
break;
}
}
/* TODO: When port is removed from the kernel */
if (nlmsg_type == RTM_DELLINK)
return;

/*
* TODO: Parse /etc/network/interfaces file to bring up port and configure corresponding IP.
*/
if (init)
if (!g_init && g_portSet.find(key) != g_portSet.end())
{
/* Bring up the front panel port as the first place*/
system(("/sbin/ifup --force " + key).c_str());
g_portSet.erase(key);
}
else
{
m_portTableProducer.set(key, fvVector);
}

return;
}
/* else discard managment or untracked netdev state */

cerr << "Unhandled netlink message received." << endl;
}
4 changes: 4 additions & 0 deletions portsyncd/linksync.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "producertable.h"
#include "netmsg.h"

#include <map>

namespace swss {

class LinkSync : public NetMsg
Expand All @@ -19,6 +21,8 @@ class LinkSync : public NetMsg
private:
ProducerTable m_portTableProducer, m_vlanTableProducer, m_lagTableProducer;
Table m_portTableConsumer, m_vlanTableConsumer, m_lagTableConsumer;

std::map<unsigned int, std::string> m_ifindexNameMap;
};

}
Expand Down
158 changes: 109 additions & 49 deletions portsyncd/portsyncd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,50 +14,136 @@
#include <set>
#include <map>

#define DEFAULT_PORT_CONFIG_FILE "port_config.ini"
#define DEFAULT_PORT_CONFIG_FILE "port_config.ini"
#define DEFAULT_VLAN_INTERFACES_FILE "/etc/network/interfaces.d/vlan_interfaces"

using namespace std;
using namespace swss;

void usage(char **argv)
/*
* This m_portSet contains all the front panel ports that the corresponding
* host interfaces needed to be created. When this LinkSync class is
* initialized, we check the database to see if some of the ports' host
* interfaces are already created and remove them from this set. We will
* remove the rest of the ports in the set when receiving the first netlink
* message indicating that the host interfaces are created. After the set
* is empty, we send out the signal ConfigDone and bring up VLAN interfaces
* when the vlan_interfaces file exists. g_init is used to limit the command
* to be run only once.
*/
set<string> g_portSet;
bool g_init = false;

void usage()
{
cout << "Usage: " << argv[0] << " [-f config_file]" << endl;
cout << "Usage: portsyncd [-p port_config.ini] [-v vlan_interfaces]" << endl;
cout << " -p port_config.ini: MANDATORY import port lane mapping" << endl;
cout << " default: port_config.ini" << endl;
cout << " -v vlan_interfaces: import VLAN interfaces configuration file" << endl;
cout << " default: /etc/network/interfaces.d/vlan_interfaces" << endl;
}

void handlePortConfigFile(ProducerTable &p, string file);
void handleVlanIntfFile(string file);

int main(int argc, char **argv)
{
int opt;
string port_config_file = DEFAULT_PORT_CONFIG_FILE;
string vlan_interfaces_file = DEFAULT_VLAN_INTERFACES_FILE;

while ((opt = getopt(argc, argv, "f:h")) != -1 )
while ((opt = getopt(argc, argv, "p:v:h")) != -1 )
{
switch (opt)
{
case 'f':
case 'p':
port_config_file.assign(optarg);
break;
case 'v':
vlan_interfaces_file.assign(optarg);
break;
case 'h':
usage(argv);
usage();
return 1;
default: /* '?' */
usage(argv);
usage();
return EXIT_FAILURE;
}
}

DBConnector db(0, "localhost", 6379, 0);
ProducerTable p(&db, APP_PORT_TABLE_NAME);

ifstream infile(port_config_file);
if (!infile.is_open())
LinkSync sync(&db);
NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, &sync);
NetDispatcher::getInstance().registerMessageHandler(RTM_DELLINK, &sync);

try
{
NetLink netlink;
Select s;

netlink.registerGroup(RTNLGRP_LINK);
cout << "Listen to link messages..." << endl;
netlink.dumpRequest(RTM_GETLINK);

handlePortConfigFile(p, port_config_file);

s.addSelectable(&netlink);
while (true)
{
Selectable *temps;
int tempfd, ret;
ret = s.select(&temps, &tempfd, 1);

if (ret == Select::ERROR)
{
cerr << "Error had been returned in select" << endl;
continue;
}

if (ret == Select::TIMEOUT)
{
if (!g_init && g_portSet.empty())
{
/*
* After finishing reading port configuration file and
* creating all host interfaces, this daemon shall send
* out a signal to orchagent indicating port initialization
* procedure is done and other application could start
* syncing.
*/
FieldValueTuple finish_notice("lanes", "0");
vector<FieldValueTuple> attrs = { finish_notice };
p.set("ConfigDone", attrs);

handleVlanIntfFile(vlan_interfaces_file);

g_init = true;
}
}
}
}
catch (...)
{
cerr << "Port configuration file not found! " << port_config_file << endl;
usage(argv);
cerr << "Exception had been thrown in deamon" << endl;
return EXIT_FAILURE;
}

return 1;
}

void handlePortConfigFile(ProducerTable &p, string file)
{
cout << "Read port configuration file..." << endl;

ifstream infile(file);
if (!infile.is_open())
{
usage();
throw "Port configuration file not found!";
}

string line;
while (getline(infile, line))
{
Expand All @@ -73,48 +159,22 @@ int main(int argc, char **argv)
FieldValueTuple lanes_attr("lanes", lanes);
vector<FieldValueTuple> attrs = { lanes_attr };
p.set(alias, attrs);

g_portSet.insert(alias);
}

infile.close();
}

/*
* After finishing reading port configuration file, this daemon shall send
* out a signal to orchagent indicating port initialization procedure is
* done and other application could start syncing.
*/
FieldValueTuple finish_notice("lanes", "0");
vector<FieldValueTuple> attrs = { finish_notice };
p.set("ConfigDone", attrs);

LinkSync sync(&db);
NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, &sync);
NetDispatcher::getInstance().registerMessageHandler(RTM_DELLINK, &sync);

while (true)
void handleVlanIntfFile(string file)
{
ifstream infile(file);
if (infile.good())
{
try
{
NetLink netlink;
Select s;

netlink.registerGroup(RTNLGRP_LINK);
cout << "Listen to link messages..." << endl;
netlink.dumpRequest(RTM_GETLINK);

s.addSelectable(&netlink);
while (true)
{
Selectable *temps;
int tempfd;
s.select(&temps, &tempfd);
}
}
catch (...)
{
cerr << "Exception had been thrown in deamon" << endl;
return EXIT_FAILURE;
}
/* Bring up VLAN interfaces when vlan_interfaces_file exists */
string cmd = "/sbin/ifup --all --force --interfaces " + file;
int ret = system(cmd.c_str());
if (!ret)
cerr << "Execute command returns non-zero value! " << cmd << endl;
}

return 1;
}

0 comments on commit f00fad6

Please sign in to comment.