diff --git a/cfgmgr/Makefile.am b/cfgmgr/Makefile.am index 7178cad1c6..bd8bf7a43a 100644 --- a/cfgmgr/Makefile.am +++ b/cfgmgr/Makefile.am @@ -5,6 +5,11 @@ LIBNL_LIBS = -lnl-genl-3 -lnl-route-3 -lnl-3 bin_PROGRAMS = vlanmgrd teammgrd portmgrd intfmgrd buffermgrd vrfmgrd nbrmgrd vxlanmgrd sflowmgrd natmgrd + +if HAVE_L2MCD +bin_PROGRAMS += l2mcmgrd +endif + if DEBUG DBGFLAGS = -ggdb -DDEBUG else @@ -51,6 +56,13 @@ vxlanmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) vxlanmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) vxlanmgrd_LDADD = -lswsscommon +if HAVE_L2MCD +l2mcmgrd_SOURCES =l2mcmgrd.cpp l2mcmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h +l2mcmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(COV_CFLAGS) $(ASAN_CFLAGS) +l2mcmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(COV_CFLAGS) $(ASAN_CFLAGS) +l2mcmgrd_LDADD = -lswsscommon $(COV_LDFLAGS) $(ASAN_LDFLAGS) +endif + sflowmgrd_SOURCES = sflowmgrd.cpp sflowmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h sflowmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) sflowmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) diff --git a/cfgmgr/l2mcmgr.cpp b/cfgmgr/l2mcmgr.cpp new file mode 100644 index 0000000000..2aa6c77655 --- /dev/null +++ b/cfgmgr/l2mcmgr.cpp @@ -0,0 +1,707 @@ +/* + * Copyright 2019 Broadcom. All rights reserved. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * cfgmgr/l2mcmgr.cpp + */ + +#include "exec.h" +#include "l2mcmgr.h" +#include "tokenize.h" +#include +#include +#include +#include + +using namespace std; +using namespace swss; + +L2McMgr::L2McMgr(DBConnector *confDb, DBConnector *applDb, DBConnector *statDb, + const vector &tables) : + Orch(tables), + m_cfgL2McGlobalTable(confDb, CFG_L2MC_TABLE_NAME), + m_cfgL2McStaticTable(confDb, CFG_L2MC_STATIC_TABLE_NAME), + m_cfgL2McMrouterTable(confDb, CFG_L2MC_MROUTER_TABLE_NAME), + m_stateVlanMemberTable(statDb, STATE_VLAN_MEMBER_TABLE_NAME), + m_stateInterfaceTableName(statDb,STATE_INTERFACE_TABLE_NAME), + m_statel2mcdLocalMemberTable(applDb, APP_L2MC_MEMBER_TABLE_NAME), + m_cfgLagMemberTable(confDb, CFG_LAG_MEMBER_TABLE_NAME), + m_stateLagTable(statDb, STATE_LAG_TABLE_NAME), + m_appPortTable(applDb, APP_PORT_TABLE_NAME), + m_appLagTable(applDb,APP_LAG_TABLE_NAME) +{ + SWSS_LOG_ENTER(); + SWSS_LOG_INFO("Add REDIS DB L2mc entry notification support"); + m_l2mcNotificationConsumer = new swss::NotificationConsumer(statDb, "L2MC_NOTIFICATIONS_REMOTE"); + auto l2mcNotificatier = new Notifier(m_l2mcNotificationConsumer, this, "L2MC_NOTIFICATIONS_REMOTE"); + Orch::addExecutor(l2mcNotificatier); + + SWSS_LOG_INFO("Add REDIS DB mrouter notification support"); + m_l2mcMrouterNotificationConsumer = new swss::NotificationConsumer(statDb, "L2MC_MROUTER_NOTIFICATIONS_REMOTE"); + auto l2mcMrouterNotificatier = new Notifier(m_l2mcMrouterNotificationConsumer, this, "L2MC_MROUTER_NOTIFICATIONS_REMOTE"); + Orch::addExecutor(l2mcMrouterNotificatier); + +} + +void L2McMgr::doTask(Consumer &consumer) +{ + auto table = consumer.getTableName(); + + if (table == CFG_L2MC_TABLE_NAME) + { + doL2McGlobalTask(consumer); + } + else if (table == CFG_L2MC_STATIC_TABLE_NAME) + { + doL2McStaticEntryTask(consumer); + } + else if (table ==STATE_VLAN_MEMBER_TABLE_NAME) + { + doL2McVlanMemUpdateTask(consumer); + } + else if (table == STATE_INTERFACE_TABLE_NAME) + { + doL2McL3InterfaceUpdateTask(consumer); + } + else if (table == CFG_L2MC_MROUTER_TABLE_NAME) + { + doL2McMrouterUpdateTask(consumer); + } + else if (table == CFG_LAG_MEMBER_TABLE_NAME) + { + doL2McLagMemberUpdateTask(consumer); + } + else if ((table == STATE_LAG_TABLE_NAME)|| (table == STATE_PORT_TABLE_NAME)) + { + doL2McInterfaceUpdateTask(consumer); + } + else + { + SWSS_LOG_ERROR("Invalid table %s", table.c_str()); + } +} + +void L2McMgr::doL2McGlobalTask(Consumer &consumer) +{ + L2MCD_CONFIG_MSG msg; + int j=0; + SWSS_LOG_ENTER(); + vector l2mcLocalMemKeys; + + memset(&msg, 0, sizeof(L2MCD_CONFIG_MSG)); + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + string vlanKey = key.substr(4); // Remove Vlan prefix + int vlan_id = stoi(vlanKey.c_str()); + msg.cmd_code=0; + msg.query_interval=125; + msg.query_max_response_time=10; + msg.last_member_query_interval=1000; + msg.version=2; + if (op == SET_COMMAND) + { + msg.op_code = L2MCD_OP_ENABLE; + msg.vlan_id = vlan_id; + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "enabled") + { + msg.enabled = (fvValue(i) == "true") ? 1 : 0; + } + else if (fvField(i) == "querier") + { + msg.querier = (fvValue(i) == "true") ? 1 : 0; + } + else if (fvField(i) == "fast-leave") + { + msg.fast_leave = (fvValue(i) == "true") ? 1 : 0; + } + else if (fvField(i) == "version") + { + msg.version = stoi(fvValue(i).c_str()); + } + else if (fvField(i) == "query-interval") + { + msg.query_interval = stoi(fvValue(i).c_str()); + } + else if (fvField(i) == "last-member-query-interval") + { + msg.last_member_query_interval = stoi(fvValue(i).c_str()); + } + else if (fvField(i) == "query-max-response-time") + { + msg.query_max_response_time = stoi(fvValue(i).c_str()); + } + vector port_list; + msg.count = getVlanMembers(key,port_list); + j=0; + for (auto pentry = port_list.begin(); pentry != port_list.end(); pentry++) + { + memcpy(&msg.ports[j++].pnames, pentry->pnames, L2MCD_IFNAME_SIZE); + SWSS_LOG_INFO("L2MCD_CFG:SNOOP vlan %s mem-port:%s idx:%d size:%d", key.c_str(), msg.ports[j-1].pnames, j-1, (int)port_list.size()); + } + } + } + else + { + msg.op_code = L2MCD_OP_DISABLE; + msg.vlan_id = vlan_id; + } + SWSS_LOG_NOTICE("L2MCD_CFG:SNOOP %s [key:%s] vlan:%d,Ver:%d, Qry:%d, qI:%d,lmqi:%d,qmr:%d,FL:%d, count:%d", + op.c_str(), key.c_str(), vlan_id, msg.version, msg.querier,msg.query_interval, + msg.last_member_query_interval, msg.query_max_response_time , + msg.fast_leave,msg.count); + sendMsgL2Mcd(L2MCD_SNOOP_CONFIG_MSG, sizeof(msg), (void *)&msg); + string type_str = ""; + int cmd_type=0; + m_statel2mcdLocalMemberTable.getKeys(l2mcLocalMemKeys); + for (auto l2mc_localMemName : l2mcLocalMemKeys) + { + m_statel2mcdLocalMemberTable.hget(l2mc_localMemName, "type", type_str); + if (type_str=="remote")cmd_type=0; + if (type_str=="dynamic")cmd_type=2; + if (type_str=="static")cmd_type=3; + SWSS_LOG_INFO("MEMBER_REPLAY %s %s", l2mc_localMemName.c_str(), type_str.c_str()); + size_t pos=key.find(':'); + auto vlan_name = key.substr(0,pos); + if (vlan_id ==stoi(vlan_name.substr(4))) + { + doL2McProcRemoteEntries("SET",l2mc_localMemName,":", cmd_type); + } + } + it = consumer.m_toSync.erase(it); + } +} + +void L2McMgr::doL2McMrouterUpdateTask(Consumer &consumer) +{ + L2MCD_CONFIG_MSG msg; + SWSS_LOG_ENTER(); + + memset(&msg, 0, sizeof(L2MCD_CONFIG_MSG)); + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + size_t pos = key.find('|'); + string vlanKey = key.substr(4,pos-4); + string iname = key.substr(pos+1); + int vlan_id = stoi(vlanKey.c_str()); + msg.vlan_id = vlan_id; + msg.count=1; + memcpy(&msg.ports[0].pnames, iname.c_str(), L2MCD_IFNAME_SIZE); + if (op == SET_COMMAND) + { + msg.op_code = L2MCD_OP_ENABLE; + } + else + { + msg.op_code = L2MCD_OP_DISABLE; + } + for (auto i : kfvFieldsValues(t)) + { + + } + SWSS_LOG_NOTICE("L2MCD_CFG:MROUTER: op:%s [key:%s] %s vlan:%d", op.c_str(), key.c_str(),msg.ports[0].pnames,vlan_id); + sendMsgL2Mcd(L2MCD_SNOOP_MROUTER_CONFIG_MSG, sizeof(msg), (void *)&msg); + it = consumer.m_toSync.erase(it); + } +} + + + +void L2McMgr::doL2McStaticEntryTask(Consumer &consumer) +{ + L2MCD_CONFIG_MSG msg; + SWSS_LOG_ENTER(); + string srcip="0.0.0.0"; + memset(&msg, 0, sizeof(L2MCD_CONFIG_MSG)); + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + size_t pos = key.find('|'); + string vlanKey = key.substr(4,pos-4); + string key2 = key.substr(pos+1); + pos = key2.find('|'); + string ipKey = key2.substr(0,pos); + string iname = key2.substr(pos+1); + msg.cmd_code=0; + int vlan_id = stoi(vlanKey.c_str()); + if (op == SET_COMMAND) + { + msg.op_code = L2MCD_OP_ENABLE; + } + else + { + msg.op_code = L2MCD_OP_DISABLE; + } + msg.vlan_id = vlan_id; + msg.count=1; + memcpy(msg.gaddr,ipKey.c_str(), L2MCD_IP_ADDR_STR_SIZE); + memcpy(msg.saddr,srcip.c_str(), L2MCD_IP_ADDR_STR_SIZE); + memcpy(msg.ports[0].pnames, iname.c_str(), L2MCD_IFNAME_SIZE); + for (auto i : kfvFieldsValues(t)) + { + SWSS_LOG_INFO("L2MCD_CFG Field: %s Val %s vlan:%d opcode:%d", fvField(i).c_str(), fvValue(i).c_str(), vlan_id, msg.op_code ); + } + SWSS_LOG_NOTICE("L2MCD_CFG:STATIC op:%s [key:%s] GA:%s Port:%s vlan:%d", op.c_str(), key.c_str(), ipKey.c_str(), iname.c_str(), vlan_id); + sendMsgL2Mcd(L2MCD_SNOOP_STATIC_CONFIG_MSG, sizeof(msg), (void *)&msg); + it = consumer.m_toSync.erase(it); + } +} + +void L2McMgr::ipcInitL2McMgr() +{ + int ret; + struct sockaddr_un addr; + + unlink(L2MCMGR_IPC_SOCK_NAME); + l2mcd_fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (!l2mcd_fd) + { + SWSS_LOG_ERROR("socket error %s", strerror(errno)); + return; + } + + // setup socket address structure + bzero(&addr, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, L2MCMGR_IPC_SOCK_NAME, sizeof(addr.sun_path)-1); + + ret = (int)bind(l2mcd_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)); + if (ret == -1) + { + SWSS_LOG_ERROR("ipc bind error %s", strerror(errno)); + close(l2mcd_fd); + return; + } +} + + +int L2McMgr::sendMsgL2Mcd(L2MCD_MSG_TYPE msgType, uint32_t msgLen, void *data) +{ + L2MCD_IPC_MSG *tx_msg; + size_t len = 0; + struct sockaddr_un addr; + int rc; + + len = msgLen + (offsetof(struct L2MCD_IPC_MSG, data)); + tx_msg = (L2MCD_IPC_MSG *)calloc(1, len); + if (tx_msg == NULL) + { + SWSS_LOG_ERROR("tx_msg mem alloc error\n"); + return -1; + } + + tx_msg->msg_type = msgType; + tx_msg->msg_len = msgLen; + memcpy(tx_msg->data, data, msgLen); + + bzero(&addr, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, L2MCD_IPC_SOCK_NAME, sizeof(addr.sun_path)-1); + + rc = (int)sendto(l2mcd_fd, (void*)tx_msg, len, 0, (struct sockaddr *)&addr, sizeof(addr)); + if (rc == -1) + { + SWSS_LOG_ERROR("tx_msg send error type:%d len%d",msgType,(int)len); + } + free(tx_msg); + return rc; +} + +int L2McMgr::isPortInitComplete(DBConnector *state_db) +{ + bool portInit = 0; + long cnt = 0; + + while(!portInit) { + Table portTable(state_db, STATE_PORT_INI_TABLE_NAME); + std::vector tuples; + portInit = portTable.get("PortInitDone", tuples); + if(portInit) + break; + sleep(1); + cnt++; + } + SWSS_LOG_NOTICE("L2MCD_CFG:PORT_INIT_DONE val:%d count:%ld", portInit, cnt); + return portInit; + +} + + +int L2McMgr::getPortOperState(string if_name) +{ + vector fvs; + string oper_status; + string oper_up="up"; + + if (if_name.find("Ethernet") != string::npos) + m_appPortTable.get(if_name.c_str(), fvs); + else + m_appLagTable.get(if_name.c_str(), fvs); + auto it = find_if(fvs.begin(), fvs.end(), [](const FieldValueTuple &fv) + { + return fv.first == "oper_status"; + }); + + if (it != fvs.end()) + { + oper_status = it->second; + } + //SWSS_LOG_INFO("%s oper:%s %d",if_name.c_str(), oper_status.c_str(),oper_up.c_str(),oper_status.compare(oper_up)); + if (oper_status.compare(oper_up) == 0) return 1; + return 0; +} + + +int L2McMgr::getL2McPortList(DBConnector *state_db) +{ + Table m_statePortTable(state_db, STATE_PORT_TABLE_NAME); + Table m_stateLagTable(state_db, STATE_LAG_TABLE_NAME); + string Pname; + vector portKeys; + vector lagKeys; + m_statePortTable.getKeys(portKeys); + m_stateLagTable.getKeys(lagKeys); + L2MCD_CONFIG_MSG msg; + int j=0; + + + memset(&msg, 0, sizeof(L2MCD_CONFIG_MSG)); + SWSS_LOG_ENTER(); + msg.count=(unsigned int) portKeys.size(); + msg.count+=(unsigned int) lagKeys.size(); + if (msg.count >L2MCD_IPC_MAX_PORTS) + { + SWSS_LOG_ERROR("port count %d invalid", msg.count); + return -1; + } + for (auto port_name : portKeys) + { + msg.ports[j].oper_state = getPortOperState(port_name); + memcpy(msg.ports[j++].pnames, port_name.c_str(), L2MCD_IFNAME_SIZE); + SWSS_LOG_INFO("Port:%s oper:%d", port_name.c_str(), msg.ports[j-1].oper_state); + } + for (auto lag_name : lagKeys) + { + msg.ports[j].oper_state = getPortOperState(lag_name); + memcpy(msg.ports[j++].pnames, lag_name.c_str(), L2MCD_IFNAME_SIZE); + SWSS_LOG_INFO("Port:%s oper:%d", lag_name.c_str(), msg.ports[j-1].oper_state); + } + msg.op_code = L2MCD_OP_ENABLE; + SWSS_LOG_NOTICE("L2MCD_CFG:PORTLIST count:%d ", msg.count); + sendMsgL2Mcd(L2MCD_SNOOP_PORT_LIST_MSG, sizeof(msg), (void *)&msg); + return 0; +} + +int L2McMgr::getL2McCfgParams(DBConnector *conf_db) +{ + L2MCD_CONFIG_MSG msg; + MacAddress m_macAddr; + memset(&msg, 0, sizeof(L2MCD_CONFIG_MSG)); + int loglevel; + + Table table(conf_db, "DEVICE_METADATA"); + std::vector ovalues; + table.get("localhost", ovalues); + auto it = std::find_if( ovalues.begin(), ovalues.end(), [](const FieldValueTuple& t){ return t.first == "mac";} ); + if ( it == ovalues.end() ) { + throw runtime_error("couldn't find MAC address of the device from config DB"); + } + m_macAddr = MacAddress(it->second); + memcpy(&msg.mac_addr[0], m_macAddr.getMac(), 6); + loglevel = swss::Logger::getInstance().getMinPrio(); + msg.count=loglevel; + SWSS_LOG_NOTICE("L2MCD_CFG:PARAMS loglevel:%d mac:%s %x:%x:%x:%x:%x:%x ", loglevel, m_macAddr.to_string().c_str(), + msg.mac_addr[0],msg.mac_addr[1],msg.mac_addr[2],msg.mac_addr[3],msg.mac_addr[4],msg.mac_addr[5]); + sendMsgL2Mcd(L2MCD_CONFIG_PARAMS_MSG, sizeof(msg), (void *)&msg); + return 0; +} + +int L2McMgr::getVlanMembers(const string &vlanKey, vector&port_list) +{ + PORT_ATTR port_id; + vector vmEntry; + vector vmKeys; + SWSS_LOG_ENTER(); + m_stateVlanMemberTable.getKeys(vmKeys); + + + for (auto key : vmKeys) + { + size_t found = key.find(CONFIGDB_KEY_SEPARATOR); //split VLAN and interface + + string vlanName; + string intfName; + if (found != string::npos) + { + vlanName = key.substr(0, found); + intfName = key.substr(found+1); + } + else + { + SWSS_LOG_ERROR("Invalid Key: %s", key.c_str()); + continue; + } + + if (vlanKey == vlanName) + { + strncpy(port_id.pnames, intfName.c_str(), L2MCD_IFNAME_SIZE); + port_list.push_back(port_id); + SWSS_LOG_INFO("vlan:%s MemIntf: %s", vlanName.c_str(), intfName.c_str()); + } + } + SWSS_LOG_INFO("vlan members Key: %s memcnt:%d", vlanKey.c_str(), (int)port_list.size()); + return (int)port_list.size(); + +} + +void L2McMgr::doL2McVlanMemUpdateTask(Consumer &consumer) +{ + L2MCD_CONFIG_MSG msg; + memset(&msg, 0, sizeof(L2MCD_CONFIG_MSG)); + + SWSS_LOG_ENTER(); + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + auto key = kfvKey(t); + auto op = kfvOp(t); + size_t found = key.find("|"); + string vlanName = key.substr(4, found-4); + string intfName = key.substr(found+1); + + SWSS_LOG_INFO("vlanmemebr key:%s op:%s", key.c_str(), op.c_str()); + if ((found != string::npos)) + { + int vlanid = stoi(vlanName.c_str()); + msg.vlan_id = vlanid; + msg.count =1; + memcpy(msg.ports[0].pnames, intfName.c_str(), L2MCD_IFNAME_SIZE); + if (op == SET_COMMAND) + { + msg.op_code = L2MCD_OP_ENABLE; + } + else + { + msg.op_code = L2MCD_OP_DISABLE; + } + SWSS_LOG_NOTICE("L2MCD_CFG:VLAN_MEMBER op:%s iname:%s %s vlan:%d", op.c_str(), msg.ports[0].pnames, intfName.c_str(), msg.vlan_id); + sendMsgL2Mcd(L2MCD_VLAN_MEM_TABLE_UPDATE, sizeof(msg), (void *)&msg); + } + it = consumer.m_toSync.erase(it); + } +} + +void L2McMgr::doL2McLagMemberUpdateTask(Consumer &consumer) +{ + L2MCD_CONFIG_MSG msg; + memset(&msg, 0, sizeof(L2MCD_CONFIG_MSG)); + int j=0; + SWSS_LOG_ENTER(); + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + auto key = kfvKey(t); + auto op = kfvOp(t); + + string po_name; + string po_mem; + size_t found = key.find(CONFIGDB_KEY_SEPARATOR); + + if (found != string::npos) + { + po_name = key.substr(0, found); + po_mem = key.substr(found+1); + SWSS_LOG_INFO("LAG_MEMBER %s %s %s", po_name.c_str(), po_mem.c_str(), op.c_str()); + memcpy(msg.ports[j++].pnames, po_name.c_str(), L2MCD_IFNAME_SIZE); + memcpy(msg.ports[j++].pnames, po_mem.c_str(), L2MCD_IFNAME_SIZE); + } + else + { + SWSS_LOG_ERROR("Invalid key format %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (op == SET_COMMAND) + { + msg.op_code = L2MCD_OP_ENABLE; + } + else + { + msg.op_code = L2MCD_OP_DISABLE; + } + msg.count =j; + SWSS_LOG_NOTICE("L2MCD_CFG:LAG_MEMBER op:%s Po:%s count:%d", op.c_str(), po_name.c_str(), msg.count); + sendMsgL2Mcd(L2MCD_LAG_MEM_TABLE_UPDATE, sizeof(msg), (void *)&msg); + it = consumer.m_toSync.erase(it); + } +} + +void L2McMgr::doL2McL3InterfaceUpdateTask(Consumer &consumer) +{ + L2MCD_CONFIG_MSG msg; + memset(&msg, 0, sizeof(L2MCD_CONFIG_MSG)); + + //Vlan700|100.100.100.2/24 -- 700 , 100.100.100.2, 24 + SWSS_LOG_ENTER(); + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + auto key = kfvKey(t); + auto op = kfvOp(t); + size_t found = key.find("|"); + size_t found2 = key.find("/"); + SWSS_LOG_INFO(" key:%s op:%s", key.c_str(), op.c_str()); + if (!key.find("Vlan",0) && (found != string::npos) && (found2 != string::npos)) + { + string vlanstr = key.substr(4, found-4); + int vlanid = stoi(vlanstr.c_str()); + + string ipstr = key.substr(found+1, found2-found-1); + string prefix = key.substr(found2+1); + int pr_len = stoi(prefix); + msg.vlan_id =vlanid; + memcpy(msg.gaddr,ipstr.c_str(), L2MCD_IP_ADDR_STR_SIZE); + msg.prefix_length = pr_len; + SWSS_LOG_NOTICE("L2MCD_CFG:INTERFACE %s Vid:%s %d ip:%s prefix:%s(%d)", op.c_str(), vlanstr.c_str(), msg.vlan_id, msg.gaddr, prefix.c_str(),msg.prefix_length); + if (op == SET_COMMAND) + { + msg.op_code = L2MCD_OP_ENABLE; + } + else + { + msg.op_code = L2MCD_OP_DISABLE; + } + + sendMsgL2Mcd(L2MCD_INTERFACE_TABLE_UPDATE, sizeof(msg), (void *)&msg); + } + it = consumer.m_toSync.erase(it); + } +} + +void L2McMgr::doL2McInterfaceUpdateTask(Consumer &consumer) +{ + L2MCD_CONFIG_MSG msg; + memset(&msg, 0, sizeof(L2MCD_CONFIG_MSG)); + + SWSS_LOG_ENTER(); + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + auto key = kfvKey(t); + auto op = kfvOp(t); + memcpy(msg.ports[0].pnames, key.c_str(), L2MCD_IFNAME_SIZE); + msg.count=1; + msg.op_code = (op == SET_COMMAND)? L2MCD_OP_ENABLE:L2MCD_OP_DISABLE; + msg.ports[0].oper_state = getPortOperState(msg.ports[0].pnames); + SWSS_LOG_NOTICE("L2MCD_CFG: IF:%s op:%s oper:%d", key.c_str(), op.c_str(),msg.ports[0].oper_state); + sendMsgL2Mcd(L2MCD_SNOOP_PORT_LIST_MSG, sizeof(msg), (void *)&msg); + it = consumer.m_toSync.erase(it); + } +} + +void L2McMgr::doL2McProcRemoteEntries(string op, string key, string key_seperator, int cmd_type) +{ + L2MCD_CONFIG_MSG msg; + memset(&msg, 0, sizeof(L2MCD_CONFIG_MSG)); + + SWSS_LOG_ENTER(); + size_t pos=key.find(key_seperator.c_str()); + auto vlan_name = key.substr(0,pos); + auto pos1 = key.find(key_seperator.c_str(), pos+1); + auto saddr = key.substr(pos+1, pos1-pos-1); + auto pos2 = key.find(key_seperator.c_str(), pos1+1); + auto gaddr = key.substr(pos1+1, pos2-pos1-1); + auto pos3 = key.find(key_seperator.c_str(), pos2+1); + auto portname = key.substr(pos2+1, pos3-pos2-1); + auto pos4 = key.find(key_seperator.c_str(), pos3+1); + auto type = key.substr(pos3+1, pos4-pos3-1); + auto pos5 = key.find(key_seperator.c_str(), pos4+1); + auto leave = key.substr(pos4+1, pos5-pos4-1); + msg.vlan_id = (unsigned int) stoi(vlan_name.substr(4)); + memcpy(msg.saddr,saddr.c_str(), L2MCD_IP_ADDR_STR_SIZE); + memcpy(msg.gaddr,gaddr.c_str(), L2MCD_IP_ADDR_STR_SIZE); + msg.count=1; + memcpy(msg.ports[0].pnames, portname.c_str(), L2MCD_IFNAME_SIZE); + if (op =="SET") + { + msg.op_code = L2MCD_OP_ENABLE; + } + else + { + msg.op_code = L2MCD_OP_DISABLE; + } + msg.cmd_code=cmd_type; + SWSS_LOG_NOTICE("L2MCD_CFG:REMOTE op:%s [key:%s] SA:%s GA:%s Port:%s vlan:%d type:%d", + op.c_str(), key.c_str(), saddr.c_str(), gaddr.c_str(), portname.c_str(), msg.vlan_id,msg.cmd_code); + sendMsgL2Mcd(L2MCD_SNOOP_REMOTE_CONFIG_MSG, sizeof(msg), (void *)&msg); +} + +void L2McMgr::doL2McProcRemoteMrouterEntries(string op, string key, string key_seperator) +{ + L2MCD_CONFIG_MSG msg; + memset(&msg, 0, sizeof(L2MCD_CONFIG_MSG)); + + SWSS_LOG_ENTER(); + size_t pos=key.find(key_seperator.c_str()); + auto vlan_name = key.substr(0,pos); + auto pos1 = key.find(key_seperator.c_str(), pos+1); + auto portname = key.substr(pos+1, pos1-pos-1); + auto pos2 = key.find(key_seperator.c_str(), pos1+1); + auto type = key.substr(pos1+1, pos2-pos1-1); + auto pos3 = key.find(key_seperator.c_str(), pos2+1); + auto leave = key.substr(pos2+1, pos3-pos2-1); + msg.vlan_id = (unsigned int) stoi(vlan_name.substr(4)); + msg.count=1; + memcpy(msg.ports[0].pnames, portname.c_str(), L2MCD_IFNAME_SIZE); + if (op =="SET") + { + msg.op_code = L2MCD_OP_ENABLE; + } + else + { + msg.op_code = L2MCD_OP_DISABLE; + } + SWSS_LOG_NOTICE("L2MCD_Mrouter:REMOTE op:%s [key:%s] Port:%s vlan:%d", + op.c_str(), key.c_str(), portname.c_str(), msg.vlan_id); + sendMsgL2Mcd(L2MCD_SNOOP_MROUTER_REMOTE_CONFIG_MSG, sizeof(msg), (void *)&msg); +} + +void L2McMgr::doTask(NotificationConsumer &consumer) +{ + std::string op; + std::string data; + std::vector values; + + if (&consumer == m_l2mcNotificationConsumer) + { + SWSS_LOG_INFO("Received l2mc entry notification"); + consumer.pop(op, data, values); + doL2McProcRemoteEntries(op, data, "|", 0); + } + else if (&consumer == m_l2mcMrouterNotificationConsumer) + { + SWSS_LOG_INFO("Received l2mc Mrouter notification"); + consumer.pop(op, data, values); + doL2McProcRemoteMrouterEntries(op, data, "|"); + } +} diff --git a/cfgmgr/l2mcmgr.h b/cfgmgr/l2mcmgr.h new file mode 100644 index 0000000000..7f2c1bbbd0 --- /dev/null +++ b/cfgmgr/l2mcmgr.h @@ -0,0 +1,79 @@ +/* + * Copyright 2019 Broadcom. All rights reserved. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * cfgmgr/l2mcmgr.h + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include "l2mcd_ipc.h" +#include +#include "dbconnector.h" +#include "netmsg.h" +#include "orch.h" +#include "producerstatetable.h" +#include +#include +#include "logger.h" +#include "tokenize.h" +#include "warm_restart.h" +#include "notifier.h" + +#define L2MCMGR_IPC_SOCK_NAME "/var/run/l2mcdmgr_ipc.sock" + +namespace swss { + +class L2McMgr : public Orch +{ +public: + L2McMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *staDb, + const vector &tables); + + using Orch::doTask; + void ipcInitL2McMgr(); + int sendMsgL2Mcd(L2MCD_MSG_TYPE msgType, uint32_t msgLen, void *data); + int isPortInitComplete(DBConnector *app_db); + int getL2McPortList(DBConnector *state_db); + int getL2McCfgParams(DBConnector *cfgDb); + + +private: + Table m_cfgL2McGlobalTable; + Table m_cfgL2McStaticTable; + Table m_cfgL2McMrouterTable; + Table m_stateVlanMemberTable; + Table m_stateInterfaceTableName; + Table m_cfgLagMemberTable; + Table m_stateLagTable; + Table m_appPortTable; + Table m_appLagTable; + Table m_statel2mcdLocalMemberTable; + NotificationConsumer* m_l2mcNotificationConsumer; + NotificationConsumer* m_l2mcMrouterNotificationConsumer; + + int l2mcd_fd; + void doTask(Consumer &consumer); + void doTask(NotificationConsumer &consumer); + void doL2McGlobalTask(Consumer &consumer); + void doL2McStaticEntryTask(Consumer &consumer); + void doL2McMrouterUpdateTask(Consumer &consumer); + void doL2McVlanMemUpdateTask(Consumer &consumer); + void doL2McL3InterfaceUpdateTask(Consumer &consumer); + void doL2McLagMemberUpdateTask(Consumer &consumer); + void doL2McInterfaceUpdateTask(Consumer &consumer); + void doL2McProcRemoteEntries(string op, string key, string key_seperator, int type); + void doL2McProcRemoteMrouterEntries(string op, string key, string key_seperator); + int getVlanMembers(const string &vlanKey, vector&port_list); + int getPortOperState(string if_name); +}; + +} diff --git a/cfgmgr/l2mcmgrd.cpp b/cfgmgr/l2mcmgrd.cpp new file mode 100644 index 0000000000..5bc136f893 --- /dev/null +++ b/cfgmgr/l2mcmgrd.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2019 Broadcom. All rights reserved. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * cfgmgr/l2mcdmgrd.cpp + */ + +#include +#include "l2mcmgr.h" +#include "netdispatcher.h" +#include "netlink.h" +#include "select.h" +#include "warm_restart.h" +#include "schema.h" + +using namespace std; +using namespace swss; + +/* select() function timout retry time in milli-seconds */ +#define SELECT_TIMEOUT 1000 + +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("l2mcdmgrd"); + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("--- Starting l2mcmgrd ---"); + + try + { + DBConnector conf_db(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector app_db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector state_db(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + + WarmStart::initialize("l2mcmgrd", "l2mcmgrd"); + WarmStart::checkWarmStart("l2mcmgrd", "l2mcmgrd"); + + // Config DB Tables + TableConnector conf_l2mc_global_table(&conf_db, CFG_L2MC_TABLE_NAME); + TableConnector conf_l2mc_static_table(&conf_db, CFG_L2MC_STATIC_TABLE_NAME); + TableConnector conf_l2mc_mrouter_table(&conf_db, CFG_L2MC_MROUTER_TABLE_NAME); + TableConnector conf_lag_member_table(&conf_db, CFG_LAG_MEMBER_TABLE_NAME); + TableConnector state_interface_table(&state_db, STATE_INTERFACE_TABLE_NAME); + TableConnector state_vlan_member_table(&state_db, STATE_VLAN_MEMBER_TABLE_NAME); + TableConnector state_lag_table(&state_db, STATE_LAG_TABLE_NAME); + TableConnector state_port_table(&state_db, STATE_PORT_TABLE_NAME); + + vector tables = { + conf_l2mc_global_table, + conf_l2mc_static_table, + conf_l2mc_mrouter_table, + conf_lag_member_table, + state_interface_table, + state_vlan_member_table, + state_lag_table, + }; + + L2McMgr l2mcmgr(&conf_db, &app_db, &state_db, tables); + l2mcmgr.ipcInitL2McMgr(); + l2mcmgr.isPortInitComplete(&state_db); + l2mcmgr.getL2McPortList(&state_db); + l2mcmgr.getL2McCfgParams(&conf_db); + vector cfgOrchList = {&l2mcmgr}; + Select s; + for (Orch *o: cfgOrchList) + { + s.addSelectables(o->getSelectables()); + } + SWSS_LOG_NOTICE("L2MCMGrd Start main loop log_debug:%d",swss::Logger::getInstance().getMinPrio()); + 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) + { + l2mcmgr.doTask(); + continue; + } + + auto *c = (Executor *)sel; + c->execute(); + } + } + catch (const exception &e) + { + SWSS_LOG_ERROR("Runtime error: %s", e.what()); + } + + return -1; +} diff --git a/configure.ac b/configure.ac index 7faa50b995..81f5a42377 100644 --- a/configure.ac +++ b/configure.ac @@ -20,6 +20,10 @@ AC_CHECK_LIB([team], [team_alloc], [AC_MSG_WARN([libteam is not installed.]) AM_CONDITIONAL(HAVE_LIBTEAM, false)]) +AC_CHECK_FILE([/usr/include/l2mcd_ipc.h], + AM_CONDITIONAL(HAVE_L2MCD, true), + [AC_MSG_WARN([l2mcd is not installed.]) + AM_CONDITIONAL(HAVE_L2MCD, false)]) AC_CHECK_LIB([sai], [sai_object_type_query], AM_CONDITIONAL(HAVE_SAI, true), [AC_MSG_WARN([libsai is not installed.]) diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index d431557b5c..089e0484e0 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -56,7 +56,9 @@ orchagent_SOURCES = \ sfloworch.cpp \ chassisorch.cpp \ debugcounterorch.cpp \ - natorch.cpp + natorch.cpp \ + l2mcorch.cpp \ + l2mcdorch.h orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp orchagent_SOURCES += debug_counter/debug_counter.cpp debug_counter/drop_counter.cpp diff --git a/orchagent/l2mcorch.cpp b/orchagent/l2mcorch.cpp new file mode 100644 index 0000000000..2128410cca --- /dev/null +++ b/orchagent/l2mcorch.cpp @@ -0,0 +1,1022 @@ +#include +#include +#include "l2mcorch.h" +#include "logger.h" +#include "swssnet.h" +#include "tokenize.h" +#include "errororch.h" +#include "sai_serialize.h" +#include "redisclient.h" +#include "debugframework.h" + +using namespace swss; + +#define VLAN_PREFIX "Vlan" + +extern sai_object_id_t gSwitchId; + +extern sai_vlan_api_t *sai_vlan_api; +extern sai_switch_api_t* sai_switch_api; +extern sai_l2mc_api_t* sai_l2mc_entry_api; +extern sai_l2mc_group_api_t* sai_l2mc_group_api; + +extern PortsOrch *gPortsOrch; +extern DebugDumpOrch *gDebugDumpOrch; + +L2mcOrch::L2mcOrch(DBConnector * appDb, vector &tableNames) : + Orch(appDb, tableNames) +{ + SWSS_LOG_ENTER(); + + /* Register with debug framework */ + this->gL2mcOrchDbgComp = "l2mcorch"; + gDebugDumpOrch->addDbgCompMap(gL2mcOrchDbgComp, this); + memset (&l2mcdbg_counters, 0, sizeof (struct L2mcDebugCounters)); +}; + +bool L2mcOrch::hasL2mcGroup(string vlan, const L2mcGroupKey &l2mckey) +{ + return m_syncdL2mcEntries.at(vlan).find(l2mckey) != m_syncdL2mcEntries.at(vlan).end(); +} + +sai_object_id_t L2mcOrch::getL2mcGroupId(string vlan, const L2mcGroupKey &l2mckey) +{ + return m_syncdL2mcEntries[vlan][l2mckey].l2mc_group_id; +} + +void L2mcOrch::increaseL2mcMemberRefCount(string vlan, const L2mcGroupKey& l2mckey) +{ + m_syncdL2mcEntries[vlan][l2mckey].ref_count ++; + return; +} + +void L2mcOrch::decreaseL2mcMemberRefCount(string vlan, const L2mcGroupKey& l2mckey) +{ + m_syncdL2mcEntries[vlan][l2mckey].ref_count --; + return; +} + +bool L2mcOrch::isMemberRefCntZero(string vlan, const L2mcGroupKey& l2mckey) const +{ + return m_syncdL2mcEntries.at(vlan).at(l2mckey).ref_count == 0; +} + +bool L2mcOrch::AddL2mcGroupMember(const L2mcGroupKey &l2mc_GrpKey, string vlan_alias, Port &port) +{ + sai_status_t status; + sai_object_id_t l2mc_member_id; + vector l2mcgm_attrs; + sai_attribute_t l2mcgm_attr; + Port vlan; + + SWSS_LOG_ENTER(); + + if (!gPortsOrch->getPort(vlan_alias, vlan)) + { + SWSS_LOG_NOTICE("AddL2mcGroupMember: Failed to locate vlan %s", vlan_alias.c_str()); + l2mcdbg_counters.l2mc_vlan_fail++; + return false; + } + + auto l2mc_group = m_syncdL2mcEntries.at(vlan_alias).find(l2mc_GrpKey); + /*Group member already exists */ + if (l2mc_group->second.l2mc_group_members.find(port.m_alias) != l2mc_group->second.l2mc_group_members.end()) + return true; + + SWSS_LOG_NOTICE("AddL2mcGroupMember: (%s,%s,%s) Add l2mc group member %s to l2mc_gid:%lx", + l2mc_GrpKey.source_address.to_string().c_str(), l2mc_GrpKey.group_address.to_string().c_str(), + l2mc_GrpKey.vlan_alias.c_str(), port.m_alias.c_str(), l2mc_group->second.l2mc_group_id); + + bzero(&l2mcgm_attr, sizeof(l2mcgm_attr)); + + l2mcgm_attr.id = SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_GROUP_ID; + l2mcgm_attr.value.oid = l2mc_group->second.l2mc_group_id; + l2mcgm_attrs.push_back(l2mcgm_attr); + + l2mcgm_attr.id = SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_OUTPUT_ID; + l2mcgm_attr.value.oid = port.m_bridge_port_id; + l2mcgm_attrs.push_back(l2mcgm_attr); + + status = sai_l2mc_group_api->create_l2mc_group_member(&l2mc_member_id, gSwitchId, + (uint32_t)l2mcgm_attrs.size(), + l2mcgm_attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("AddL2mcGroupMember: Failed to add l2mc group member %s, to l2mc-grp:%lx rv:%d", + port.m_alias.c_str(), l2mc_group->second.l2mc_group_id, status); + l2mcdbg_counters.l2mc_member_add_fail++; + return false; + } + l2mc_group->second.l2mc_group_members[port.m_alias] = l2mc_member_id; + l2mcdbg_counters.l2mc_member_add++; + increaseL2mcMemberRefCount(vlan_alias, l2mc_GrpKey); + + port.m_l2mc_count++; + gPortsOrch->setPort(port.m_alias, port); + vlan.m_l2mc_count++; + gPortsOrch->setPort(vlan.m_alias, vlan); + SWSS_LOG_NOTICE("port %s l2mc_count %u vlan %s l2mc_count %u", + port.m_alias.c_str(), port.m_l2mc_count, vlan.m_alias.c_str(), vlan.m_l2mc_count); + + return true; +} + +bool L2mcOrch::RemoveL2mcGroupMember(const L2mcGroupKey &l2mc_GrpKey, string vlan_alias, Port &port) +{ + sai_status_t status; + Port vlan; + + SWSS_LOG_ENTER(); + + if (!gPortsOrch->getPort(vlan_alias, vlan)) + { + SWSS_LOG_NOTICE("RemoveL2mcGroupMember: Failed to locate vlan %s", vlan_alias.c_str()); + l2mcdbg_counters.l2mc_vlan_fail++; + return false; + } + + auto l2mc_group = m_syncdL2mcEntries.at(vlan_alias).find(l2mc_GrpKey); + + /* l2mc group member already deleted or doesn't exists */ + if(l2mc_group->second.l2mc_group_members.find(port.m_alias) == l2mc_group->second.l2mc_group_members.end()) + return true; + + SWSS_LOG_NOTICE("RemoveL2mcGroupMember: (%s,%s,%s) Delete l2mc group member %s from l2mc_gid:%lx", + l2mc_GrpKey.source_address.to_string().c_str(), l2mc_GrpKey.group_address.to_string().c_str(), + l2mc_GrpKey.vlan_alias.c_str(), port.m_alias.c_str(), l2mc_group->second.l2mc_group_id); + + auto l2mc_group_member = l2mc_group->second.l2mc_group_members.find(port.m_alias); + if (l2mc_group_member != l2mc_group->second.l2mc_group_members.end()) + { + status = sai_l2mc_group_api->remove_l2mc_group_member(l2mc_group_member->second); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("RemoveL2mcGroupMember: Failed to remove l2mc group member %lx, from l2mc-grp:%lx rv:%d", + l2mc_group_member->second, l2mc_group->second.l2mc_group_id, status); + l2mcdbg_counters.l2mc_member_del_fail++; + return false; + } + decreaseL2mcMemberRefCount(vlan_alias, l2mc_GrpKey); + l2mcdbg_counters.l2mc_member_del++; + l2mc_group->second.l2mc_group_members.erase(l2mc_group_member); + + port.m_l2mc_count--; + gPortsOrch->setPort(port.m_alias, port); + vlan.m_l2mc_count--; + gPortsOrch->setPort(vlan.m_alias, vlan); + SWSS_LOG_NOTICE("port %s l2mc_count %u vlan %s l2mc_count %u", + port.m_alias.c_str(), port.m_l2mc_count, vlan.m_alias.c_str(), vlan.m_l2mc_count); + } + return true; +} + +bool L2mcOrch::AddL2mcEntry(const L2mcGroupKey &l2mcGrpKey, Port &vlan, Port &port) +{ + sai_object_id_t l2mc_group_id; + sai_status_t status; + L2mcGroupEntry l2mc_group_entry; + + SWSS_LOG_ENTER(); + + if (port.m_bridge_port_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_NOTICE("AddL2mcEntry: NULL port OID"); + l2mcdbg_counters.l2mc_port_oid_fail++; + return false; + } + + if (vlan.m_vlan_info.vlan_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_NOTICE("AddL2mcEntry: NULL vlan OID"); + l2mcdbg_counters.l2mc_vlan_oid_fail++; + return false; + } + + if (!hasL2mcGroup(vlan.m_alias,l2mcGrpKey)) + { + status = sai_l2mc_group_api->create_l2mc_group(&l2mc_group_id, gSwitchId, 0 , NULL); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("AddL2mcEntry: Failed to add l2mc group %lx, rv:%d", l2mc_group_id, status); + l2mcdbg_counters.l2mc_group_add_fail++; + return false; + } + + l2mc_group_entry.l2mc_group_id = l2mc_group_id; + l2mc_group_entry.ref_count = 0; + } + else + { + l2mc_group_id = getL2mcGroupId(vlan.m_alias, l2mcGrpKey); + } + + sai_attribute_t l2mc_entry_attr; + sai_l2mc_entry_t l2mc_entry; + vector l2mc_entry_attrs; + + bzero(&l2mc_entry_attr, sizeof(l2mc_entry_attr)); + + /*Add l2mc entry */ + l2mc_entry_attr.id = SAI_L2MC_ENTRY_ATTR_OUTPUT_GROUP_ID; + l2mc_entry_attr.value.oid = l2mc_group_id; + l2mc_entry_attrs.push_back(l2mc_entry_attr); + + l2mc_entry_attr.id = SAI_L2MC_ENTRY_ATTR_PACKET_ACTION; + l2mc_entry_attr.value.oid = SAI_PACKET_ACTION_FORWARD; + l2mc_entry_attrs.push_back(l2mc_entry_attr); + + l2mc_entry.switch_id = gSwitchId; + copy(l2mc_entry.source, l2mcGrpKey.source_address); + copy(l2mc_entry.destination, l2mcGrpKey.group_address); + l2mc_entry.source.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + l2mc_entry.destination.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + l2mc_entry.bv_id = vlan.m_vlan_info.vlan_oid; + + if(IpAddress(l2mcGrpKey.source_address.to_string()).isZero()) + l2mc_entry.type = SAI_L2MC_ENTRY_TYPE_XG; + else + l2mc_entry.type = SAI_L2MC_ENTRY_TYPE_SG; + + + status = sai_l2mc_entry_api->create_l2mc_entry(&l2mc_entry, (uint32_t)l2mc_entry_attrs.size(), + l2mc_entry_attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("AddL2mcEntry: Failed to create l2mc entry (%s, %s, %d) rv:%d", + l2mcGrpKey.source_address.to_string().c_str(), l2mcGrpKey.group_address.to_string().c_str(), vlan.m_vlan_info.vlan_id, status); + l2mcdbg_counters.l2mc_entry_add_fail++; + return false; + } + + if (!hasL2mcGroup(vlan.m_alias, l2mcGrpKey)) + m_syncdL2mcEntries[vlan.m_alias][l2mcGrpKey] = l2mc_group_entry; + + AddL2mcGroupMember(l2mcGrpKey, vlan.m_alias, port); + + SWSS_LOG_NOTICE("AddL2mcEntry: Added l2mc entry (%s,%s,%d) with l2mc-gid:%lx", l2mcGrpKey.source_address.to_string().c_str(), + l2mcGrpKey.group_address.to_string().c_str(), vlan.m_vlan_info.vlan_id, l2mc_group_id); + + return true; +} + +bool L2mcOrch::RemoveL2mcEntry(const L2mcGroupKey &key, Port &vlan) +{ + sai_l2mc_entry_t l2mc_entry; + sai_object_id_t l2mc_group_id; + sai_status_t status; + + SWSS_LOG_ENTER(); + + auto l2mc_group = m_syncdL2mcEntries.at(vlan.m_alias).find(key); + + if(l2mc_group == m_syncdL2mcEntries.at(vlan.m_alias).end()) + { + SWSS_LOG_INFO("RemoveL2mcEntry: L2mc Entry not found. (%s, %s, %d)", key.source_address.to_string().c_str(), + key.group_address.to_string().c_str(), vlan.m_vlan_info.vlan_id); + return true; + } + /*Remove l2mc entry */ + l2mc_entry.switch_id = gSwitchId; + copy(l2mc_entry.source, key.source_address); + copy(l2mc_entry.destination, key.group_address); + l2mc_entry.source.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + l2mc_entry.destination.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + l2mc_entry.bv_id = vlan.m_vlan_info.vlan_oid; + + if(IpAddress(key.source_address.to_string()).isZero()) + l2mc_entry.type = SAI_L2MC_ENTRY_TYPE_XG; + else + l2mc_entry.type = SAI_L2MC_ENTRY_TYPE_SG; + + status = sai_l2mc_entry_api->remove_l2mc_entry(&l2mc_entry); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("RemoveL2mcEntry: Failed to remove l2mc entry (%s, %s, %d) rv:%d", + key.source_address.to_string().c_str(), key.group_address.to_string().c_str(), vlan.m_vlan_info.vlan_id, status); + l2mcdbg_counters.l2mc_entry_del_fail++; + return false; + } + l2mc_group_id = getL2mcGroupId(vlan.m_alias, key); + + SWSS_LOG_NOTICE("RemoveL2mcEntry: (%s,%s,%d) Delete l2mc group %lx", key.source_address.to_string().c_str(), + key.group_address.to_string().c_str(), vlan.m_vlan_info.vlan_id, l2mc_group_id); + + /* Remove all mrouter ports added as group members */ + for (auto l2mc_group_member = l2mc_group->second.l2mc_group_members.begin(); + l2mc_group_member != l2mc_group->second.l2mc_group_members.end();) + { + status = sai_l2mc_group_api->remove_l2mc_group_member(l2mc_group_member->second); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("RemoveL2mcEntry: Failed to remove mrouter from l2mc group member %lx, rv:%d", + l2mc_group_member->second, status); + l2mcdbg_counters.l2mc_mrouter_del_fail++; + return false; + } + l2mc_group_member = l2mc_group->second.l2mc_group_members.erase(l2mc_group_member); + } + /* Remove l2mc group */ + status = sai_l2mc_group_api->remove_l2mc_group(l2mc_group_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("RemoveL2mcEntry: Failed to remove l2mc group %lx, rv:%d", l2mc_group_id, status); + l2mcdbg_counters.l2mc_group_del_fail++; + return false; + } + + m_syncdL2mcEntries.at(vlan.m_alias).erase(key); + + return true; +} + +bool L2mcOrch::AddL2mcMrouterPort(const L2mcGroupKey &l2mc_GrpKey, string vlan, Port &port) +{ + sai_status_t status; + sai_object_id_t l2mc_member_id; + vector l2mcgm_attrs; + sai_attribute_t l2mcgm_attr; + + SWSS_LOG_ENTER(); + + if (port.m_bridge_port_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_NOTICE("AddL2mcMrouterPort: NULL port OID"); + l2mcdbg_counters.l2mc_port_oid_fail++; + return false; + } + + auto l2mc_group = m_syncdL2mcEntries.at(vlan).find(l2mc_GrpKey); + /*Group member already exists */ + if (l2mc_group->second.l2mc_group_members.find(port.m_alias) != l2mc_group->second.l2mc_group_members.end()) + return true; + + SWSS_LOG_NOTICE("AddL2mcMrouterPort: (%s,%s,%s) Add mrouter port %s as l2mc group member to l2mc_gid:%lx", + l2mc_GrpKey.source_address.to_string().c_str(), l2mc_GrpKey.group_address.to_string().c_str(), + l2mc_GrpKey.vlan_alias.c_str(), port.m_alias.c_str(), l2mc_group->second.l2mc_group_id); + + bzero(&l2mcgm_attr, sizeof(l2mcgm_attr)); + + l2mcgm_attr.id = SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_GROUP_ID; + l2mcgm_attr.value.oid = l2mc_group->second.l2mc_group_id; + l2mcgm_attrs.push_back(l2mcgm_attr); + + l2mcgm_attr.id = SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_OUTPUT_ID; + l2mcgm_attr.value.oid = port.m_bridge_port_id; + l2mcgm_attrs.push_back(l2mcgm_attr); + + status = sai_l2mc_group_api->create_l2mc_group_member(&l2mc_member_id, gSwitchId, + (uint32_t)l2mcgm_attrs.size(), + l2mcgm_attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("AddL2mcMrouterPort: Failed to add mrouter port %s as l2mc group member to l2mc-grp:%lx rv:%d", + port.m_alias.c_str(), l2mc_group->second.l2mc_group_id, status); + l2mcdbg_counters.l2mc_mrouter_add_fail++; + return false; + } + l2mc_group->second.l2mc_group_members[port.m_alias] = l2mc_member_id; + l2mcdbg_counters.l2mc_mrouter_add++; + return true; +} + +bool L2mcOrch::RemoveL2mcMrouterPort(const L2mcGroupKey &l2mc_GrpKey, string vlan, string port) +{ + sai_status_t status; + + SWSS_LOG_ENTER(); + + auto l2mc_group = m_syncdL2mcEntries.at(vlan).find(l2mc_GrpKey); + + /* l2mc group member already deleted or doesn't exists */ + if(l2mc_group->second.l2mc_group_members.find(port) == l2mc_group->second.l2mc_group_members.end()) + return true; + + SWSS_LOG_NOTICE("RemoveL2mcMrouterPort: (%s,%s,%s) Delete l2mc mrouter group member %s from l2mc_gid:%lx", + l2mc_GrpKey.source_address.to_string().c_str(), l2mc_GrpKey.group_address.to_string().c_str(), + l2mc_GrpKey.vlan_alias.c_str(), port.c_str(), l2mc_group->second.l2mc_group_id); + + auto l2mc_group_member = l2mc_group->second.l2mc_group_members.find(port); + if (l2mc_group_member != l2mc_group->second.l2mc_group_members.end()) + { + status = sai_l2mc_group_api->remove_l2mc_group_member(l2mc_group_member->second); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("RemoveL2mcMrouterPort: Failed to remove l2mc mrouter group member %lx, from l2mc-grp:%lx rv:%d", + l2mc_group_member->second, l2mc_group->second.l2mc_group_id, status); + l2mcdbg_counters.l2mc_mrouter_del_fail++; + return false; + } + l2mcdbg_counters.l2mc_mrouter_del++; + l2mc_group->second.l2mc_group_members.erase(l2mc_group_member); + } + return true; +} + +bool L2mcOrch::EnableIgmpSnooping(string vlan_alias) +{ + Port vlan; + sai_attribute_t attr; + + SWSS_LOG_ENTER(); + + if (!gPortsOrch->getPort(vlan_alias, vlan)) + { + SWSS_LOG_INFO("Failed to locate VLAN %s", vlan_alias.c_str()); + l2mcdbg_counters.l2mc_vlan_fail++; + return false; + } + + if (vlan.m_vlan_info.vlan_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_NOTICE("EnableIgmpSnooping: NULL vlan OID"); + l2mcdbg_counters.l2mc_vlan_oid_fail++; + return false; + } + + bzero(&attr, sizeof(attr)); + + attr.id = SAI_VLAN_ATTR_CUSTOM_IGMP_SNOOPING_ENABLE; + attr.value.booldata = true; + + sai_status_t status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR(" Failed to Enable L2mc snooping on %s status %u", vlan_alias.c_str(), status); + return false; + } + SWSS_LOG_NOTICE(" Enabled L2mc Snooping on %s ", vlan_alias.c_str()); + return true; +} + +bool L2mcOrch::DisableIgmpSnooping(string vlan_alias) +{ + Port vlan; + sai_attribute_t attr; + + SWSS_LOG_ENTER(); + + if (!gPortsOrch->getPort(vlan_alias, vlan)) + { + SWSS_LOG_INFO("Failed to locate VLAN %s", vlan_alias.c_str()); + l2mcdbg_counters.l2mc_vlan_fail++; + return false; + } + + bzero(&attr, sizeof(attr)); + + attr.id = SAI_VLAN_ATTR_CUSTOM_IGMP_SNOOPING_ENABLE; + attr.value.booldata = false; + + sai_status_t status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR(" Failed to Disable L2mc snooping on %s status %u", vlan_alias.c_str(), status); + return false; + } + SWSS_LOG_NOTICE(" Disabled L2mc Snooping on %s ", vlan_alias.c_str()); + return true; +} + + +void L2mcOrch::doL2mcTask(Consumer &consumer) +{ + unsigned short vlan_id; + string vlan_alias; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + string op = kfvOp(t); + vector keys = tokenize(kfvKey(t), ':'); + /* Key: */ + + /* Ensure the key size is 1 otherwise ignore */ + if (keys.size() != 1) + { + SWSS_LOG_ERROR("Invalid key size, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the key starts with "Vlan" otherwise ignore */ + if (strncmp(keys[0].c_str(), VLAN_PREFIX, 4)) + { + SWSS_LOG_ERROR("doL2mcTask:Invalid key format. No 'Vlan' prefix: %s", keys[0].c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + vlan_id = (unsigned short) stoi(keys[0].substr(4)); + vlan_alias = VLAN_PREFIX + to_string(vlan_id); + + if (op == SET_COMMAND) + { + m_snoop_enabled_vlans.push_back(vlan_alias); + if(EnableIgmpSnooping(vlan_alias)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else if (op == DEL_COMMAND) + { + auto v = find(m_snoop_enabled_vlans.begin(), m_snoop_enabled_vlans.end(), vlan_alias); + if (v != m_snoop_enabled_vlans.end()) + m_snoop_enabled_vlans.erase(v); + + if(DisableIgmpSnooping(vlan_alias)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +void L2mcOrch::doL2mcMemberTask(Consumer &consumer) +{ + if (!gPortsOrch->allPortsReady()) + { + return; + } + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + string op = kfvOp(t); + vector keys = tokenize(kfvKey(t), ':'); + /* Key: : */ + + /* Ensure the key size is 4 otherwise ignore */ + if (keys.size() != 4) + { + SWSS_LOG_ERROR("doL2mcMemberTask: Invalid key size, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the key starts with "Vlan" otherwise ignore */ + if (strncmp(keys[0].c_str(), VLAN_PREFIX, 4)) + { + SWSS_LOG_ERROR("doL2mcMemberTask: Invalid key format. No 'Vlan' prefix: %s", keys[0].c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + unsigned short vlan_id; + std::string port_alias, vlan_alias; + Port port, mrport, vlan; + + vlan_id = (unsigned short) stoi(keys[0].substr(4)); + vlan_alias = VLAN_PREFIX + to_string(vlan_id); + port_alias = keys[3]; + + if (!gPortsOrch->getPort(port_alias, port)) + { + SWSS_LOG_ERROR("doL2mcMemberTask: Failed to get port for %s alias", port_alias.c_str()); + l2mcdbg_counters.l2mc_port_fail++; + continue; + } + + if (!gPortsOrch->getPort(vlan_alias, vlan)) + { + SWSS_LOG_NOTICE("doL2mcMemberTask: Failed to locate vlan %s", vlan_alias.c_str()); + l2mcdbg_counters.l2mc_vlan_fail++; + continue; + } + + auto l2mcKey = L2mcGroupKey(vlan_alias, IpAddress(keys[1]), IpAddress(keys[2])); + + if (m_syncdL2mcEntries.find(vlan.m_alias) == m_syncdL2mcEntries.end()) + { + m_syncdL2mcEntries.emplace(vlan.m_alias, L2mcEntryTable()); + } + + if (op == SET_COMMAND) + { + + if (!hasL2mcGroup(vlan_alias, l2mcKey)) + { + if (AddL2mcEntry(l2mcKey, vlan, port)) + { + /* Add all mrouter ports to the newly added l2mc entry */ + auto mrouter_ports = mrouter_ports_per_vlan[vlan_alias]; + + if(!mrouter_ports.empty()) + { + for (const auto& mrouter: mrouter_ports) + { + if (gPortsOrch->getPort(mrouter, mrport)) + AddL2mcMrouterPort(l2mcKey, vlan_alias, mrport); + } + } + it = consumer.m_toSync.erase(it); + } + else + it++; + } + else + { + if (AddL2mcGroupMember(l2mcKey, vlan_alias, port)) + it = consumer.m_toSync.erase(it); + else + it++; + } + } + else if (op == DEL_COMMAND) + { + if (hasL2mcGroup(vlan_alias, l2mcKey)) + { + if (RemoveL2mcGroupMember(l2mcKey, vlan_alias, port)) + { + if (isMemberRefCntZero(vlan_alias, l2mcKey)) + RemoveL2mcEntry(l2mcKey, vlan); + + it = consumer.m_toSync.erase(it); + } + else + it++; + } + else + it = consumer.m_toSync.erase(it); + } + else + { + SWSS_LOG_ERROR("doL2mcMemberTask: Unknown operation type %s\n", op.c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +void L2mcOrch::addMrouterPortToL2mcEntries(string vlan, Port &port) +{ + if (m_syncdL2mcEntries.find(vlan) == m_syncdL2mcEntries.end()) + return; + + /* Add mrouter port to all groups learnt on this vlan*/ + auto itr = m_syncdL2mcEntries.at(vlan).begin(); + while (itr != m_syncdL2mcEntries.at(vlan).end()) + { + auto l2mcKey = itr->first; + AddL2mcMrouterPort(l2mcKey, vlan, port); + itr++; + } + return; +} + +void L2mcOrch::removeMrouterPortFromL2mcEntries(string vlan, string mrouterport) +{ + auto iter = mrouter_ports_per_vlan[vlan].begin(); + while(iter != mrouter_ports_per_vlan[vlan].end()) + { + if (*iter == mrouterport) + { + mrouter_ports_per_vlan[vlan].erase(iter); + SWSS_LOG_NOTICE("removeMrouterPortFromL2mcEntries: Mrouter port %s deleted from vlan %s", mrouterport.c_str(), vlan.c_str()); + + /* When no l2mc entries learnt on this vlan*/ + if (m_syncdL2mcEntries.find(vlan) == m_syncdL2mcEntries.end()) + return; + + /* Remove mrouter port from all the groups learnt on this vlan if present*/ + auto itr = m_syncdL2mcEntries.at(vlan).begin(); + while (itr != m_syncdL2mcEntries.at(vlan).end()) + { + auto l2mcKey = itr->first; + RemoveL2mcMrouterPort(l2mcKey, vlan, mrouterport); + itr++; + } + break; + } + iter++; + } + return; +} + +void L2mcOrch::doL2mcMrouterTask(Consumer &consumer) +{ + if (!gPortsOrch->allPortsReady()) + { + return; + } + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + string op = kfvOp(t); + vector keys = tokenize(kfvKey(t), ':'); + + /* Key: : */ + + /* Ensure the key size is 1 otherwise ignore */ + if (keys.size() != 2) + { + SWSS_LOG_ERROR("Invalid key size, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + /* Ensure the key starts with "Vlan" otherwise ignore */ + if (strncmp(keys[0].c_str(), VLAN_PREFIX, 4)) + { + SWSS_LOG_ERROR("doL2mcMrouterTask: Invalid key format. No 'Vlan' prefix: %s", keys[0].c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + unsigned short vlan_id; + std::string port_alias, vlan_alias; + Port port, vlan; + + vlan_id = (unsigned short) stoi(keys[0].substr(4)); + vlan_alias = VLAN_PREFIX + to_string(vlan_id); + port_alias = keys[1]; + + if (!gPortsOrch->getPort(port_alias, port)) + { + SWSS_LOG_ERROR("doL2mcMrouterTask: Failed to get port for %s alias", port_alias.c_str()); + l2mcdbg_counters.l2mc_port_fail++; + continue; + } + + if (!gPortsOrch->getPort(vlan_alias, vlan)) + { + SWSS_LOG_NOTICE("doL2mcMrouterTask: Failed to locate vlan %s", vlan_alias.c_str()); + l2mcdbg_counters.l2mc_vlan_fail++; + continue; + } + + if (op == SET_COMMAND) + { + mrouter_ports_per_vlan[vlan_alias].push_back(port_alias); + addMrouterPortToL2mcEntries(vlan_alias, port); + SWSS_LOG_NOTICE("doL2mcMrouterTask: Mrouter port %s added to vlan %s", port_alias.c_str(), vlan_alias.c_str()); + } + else if (op == DEL_COMMAND) + { + auto mrouter_ports = mrouter_ports_per_vlan[vlan_alias]; + + if(!mrouter_ports.empty()) + removeMrouterPortFromL2mcEntries(vlan_alias, port_alias); + } + else + { + SWSS_LOG_ERROR("doL2mcMrouterTask: Unknown operation type %s\n", op.c_str()); + } + it = consumer.m_toSync.erase(it); + } +} + +void L2mcOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + string table_name = consumer.getTableName(); + + if (table_name == APP_L2MC_VLAN_TABLE_NAME) + { + SWSS_LOG_INFO("Received APP_L2MC_VLAN_TABLE_NAME update"); + doL2mcTask(consumer); + } + else if (table_name == APP_L2MC_MEMBER_TABLE_NAME) + { + SWSS_LOG_INFO("Received APP_L2MC_MEMBER_TABLE_NAME update"); + doL2mcMemberTask(consumer); + } + else if (table_name == APP_L2MC_MROUTER_TABLE_NAME) + { + SWSS_LOG_INFO("Received APP_L2MC_MROUTER_TABLE_NAME update"); + doL2mcMrouterTask(consumer); + } + else + { + SWSS_LOG_ERROR("Unknown L2mc Table"); + return; + } +} + + + +/* + * L2mcOrch: + * show debug l2mcOrch entries ===> dumps all l2mc entries + * show debug l2mcOrch l2mcGrp2Mbrs ===> L2mc group with members + * show debug l2mcOrch counters ===> dump all internal counters + * show debug l2mcOrch dumpall ===> dump all internal db entries and counters + **/ + + +bool L2mcOrch::debugdumpCLI(KeyOpFieldsValuesTuple t) +{ + string keywd = kfvKey(t); + string group = "dumpall"; + string vlan = ""; + + SWSS_LOG_ENTER(); + + if (keywd != gL2mcOrchDbgComp) + { + return true; + } + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "group") + group = fvValue(i); + else if(fvField(i) == "vlan") + vlan = fvValue(i); + else + { + string field = fvField(i); + string value = fvValue(i); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp, "L2mcOrch: Rcvd field %s, Value %s", field.c_str(),value.c_str()); + } + } + + if (group == "entries") + { + if (!vlan.empty()) + debugdump_l2mcEntries(vlan, false); + else + { + for (auto &v : m_snoop_enabled_vlans) + debugdump_l2mcEntries(v, false); + } + } + else if (group == "grp2members") + { + if (!vlan.empty()) + debugdump_l2mcEntries(vlan, true); + else + { + for (auto &v : m_snoop_enabled_vlans) + debugdump_l2mcEntries(v, true); + } + } + else if (group == "counters") + { + debugdump_l2mcDbgCounters(); + } + else if (group == "mrouter_ports") + { + if (!vlan.empty()) + debugdump_l2mcMrouterPorts(vlan); + else + { + for (auto &v : m_snoop_enabled_vlans) + debugdump_l2mcMrouterPorts(v); + } + } + else if (group == "snoop_vlans") + { + debugShowSnoopVlans(); + } + else if (group == "all") + { + l2mcDbgDumpAll(); + } + else + { + SWSS_LOG_DEBUG("UnSupported Group/Option %s for L2mcOrch", group.c_str()); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp, "L2mcOrch: UnSupported Group/Option %s", group.c_str()); + } + return true; +} + + +void L2mcOrch::clearAllL2mcDbgCounters() +{ + memset (&l2mcdbg_counters, 0, sizeof (struct L2mcDebugCounters)); + SWSS_LOG_INFO("L2mcOrch cleared all L2mc Debug counters"); + return; +} + +void L2mcOrch::debugdump_l2mcGrps2Mbrs(string vlan, L2mcGroupMembers &l2mcgrpMembers) +{ + SWSS_LOG_ENTER(); + + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp, "%-20s %-25s %-20s Members_OID", "", "", ""); + + auto it = l2mcgrpMembers.begin(); + while(it != l2mcgrpMembers.end()) + { + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp, "%-20s %-25s %-20s 0x%lx", + "", "", "", it->second); + + it++; + } + return; +} + + +void L2mcOrch::debugdump_l2mcEntries(string vlan, bool members) +{ + SWSS_LOG_ENTER(); + + if (m_syncdL2mcEntries.find(vlan) == m_syncdL2mcEntries.end()) + { + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp, " No L2mc entries found on %s \n", vlan.c_str()); + return; + } + + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp, "------------L2mc Entry Table ------------------------------------------------------------\n"); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp, "Source Group Vlan L2mcGrp-SAI-OID:MbrsCnt" ); + + int l2mc_entry_count=0; + auto it = m_syncdL2mcEntries.at(vlan).begin(); + while (it != m_syncdL2mcEntries.at(vlan).end()) + { + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp, "\n%-20s %-25s %-20s 0x%lx %d", + it->first.source_address.to_string().c_str(), it->first.group_address.to_string().c_str(), it->first.vlan_alias.c_str(), + it->second.l2mc_group_id, it->second.ref_count); + + if(members) + debugdump_l2mcGrps2Mbrs(vlan, it->second.l2mc_group_members); + + it++; + l2mc_entry_count++; + } + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp, "\n Total number of L2mc entries : %d\n", l2mc_entry_count); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp, "------------L2mc Entry Table End---------------------------------------------------------\n"); + return; +} + +void L2mcOrch::debugdump_l2mcMrouterPorts(string vlan) +{ + SWSS_LOG_ENTER(); + + auto mrouter_ports = mrouter_ports_per_vlan[vlan]; + + if(!mrouter_ports.empty()) + { + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp, "%s Mrouter List:", vlan.c_str()); + + for (const auto& mrouter: mrouter_ports) + { + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp, "%s, ", mrouter.c_str()); + } + } + else + { + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp, "L2mc Mrouter Port table is empty on vlan %s", vlan.c_str()); + } + return; +} + +void L2mcOrch::debugShowSnoopVlans() +{ + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp, "IGMP Snooping Enabled Vlans:"); + for (auto &v : m_snoop_enabled_vlans) + { + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp, " %s", v.c_str()); + } +} + +void L2mcOrch::debugdump_l2mcDbgCounters() +{ + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc Counters"); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"----------------------------------------------"); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"Total L2mc Entries : %d", (l2mcdbg_counters.l2mc_entry_add - l2mcdbg_counters.l2mc_entry_del)); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc Entry Add : %d", l2mcdbg_counters.l2mc_entry_add); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc Entry Delete : %d", l2mcdbg_counters.l2mc_entry_del); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc Group Add : %d", l2mcdbg_counters.l2mc_group_add); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc Group Delete : %d", l2mcdbg_counters.l2mc_group_del); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc Member Add : %d", l2mcdbg_counters.l2mc_member_add); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc Member Delete : %d", l2mcdbg_counters.l2mc_member_del); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc Entry Add Fail : %d", l2mcdbg_counters.l2mc_entry_add_fail); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc Entry Delete Fail : %d", l2mcdbg_counters.l2mc_entry_del_fail); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc Group Add Fail : %d", l2mcdbg_counters.l2mc_group_add_fail); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc Group Delete Fail : %d", l2mcdbg_counters.l2mc_group_del_fail); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc Member Add fail : %d", l2mcdbg_counters.l2mc_member_add_fail); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc Member Delete Fail : %d", l2mcdbg_counters.l2mc_member_del_fail); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc Port get Fail : %d", l2mcdbg_counters.l2mc_port_fail); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc Port OID NULL : %d", l2mcdbg_counters.l2mc_port_oid_fail); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc vlan get Fail : %d", l2mcdbg_counters.l2mc_vlan_fail); + SWSS_DEBUG_PRINT(gL2mcOrchDbgComp,"L2mc vlan OID NULL : %d", l2mcdbg_counters.l2mc_vlan_oid_fail); + + return; +} + +void L2mcOrch::l2mcDbgDumpAll() +{ + debugShowSnoopVlans(); + + for (auto &v : m_snoop_enabled_vlans) + { + debugdump_l2mcMrouterPorts(v); + debugdump_l2mcEntries(v, true); + } + debugdump_l2mcDbgCounters(); +} diff --git a/orchagent/l2mcorch.h b/orchagent/l2mcorch.h new file mode 100644 index 0000000000..be2db6d629 --- /dev/null +++ b/orchagent/l2mcorch.h @@ -0,0 +1,138 @@ +#ifndef SWSS_L2MCORCH_H +#define SWSS_L2MCORCH_H + +#include "orch.h" +#include "observer.h" +#include "intfsorch.h" +#include "debugdumporch.h" + +#include "ipaddress.h" +#include "ipaddresses.h" +#include "ipprefix.h" +#include "tokenize.h" + +#include +#include +#include + +struct L2mcGroupKey +{ + std::string vlan_alias; + IpAddress source_address; + IpAddress group_address; + + L2mcGroupKey() = default; + + L2mcGroupKey(std::string vlan, IpAddress srcAddress, IpAddress grpAddress) + { + vlan_alias = vlan; + source_address = srcAddress; + group_address = grpAddress; + }; + + bool operator<(const L2mcGroupKey& o) const + { + return tie(vlan_alias, source_address, group_address) < tie(o.vlan_alias, o.source_address, o.group_address); + } + + bool operator==(const L2mcGroupKey& o) const + { + return tie(vlan_alias, source_address, group_address) == tie(o.vlan_alias, o.source_address, o.group_address); + } +}; + +typedef std::map L2mcGroupMembers; + +struct L2mcGroupEntry +{ + sai_object_id_t l2mc_group_id; // l2mc group id + int ref_count; // group members reference count + L2mcGroupMembers l2mc_group_members; // ids of members indexed by +}; + +/* L2mcEntryTable: L2mcGroupKey, L2mcGroupEntry */ +typedef std::map L2mcEntryTable; +/*L2mcEntryTables: Vlan, L2mcEntryTable */ +typedef std::map L2mcEntryTables; +/*mrouter_ports: Vlan, mrouter_ports */ +typedef unordered_map> mrouter_ports; + +struct L2mcDebugCounters +{ + int l2mc_entry_add; + int l2mc_entry_del; + int l2mc_group_add; + int l2mc_group_del; + int l2mc_member_add; + int l2mc_member_del; + int l2mc_member_add_fail; + int l2mc_member_del_fail; + int l2mc_group_add_fail; + int l2mc_group_del_fail; + int l2mc_entry_add_fail; + int l2mc_entry_del_fail; + int l2mc_mrouter_add; + int l2mc_mrouter_del; + int l2mc_mrouter_add_fail; + int l2mc_mrouter_del_fail; + int l2mc_vlan_fail; + int l2mc_port_fail; + int l2mc_vlan_oid_fail; + int l2mc_port_oid_fail; +}; + +class L2mcOrch : public Orch, public DebugDump +{ +public: + L2mcOrch(DBConnector *appDb, vector &tableNames); + + ~L2mcOrch() + { + // do nothing + } + + bool hasL2mcGroup(string vlan, const L2mcGroupKey&); + sai_object_id_t getL2mcGroupId(string vlan, const L2mcGroupKey&); + + void increaseL2mcMemberRefCount(string vlan, const L2mcGroupKey&); + void decreaseL2mcMemberRefCount(string vlan, const L2mcGroupKey&); + bool isMemberRefCntZero(string vlan, const L2mcGroupKey&) const; + bool debugdumpCLI(KeyOpFieldsValuesTuple t); + void l2mcDbgDumpAll(); + +private: + + L2mcEntryTables m_syncdL2mcEntries; + mrouter_ports mrouter_ports_per_vlan; + vector m_snoop_enabled_vlans; + + struct L2mcDebugCounters l2mcdbg_counters; + + void doTask(Consumer &consumer); + void doL2mcTask(Consumer &consumer); + void doL2mcMemberTask(Consumer &consumer); + void doL2mcMrouterTask(Consumer &consumer); + + bool EnableIgmpSnooping(string vlan); + bool DisableIgmpSnooping(string vlan); + + bool AddL2mcEntry(const L2mcGroupKey &l2mckey, Port &vlan, Port &port); + bool RemoveL2mcEntry(const L2mcGroupKey &l2mkey, Port &vlan); + bool AddL2mcGroupMember(const L2mcGroupKey &l2mc_grpKey, string vlan, Port &port); + bool RemoveL2mcGroupMember(const L2mcGroupKey &l2mc_grpKey, string vlan, Port &port); + + bool AddL2mcMrouterPort(const L2mcGroupKey &l2mc_grpKey, string vlan, Port &port); + bool RemoveL2mcMrouterPort(const L2mcGroupKey &l2mc_grpKey, string vlan, string port); + void addMrouterPortToL2mcEntries(string vlan, Port &port); + void removeMrouterPortFromL2mcEntries(string vlan, string mrouterport); + + void clearAllL2mcDbgCounters(); + void debugdump_l2mcGrps2Mbrs(string vlan, L2mcGroupMembers &l2mcgrpMembers); + void debugdump_l2mcEntries(string vlan, bool val); + void debugdump_l2mcMrouterPorts(string vlan); + void debugShowSnoopVlans(); + void debugdump_l2mcDbgCounters(); + string gL2mcOrchDbgComp; +}; + +#endif /* SWSS_L2MCORCH_H */ diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 26ff1f0301..d447cc3f92 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -36,7 +36,7 @@ BufferOrch *gBufferOrch; SwitchOrch *gSwitchOrch; Directory gDirectory; NatOrch *gNatOrch; - +L2mcOrch *gL2mcOrch; bool gIsNatSupported = false; OrchDaemon::OrchDaemon(DBConnector *applDb, DBConnector *configDb, DBConnector *stateDb) : @@ -89,6 +89,13 @@ bool OrchDaemon::init() TableConnector applDbFdb(m_applDb, APP_FDB_TABLE_NAME); TableConnector stateDbFdb(m_stateDb, STATE_FDB_TABLE_NAME); gFdbOrch = new FdbOrch(applDbFdb, stateDbFdb, gPortsOrch); + vector app_l2mc_tables = { + APP_L2MC_VLAN_TABLE_NAME, + APP_L2MC_MEMBER_TABLE_NAME, + APP_L2MC_MROUTER_TABLE_NAME + }; + + gL2mcOrch = new L2mcOrch(m_applDb, app_l2mc_tables); vector vnet_tables = { APP_VNET_RT_TABLE_NAME, @@ -282,6 +289,8 @@ bool OrchDaemon::init() m_orchList.push_back(vnet_orch); m_orchList.push_back(vnet_rt_orch); m_orchList.push_back(gNatOrch); + m_orchList.push_back(gL2mcOrch); + m_select = new Select(); diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 3094692df6..c290cc1547 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -31,6 +31,7 @@ #include "debugcounterorch.h" #include "directory.h" #include "natorch.h" +#include "l2mcorch.h" using namespace swss; diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index ec990d605e..22efe2c5eb 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -44,6 +44,8 @@ sai_bmtor_api_t* sai_bmtor_api; sai_samplepacket_api_t* sai_samplepacket_api; sai_debug_counter_api_t* sai_debug_counter_api; sai_nat_api_t* sai_nat_api; +sai_l2mc_api_t* sai_l2mc_entry_api; +sai_l2mc_group_api_t* sai_l2mc_group_api; extern sai_object_id_t gSwitchId; extern bool gSairedisRecord; @@ -136,6 +138,9 @@ void initSaiApi() sai_api_query(SAI_API_SAMPLEPACKET, (void **)&sai_samplepacket_api); sai_api_query(SAI_API_DEBUG_COUNTER, (void **)&sai_debug_counter_api); sai_api_query(SAI_API_NAT, (void **)&sai_nat_api); + sai_api_query(SAI_API_L2MC, (void **)&sai_l2mc_entry_api); + sai_api_query(SAI_API_L2MC_GROUP, (void **)&sai_l2mc_group_api); + sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BRIDGE, SAI_LOG_LEVEL_NOTICE); @@ -165,6 +170,9 @@ void initSaiApi() sai_log_set(SAI_API_SAMPLEPACKET, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_DEBUG_COUNTER, SAI_LOG_LEVEL_NOTICE); sai_log_set((sai_api_t)SAI_API_NAT, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_L2MC, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_L2MC_GROUP, SAI_LOG_LEVEL_NOTICE); + } void initSaiRedis(const string &record_location)