diff --git a/portsyncd/linksync.cpp b/portsyncd/linksync.cpp index aa6354ecc778..e6073389f970 100644 --- a/portsyncd/linksync.cpp +++ b/portsyncd/linksync.cpp @@ -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), @@ -24,24 +31,79 @@ 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)); @@ -49,38 +111,36 @@ void LinkSync::onMsg(int nlmsg_type, struct nl_object *obj) 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; } diff --git a/portsyncd/linksync.h b/portsyncd/linksync.h index 1a22ab7de647..dcb183a0c736 100644 --- a/portsyncd/linksync.h +++ b/portsyncd/linksync.h @@ -5,6 +5,8 @@ #include "producertable.h" #include "netmsg.h" +#include <map> + namespace swss { class LinkSync : public NetMsg @@ -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; }; } diff --git a/portsyncd/portsyncd.cpp b/portsyncd/portsyncd.cpp index ee851a70b602..9cfb703da756 100644 --- a/portsyncd/portsyncd.cpp +++ b/portsyncd/portsyncd.cpp @@ -14,33 +14,59 @@ #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; } } @@ -48,16 +74,76 @@ int main(int argc, char **argv) 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)) { @@ -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; }