From 0de72daf9ec43222d97ce320e2d0a90acbf3b04d Mon Sep 17 00:00:00 2001 From: shine4chen <37530989+shine4chen@users.noreply.github.com> Date: Sun, 22 Mar 2020 13:41:50 +0800 Subject: [PATCH] [mclag]:add mclagsyncd (#811) * add mclag support(#2514) Signed-off-by: shine.chen * Fix show acl table can't display mclag acl rules Signed-off-by: shine.chen * refine mclagsyncd code Signed-off-by: shine.chen * change APP_ACL_TABLE_NAME to APP_ACL_TABLE_TABLE_NAME Signed-off-by: shine.chen * optimize acl rule operation Signed-off-by: shine.chen * refine function name using camelcased Signed-off-by: shine.chen * refine readData function per selectable base-class change Signed-off-by: shine.chen * refine sys log Signed-off-by: shine.chen * remove binary file Signed-off-by: shine.chen * set traffic isolation using ACL_TABLE_MCLAG Signed-off-by: shine.chen Co-authored-by: shine.chen Co-authored-by: shine.chen --- Makefile.am | 3 +- configure.ac | 1 + mclagsyncd/Makefile.am | 15 + mclagsyncd/mclag.h | 164 +++++++++ mclagsyncd/mclaglink.cpp | 739 ++++++++++++++++++++++++++++++++++++++ mclagsyncd/mclaglink.h | 152 ++++++++ mclagsyncd/mclagsyncd.cpp | 98 +++++ 7 files changed, 1171 insertions(+), 1 deletion(-) create mode 100644 mclagsyncd/Makefile.am create mode 100644 mclagsyncd/mclag.h create mode 100644 mclagsyncd/mclaglink.cpp create mode 100644 mclagsyncd/mclaglink.h create mode 100644 mclagsyncd/mclagsyncd.cpp diff --git a/Makefile.am b/Makefile.am index 4f1572daeb..b2e8154888 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,5 @@ -SUBDIRS = fpmsyncd neighsyncd portsyncd natsyncd orchagent swssconfig cfgmgr tests + +SUBDIRS = fpmsyncd neighsyncd portsyncd mclagsyncd natsyncd orchagent swssconfig cfgmgr tests if HAVE_LIBTEAM SUBDIRS += teamsyncd diff --git a/configure.ac b/configure.ac index 7faa50b995..ec6c22ddf4 100644 --- a/configure.ac +++ b/configure.ac @@ -82,6 +82,7 @@ AC_CONFIG_FILES([ natsyncd/Makefile portsyncd/Makefile teamsyncd/Makefile + mclagsyncd/Makefile swssconfig/Makefile cfgmgr/Makefile tests/Makefile diff --git a/mclagsyncd/Makefile.am b/mclagsyncd/Makefile.am new file mode 100644 index 0000000000..4a72f86638 --- /dev/null +++ b/mclagsyncd/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = -I $(top_srcdir) + +bin_PROGRAMS = mclagsyncd + +if DEBUG +DBGFLAGS = -ggdb -DDEBUG +else +DBGFLAGS = -g +endif + +mclagsyncd_SOURCES = mclagsyncd.cpp mclaglink.cpp + +mclagsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) +mclagsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) +mclagsyncd_LDADD = -lnl-3 -lnl-route-3 -lswsscommon diff --git a/mclagsyncd/mclag.h b/mclagsyncd/mclag.h new file mode 100644 index 0000000000..066b919862 --- /dev/null +++ b/mclagsyncd/mclag.h @@ -0,0 +1,164 @@ +/* Copyright(c) 2016-2019 Nephos. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along with +* this program; if not, see . +* +* The full GNU General Public License is included in this distribution in +* the file called "COPYING". +* +* Maintainer: Jim Jiang from nephos +*/ + +#ifndef _MCLAG_H +#define _MCLAG_H +#define MCLAG_DEFAULT_IP 0x7f000006 + +enum MCLAG_FDB_OP_TYPE { + MCLAG_FDB_OPER_ADD =1, + MCLAG_FDB_OPER_DEL = 2, +}; + +enum MCLAG_FDB_TYPE { + MCLAG_FDB_TYPE_STATIC = 1, + MCLAG_FDB_TYPE_DYNAMIC = 2, +}; + +/* + * default port for mclag connections + */ +#define MCLAG_DEFAULT_PORT 2626 + +/* + * Largest message that can be sent to or received from the MCLAG. + */ +#define MCLAG_MAX_MSG_LEN 4096 +#define MCLAG_MAX_SEND_MSG_LEN 4096 + +typedef struct mclag_msg_hdr_t_ { + /* + * Protocol version. + */ + uint8_t version; + + /* + * Type of message, see below. + */ + uint8_t msg_type; + + /* + * Length of entire message, including the header. + */ + uint16_t msg_len; +}mclag_msg_hdr_t; + +#define MCLAG_PROTO_VERSION 1 +#define MCLAG_MSG_HDR_LEN (sizeof (mclag_msg_hdr_t)) + +/*syncd send msg type to iccpd*/ +typedef enum mclag_syncd_msg_type_e_ { + MCLAG_SYNCD_MSG_TYPE_NONE = 0, + MCLAG_SYNCD_MSG_TYPE_FDB_OPERATION = 1 +}mclag_syncd_msg_type_e; + +/*iccpd send msg type to syncd*/ +typedef enum mclag_msg_type_e_ { + MCLAG_MSG_TYPE_NONE = 0, + MCLAG_MSG_TYPE_PORT_ISOLATE = 1, + MCLAG_MSG_TYPE_PORT_MAC_LEARN_MODE = 2, + MCLAG_MSG_TYPE_FLUSH_FDB = 3, + MCLAG_MSG_TYPE_SET_INTF_MAC = 4, + MCLAG_MSG_TYPE_SET_FDB = 5, + MCLAG_MSG_TYPE_FLUSH_FDB_BY_PORT = 6, + MCLAG_MSG_TYPE_GET_FDB_CHANGES = 20 +}mclag_msg_type_e; + +typedef struct mclag_sub_option_hdr_t_ { + uint8_t op_type; + + /* + * Length of option value, not including the header. + */ + uint16_t op_len; +}mclag_sub_option_hdr_t; + +#define MCLAG_SUB_OPTION_HDR_LEN (sizeof (mclag_sub_option_hdr_t)) + +typedef enum mclag_sub_option_type_e_ { + MCLAG_SUB_OPTION_TYPE_NONE = 0, + MCLAG_SUB_OPTION_TYPE_ISOLATE_SRC = 1, + MCLAG_SUB_OPTION_TYPE_ISOLATE_DST = 2, + MCLAG_SUB_OPTION_TYPE_MAC_LEARN_ENABLE = 3, + MCLAG_SUB_OPTION_TYPE_MAC_LEARN_DISABLE = 4, + MCLAG_SUB_OPTION_TYPE_SET_MAC_SRC = 5, + MCLAG_SUB_OPTION_TYPE_SET_MAC_DST = 6 +} mclag_sub_option_type_e; + +static inline size_t +mclag_msg_len (const mclag_msg_hdr_t *hdr) +{ + return hdr->msg_len; +} + +/* + * mclag_msg_data_len + */ +static inline size_t +mclag_msg_data_len (const mclag_msg_hdr_t *hdr) +{ + return (mclag_msg_len (hdr) - MCLAG_MSG_HDR_LEN); +} + +/* + * mclag_msg_hdr_ok + * + * Returns TRUE if a message header looks well-formed. + */ +static inline int +mclag_msg_hdr_ok (const mclag_msg_hdr_t *hdr) +{ + size_t msg_len; + + if (hdr->msg_type == MCLAG_MSG_TYPE_NONE) + return 0; + + msg_len = mclag_msg_len (hdr); + + if (msg_len < MCLAG_MSG_HDR_LEN || msg_len > MCLAG_MAX_MSG_LEN) + return 0; + + return 1; +} + +/* + * mclag_msg_ok + * + * Returns TRUE if a message looks well-formed. + * + * @param len The length in bytes from 'hdr' to the end of the buffer. + */ +static inline int +mclag_msg_ok (const mclag_msg_hdr_t *hdr, size_t len) +{ + if (len < MCLAG_MSG_HDR_LEN) + return 0; + + if (!mclag_msg_hdr_ok (hdr)) + return 0; + + if (mclag_msg_len (hdr) > len) + return 0; + + return 1; +} + + +#endif diff --git a/mclagsyncd/mclaglink.cpp b/mclagsyncd/mclaglink.cpp new file mode 100644 index 0000000000..369dff08f0 --- /dev/null +++ b/mclagsyncd/mclaglink.cpp @@ -0,0 +1,739 @@ +/* Copyright(c) 2016-2019 Nephos. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: Jim Jiang from nephos + */ + +#include +#include +#include +#include +#include +#include "logger.h" +#include "netmsg.h" +#include "netdispatcher.h" +#include "swss/notificationproducer.h" +#include "mclagsyncd/mclaglink.h" +#include "mclagsyncd/mclag.h" +#include +#include + +using namespace swss; +using namespace std; + +void MclagLink::getOidToPortNameMap(std::unordered_map & port_map) +{ + std::unordered_map::iterator it; + auto hash = p_redisClient_to_counters->hgetall("COUNTERS_PORT_NAME_MAP"); + + for (it = hash.begin(); it != hash.end(); ++it) + port_map.insert(pair(it->second, it->first)); + + return; +} + +void MclagLink::getBridgePortIdToAttrPortIdMap(std::map *oid_map) +{ + std::string bridge_port_id; + size_t pos1 = 0; + + std::unordered_map::iterator attr_port_id; + + auto keys = p_redisClient_to_asic->keys("ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:*"); + + for (auto& key : keys) + { + pos1 = key.find("oid:", 0); + bridge_port_id = key.substr(pos1); + + auto hash = p_redisClient_to_asic->hgetall(key); + attr_port_id = hash.find("SAI_BRIDGE_PORT_ATTR_PORT_ID"); + if (attr_port_id == hash.end()) + { + attr_port_id = hash.find("SAI_BRIDGE_PORT_ATTR_TUNNEL_ID"); + if (attr_port_id == hash.end()) + continue; + } + + oid_map->insert(pair(bridge_port_id, attr_port_id->second)); + } + + return; +} + +void MclagLink::getVidByBvid(std::string &bvid, std::string &vlanid) +{ + std::unordered_map::iterator attr_vlan_id; + std::string pre = "ASIC_STATE:SAI_OBJECT_TYPE_VLAN:"; + std::string key = pre + bvid; + + auto hash = p_redisClient_to_asic->hgetall(key.c_str()); + + attr_vlan_id = hash.find("SAI_VLAN_ATTR_VLAN_ID"); + if (attr_vlan_id == hash.end()) + return; + + vlanid = attr_vlan_id->second; + return; +} + +void MclagLink::getFdbSet(std::set *fdb_set) +{ + string bvid; + string bri_port_id; + string port_name; + string mac; + string type; + string vlanid; + int vid; + size_t pos1 = 0; + size_t pos2 = 0; + std::unordered_map oid_to_portname_map; + std::map brPortId_to_attrPortId_map; + std::unordered_map::iterator type_it; + std::unordered_map::iterator brPortId_it; + std::map::iterator brPortId_to_attrPortId_it; + std::unordered_map::iterator oid_to_portName_it; + + auto keys = p_redisClient_to_asic->keys("ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:*"); + + for (auto& key : keys) + { + /*get vid*/ + pos1 = key.find("vlan", 0); + if (pos1 != key.npos) + { + pos1 = pos1 + 7; + pos2 = key.find(",", pos1) - 2; + vlanid = key.substr(pos1, pos2 - pos1 + 1); + } + else + { + pos1 = key.find("oid:", 0); + pos2 = key.find(",", 0) - 2; + bvid = key.substr(pos1, pos2 - pos1 + 1); + getVidByBvid(bvid, vlanid); + } + + vid = atoi(vlanid.c_str()); + /*get mac*/ + pos1 = key.find("mac", 0) + 6; + pos2 = key.find(",", pos1) - 2; + mac = key.substr(pos1, pos2 - pos1 + 1); + + /*get type*/ + auto hash = p_redisClient_to_asic->hgetall(key); + type_it = hash.find("SAI_FDB_ENTRY_ATTR_TYPE"); + if (type_it == hash.end()) + { + continue; + } + + if (memcmp(type_it->second.c_str(), "SAI_FDB_ENTRY_TYPE_DYNAMIC", type_it->second.length()) == 0) + type = "dynamic"; + else + type = "static"; + + /*get port name*/ + getOidToPortNameMap(oid_to_portname_map); + getBridgePortIdToAttrPortIdMap(&brPortId_to_attrPortId_map); + brPortId_it = hash.find("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID"); + if (brPortId_it == hash.end()) + { + continue; + } + bri_port_id = brPortId_it->second; + + brPortId_to_attrPortId_it = brPortId_to_attrPortId_map.find(bri_port_id); + if (brPortId_to_attrPortId_it == brPortId_to_attrPortId_map.end()) + { + continue; + } + + oid_to_portName_it = oid_to_portname_map.find(brPortId_to_attrPortId_it->second); + if (oid_to_portName_it == oid_to_portname_map.end()) + { + continue; + } + + port_name = oid_to_portName_it->second; + + /*insert set*/ + SWSS_LOG_DEBUG("Read one fdb entry(MAC:%s, vid:%d, port_name:%s, type:%s) from ASIC_DB and insert new_set.", mac.c_str(), vid, port_name.c_str(), type.c_str()); + fdb_set->insert(mclag_fdb(mac, vid, port_name, type)); + } + + return; +} + +void MclagLink::setPortIsolate(char *msg) +{ + mclag_sub_option_hdr_t *op_hdr = NULL; + string isolate_src_port; + string isolate_dst_port; + char * cur = NULL; + string acl_name = "mclag"; + string acl_rule_name = "mclag:mclag"; + vector acl_attrs; + vector acl_rule_attrs; + std::string acl_key = std::string("") + APP_ACL_TABLE_TABLE_NAME + ":" + acl_name; + std::string acl_rule_key = std::string("") + APP_ACL_RULE_TABLE_NAME + ":" + acl_rule_name; + static int acl_table_is_added = 0; + + cur = msg; + + /*get isolate src port infor*/ + op_hdr = (mclag_sub_option_hdr_t *)cur; + cur = cur + MCLAG_SUB_OPTION_HDR_LEN; + isolate_src_port.insert(0, (const char*)cur, op_hdr->op_len); + + cur = cur + op_hdr->op_len; + + /*get isolate dst ports infor*/ + op_hdr = (mclag_sub_option_hdr_t *)cur; + cur = cur + MCLAG_SUB_OPTION_HDR_LEN; + isolate_dst_port.insert(0, (const char*)cur, op_hdr->op_len); + + if (op_hdr->op_len == 0) + { + /* If dst port is NULL, delete the acl table 'mclag' */ + p_acl_table_tbl->del(acl_name); + acl_table_is_added = 0; + SWSS_LOG_DEBUG("Disable port isolate, src port: %s, dst port is NULL", + isolate_src_port.c_str()); + return; + } + + SWSS_LOG_DEBUG("Set port isolate, src port: %s, dst port: %s", + isolate_src_port.c_str(), isolate_dst_port.c_str()); + + if (acl_table_is_added == 0) + { + /*First create ACL table*/ + FieldValueTuple desc_attr("policy_desc", "Mclag egress port isolate acl"); + acl_attrs.push_back(desc_attr); + + FieldValueTuple type_attr("type", "MCLAG"); + acl_attrs.push_back(type_attr); + + FieldValueTuple port_attr("ports", isolate_src_port); + acl_attrs.push_back(port_attr); + + p_acl_table_tbl->set(acl_name, acl_attrs); + + acl_table_is_added = 1; + /*End create ACL table*/ + } + + /*Then create ACL rule table*/ + FieldValueTuple ip_type_attr("IP_TYPE", "ANY"); + acl_rule_attrs.push_back(ip_type_attr); + + FieldValueTuple out_port_attr("OUT_PORTS", isolate_dst_port); + acl_rule_attrs.push_back(out_port_attr); + + FieldValueTuple packet_attr("PACKET_ACTION", "DROP"); + acl_rule_attrs.push_back(packet_attr); + + p_acl_rule_tbl->set(acl_rule_name, acl_rule_attrs); + /*End create ACL rule table*/ + + return; +} + +void MclagLink::setPortMacLearnMode(char *msg) +{ + string learn_port; + string learn_mode; + mclag_sub_option_hdr_t *op_hdr = NULL; + char * cur = NULL; + + cur = msg; + + /*get port learning mode info*/ + op_hdr = (mclag_sub_option_hdr_t *)cur; + if (op_hdr->op_type == MCLAG_SUB_OPTION_TYPE_MAC_LEARN_ENABLE) + { + learn_mode = "hardware"; + } + else if (op_hdr->op_type == MCLAG_SUB_OPTION_TYPE_MAC_LEARN_DISABLE) + { + learn_mode = "disable"; + } + + cur = cur + MCLAG_SUB_OPTION_HDR_LEN; + + learn_port.insert(0, (const char*)cur, op_hdr->op_len); + + vector attrs; + FieldValueTuple learn_attr("learn_mode", learn_mode); + attrs.push_back(learn_attr); + if (strncmp(learn_port.c_str(), PORTCHANNEL_PREFIX, strlen(PORTCHANNEL_PREFIX)) == 0) + p_lag_tbl->set(learn_port, attrs); + /*vxlan tunnel dont supported currently, for src_ip is the mandatory attribute*/ + /*else if(strncmp(learn_port.c_str(),VXLAN_TUNNEL_PREFIX,5)==0) + p_tnl_tbl->set(learn_port, attrs); */ + else + p_port_tbl->set(learn_port, attrs); + + SWSS_LOG_DEBUG("Set port mac learn mode, port: %s, learn-mode: %s", + learn_port.c_str(), learn_mode.c_str()); + + return; +} + +void MclagLink::setFdbFlush() +{ + swss::NotificationProducer flushFdb(p_appl_db, "FLUSHFDBREQUEST"); + + vector values; + + SWSS_LOG_DEBUG("Send fdb flush notification"); + + flushFdb.send("ALL", "ALL", values); + + return; +} + +void MclagLink::setFdbFlushByPort(char *msg) +{ + string port; + char *cur = NULL; + mclag_sub_option_hdr_t *op_hdr = NULL; + swss::NotificationProducer flushFdb(p_appl_db, "FLUSHFDBREQUEST"); + vector values; + + cur = msg; + /*get port infor*/ + op_hdr = (mclag_sub_option_hdr_t *)cur; + cur = cur + MCLAG_SUB_OPTION_HDR_LEN; + port.insert(0, (const char*)cur, op_hdr->op_len); + + SWSS_LOG_DEBUG("Send fdb flush by port %s notification", port.c_str()); + + flushFdb.send("ALL", port, values); + + return; +} + +void MclagLink::setIntfMac(char *msg) +{ + mclag_sub_option_hdr_t *op_hdr = NULL; + string intf_key; + string mac_value; + char *cur = NULL; + + cur = msg; + + /*get intf key name*/ + op_hdr = (mclag_sub_option_hdr_t *)cur; + cur = cur + MCLAG_SUB_OPTION_HDR_LEN; + intf_key.insert(0, (const char*)cur, op_hdr->op_len); + + cur = cur + op_hdr->op_len; + + /*get mac*/ + op_hdr = (mclag_sub_option_hdr_t *)cur; + cur = cur + MCLAG_SUB_OPTION_HDR_LEN; + mac_value.insert(0, (const char*)cur, op_hdr->op_len); + + SWSS_LOG_DEBUG("Set mac to chip, intf key name: %s, mac: %s", intf_key.c_str(), mac_value.c_str()); + vector attrs; + FieldValueTuple mac_attr("mac_addr", mac_value); + attrs.push_back(mac_attr); + p_intf_tbl->set(intf_key, attrs); + + return; +} + +void MclagLink::setFdbEntry(char *msg, int msg_len) +{ + struct mclag_fdb_info * fdb_info = NULL; + struct mclag_fdb fdb; + string fdb_key; + char key[64] = { 0 }; + char *cur = NULL; + short count = 0; + int index = 0; + int exist = 0; + set ::iterator it; + + cur = msg; + count = (short)(msg_len / sizeof(struct mclag_fdb_info)); + + for (index = 0; index < count; index++) + { + memset(key, 0, 64); + + fdb_info = (struct mclag_fdb_info *)(cur + index * sizeof(struct mclag_fdb_info)); + + fdb.mac = fdb_info->mac; + fdb.port_name = fdb_info->port_name; + fdb.vid = fdb_info->vid; + if (fdb_info->type == MCLAG_FDB_TYPE_STATIC) + fdb.type = "static"; + else + fdb.type = "dynamic"; + + if ((it = find(p_old_fdb->begin(), p_old_fdb->end(), fdb)) == p_old_fdb->end()) + exist = 0; + else + exist = 1; + + snprintf(key, 64, "%s%d:%s", "Vlan", fdb_info->vid, fdb_info->mac); + fdb_key = key; + + if (fdb_info->op_type == MCLAG_FDB_OPER_ADD) + { + vector attrs; + + /*set port attr*/ + FieldValueTuple port_attr("port", fdb.port_name); + attrs.push_back(port_attr); + + /*set type attr*/ + FieldValueTuple type_attr("type", fdb.type); + attrs.push_back(type_attr); + + if (exist == 0) + { + p_old_fdb->insert(fdb); + SWSS_LOG_DEBUG("Insert node(portname =%s, mac =%s, vid =%d, type =%s) into old_fdb_set", + fdb.port_name.c_str(), fdb.mac.c_str(), fdb.vid, fdb.type.c_str()); + } + else + { + if (it->port_name == fdb.port_name && it->type == fdb.type) + { + SWSS_LOG_DEBUG("All items of mac is same (mac =%s, vid =%d, portname :%s ==> %s, type:%s ==>%s), return.", + fdb.mac.c_str(), fdb.vid, it->port_name.c_str(), fdb.port_name.c_str(), it->type.c_str(), fdb.type.c_str()); + return; + } + SWSS_LOG_DEBUG("Modify node(mac =%s, vid =%d, portname :%s ==> %s, type:%s ==>%s)", + fdb.mac.c_str(), fdb.vid, it->port_name.c_str(), fdb.port_name.c_str(), it->type.c_str(), fdb.type.c_str()); + p_old_fdb->erase(it); + p_old_fdb->insert(fdb); + #if 0 + fdb_entry = &(*it); + fdb_entry->port_name = fdb.port_name; + fdb_entry->type = fdb.type; + #endif + } + + p_fdb_tbl->set(fdb_key, attrs); + SWSS_LOG_DEBUG("Add fdb entry into ASIC_DB:key =%s, type =%s", fdb_key.c_str(), fdb.type.c_str()); + } + else if (fdb_info->op_type == MCLAG_FDB_OPER_DEL) + { + if (exist) + { + SWSS_LOG_DEBUG("Erase node(portname =%s, mac =%s, vid =%d, type =%s) from old_fdb_set", + it->port_name.c_str(), it->mac.c_str(), it->vid, it->type.c_str()); + p_old_fdb->erase(it); + } + p_fdb_tbl->del(fdb_key); + SWSS_LOG_DEBUG("Del fdb entry from ASIC_DB:key =%s", fdb_key.c_str()); + } + } + + return; +} + +ssize_t MclagLink::getFdbChange(char *msg_buf) +{ + set new_fdb; + set del_fdb; + set add_fdb; + struct mclag_fdb_info info; + mclag_msg_hdr_t * msg_head = NULL; + ssize_t write = 0; + size_t infor_len = 0; + char *infor_start = msg_buf; + set *p_new_fdb = &new_fdb; + + del_fdb.clear(); + add_fdb.clear(); + p_new_fdb->clear(); + + infor_len = infor_len + sizeof(mclag_msg_hdr_t); + + getFdbSet(p_new_fdb); + + set_difference(p_old_fdb->begin(), p_old_fdb->end(), p_new_fdb->begin(), + p_new_fdb->end(), inserter(del_fdb, del_fdb.begin())); + set_difference(p_new_fdb->begin(), p_new_fdb->end(), p_old_fdb->begin(), + p_old_fdb->end(), inserter(add_fdb, add_fdb.begin())); + + p_old_fdb->swap(*p_new_fdb); + + /*Remove the same item from del set, this may be MAC move*/ + auto itdel = del_fdb.begin(); + while (itdel != del_fdb.end()) + { + auto ittmp = itdel; + itdel++; + for (auto itadd = add_fdb.begin(); itadd != add_fdb.end(); itadd++) + { + if (ittmp->mac == itadd->mac && ittmp->vid == itadd->vid) + { + SWSS_LOG_DEBUG("Mac move: mac %s, vid %d, portname %s, type %s", + ittmp->mac.c_str(), ittmp->vid, ittmp->port_name.c_str(), ittmp->type.c_str()); + del_fdb.erase(ittmp); + break; + } + } + } + + for (auto it = del_fdb.begin(); it != del_fdb.end(); it++) + { + if (MCLAG_MAX_SEND_MSG_LEN - infor_len < sizeof(struct mclag_fdb_info)) + { + msg_head = (mclag_msg_hdr_t *)infor_start; + msg_head->version = 1; + msg_head->msg_len = (unsigned short)infor_len; + msg_head->msg_type = MCLAG_SYNCD_MSG_TYPE_FDB_OPERATION; + + SWSS_LOG_DEBUG("Mclagsycnd send msg to iccpd, msg_len =%d, msg_type =%d", + msg_head->msg_len, msg_head->msg_type); + write = ::write(m_connection_socket, infor_start, msg_head->msg_len); + if (write <= 0) + return write; + + infor_len = sizeof(mclag_msg_hdr_t); + } + SWSS_LOG_DEBUG("Notify iccpd to del fdb_entry:mac:%s, vid:%d, portname:%s, type:%s", + it->mac.c_str(), it->vid, it->port_name.c_str(), it->type.c_str()); + memset(&info, 0, sizeof(struct mclag_fdb_info)); + info.op_type = MCLAG_FDB_OPER_DEL; + memcpy(info.mac, it->mac.c_str(), it->mac.length()); + info.vid = it->vid; + memcpy(info.port_name, it->port_name.c_str(), it->port_name.length()); + if (memcmp(it->type.c_str(), "SAI_FDB_ENTRY_TYPE_DYNAMIC", it->type.length()) == 0) + info.type = MCLAG_FDB_TYPE_DYNAMIC; + else + info.type = MCLAG_FDB_TYPE_STATIC; + + memcpy((char*)(infor_start + infor_len), (char*)&info, sizeof(struct mclag_fdb_info)); + infor_len = infor_len + sizeof(struct mclag_fdb_info); + } + + for (auto it = add_fdb.begin(); it != add_fdb.end(); it++) + { + if (MCLAG_MAX_SEND_MSG_LEN - infor_len < sizeof(struct mclag_fdb_info)) + { + msg_head = (mclag_msg_hdr_t *)infor_start; + msg_head->version = 1; + msg_head->msg_len = (unsigned short)infor_len; + msg_head->msg_type = MCLAG_SYNCD_MSG_TYPE_FDB_OPERATION; + + /*SWSS_LOG_DEBUG("Mclagsycnd send msg to iccpd, msg_len =%d, msg_type =%d", + msg_head->msg_len, msg_head->msg_type);*/ + write = ::write(m_connection_socket, infor_start, msg_head->msg_len); + if (write <= 0) + return write; + + infor_len = sizeof(mclag_msg_hdr_t); + } + SWSS_LOG_DEBUG("Notify iccpd to add fdb_entry:mac:%s, vid:%d, portname:%s, type:%s", + it->mac.c_str(), it->vid, it->port_name.c_str(), it->type.c_str()); + memset(&info, 0, sizeof(struct mclag_fdb_info)); + info.op_type = MCLAG_FDB_OPER_ADD; + memcpy(info.mac, it->mac.c_str(), it->mac.length()); + info.vid = it->vid; + memcpy(info.port_name, it->port_name.c_str(), it->port_name.length()); + if (memcmp(it->type.c_str(), "dynamic", it->type.length()) == 0) + info.type = MCLAG_FDB_TYPE_DYNAMIC; + else + info.type = MCLAG_FDB_TYPE_STATIC; + + memcpy((char*)(infor_start + infor_len), (char*)&info, sizeof(struct mclag_fdb_info)); + infor_len = infor_len + sizeof(struct mclag_fdb_info); + } + + if (infor_len <= sizeof(mclag_msg_hdr_t)) /*no fdb entry need notifying iccpd*/ + return 1; + + msg_head = (mclag_msg_hdr_t *)infor_start; + msg_head->version = 1; + msg_head->msg_len = (unsigned short)infor_len; + msg_head->msg_type = MCLAG_SYNCD_MSG_TYPE_FDB_OPERATION; + + /*SWSS_LOG_DEBUG("Mclagsycnd send msg to iccpd, msg_len =%d, msg_type =%d", + msg_head->msg_len, msg_head->msg_type);*/ + write = ::write(m_connection_socket, infor_start, msg_head->msg_len); + + return write; +} + +MclagLink::MclagLink(int port) : + MSG_BATCH_SIZE(256), + m_bufSize(MCLAG_MAX_MSG_LEN * MSG_BATCH_SIZE), + m_messageBuffer(NULL), + m_pos(0), + m_connected(false), + m_server_up(false) +{ + struct sockaddr_in addr; + int true_val = 1; + + m_server_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_server_socket < 0) + throw system_error(errno, system_category()); + + if (setsockopt(m_server_socket, SOL_SOCKET, SO_REUSEADDR, &true_val, + sizeof(true_val)) < 0) + { + close(m_server_socket); + throw system_error(errno, system_category()); + } + + if (setsockopt(m_server_socket, SOL_SOCKET, SO_KEEPALIVE, &true_val, + sizeof(true_val)) < 0) + { + close(m_server_socket); + throw system_error(errno, system_category()); + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = htonl(MCLAG_DEFAULT_IP); + + if (bind(m_server_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) + { + close(m_server_socket); + throw system_error(errno, system_category()); + } + + if (listen(m_server_socket, 2) != 0) + { + close(m_server_socket); + throw system_error(errno, system_category()); + } + + m_server_up = true; + m_messageBuffer = new char[m_bufSize]; + m_messageBuffer_send = new char[MCLAG_MAX_SEND_MSG_LEN]; +} + +MclagLink::~MclagLink() +{ + delete m_messageBuffer; + delete m_messageBuffer_send; + if (m_connected) + close(m_connection_socket); + if (m_server_up) + close(m_server_socket); +} + +void MclagLink::accept() +{ + struct sockaddr_in client_addr; + socklen_t client_len; + + m_connection_socket = ::accept(m_server_socket, (struct sockaddr *)&client_addr, + &client_len); + if (m_connection_socket < 0) + throw system_error(errno, system_category()); + + SWSS_LOG_NOTICE("New connection accepted from: %s", inet_ntoa(client_addr.sin_addr)); +} + +int MclagLink::getFd() +{ + return m_connection_socket; +} + +uint64_t MclagLink::readData() +{ + mclag_msg_hdr_t *hdr = NULL; + size_t msg_len = 0; + size_t start = 0, left = 0; + ssize_t read = 0; + ssize_t write = 0; + char * msg = NULL; + + read = ::read(m_connection_socket, m_messageBuffer + m_pos, m_bufSize - m_pos); + if (read == 0) + throw MclagConnectionClosedException(); + if (read < 0) + throw system_error(errno, system_category()); + m_pos += (uint32_t)read; + + while (true) + { + hdr = (mclag_msg_hdr_t *)(m_messageBuffer + start); + left = m_pos - start; + if (left < MCLAG_MSG_HDR_LEN) + break; + + msg_len = mclag_msg_len(hdr); + if (left < msg_len) + break; + + if (!mclag_msg_ok(hdr, left)) + throw system_error(make_error_code(errc::bad_message), "Malformed MCLAG message received"); + + msg = ((char*)hdr) + MCLAG_MSG_HDR_LEN; + + switch (hdr->msg_type) + { + case MCLAG_MSG_TYPE_PORT_ISOLATE: + setPortIsolate(msg); + break; + + case MCLAG_MSG_TYPE_PORT_MAC_LEARN_MODE: + setPortMacLearnMode(msg); + break; + + case MCLAG_MSG_TYPE_FLUSH_FDB: + setFdbFlush(); + break; + + case MCLAG_MSG_TYPE_FLUSH_FDB_BY_PORT: + setFdbFlushByPort(msg); + break; + + case MCLAG_MSG_TYPE_SET_INTF_MAC: + setIntfMac(msg); + break; + + case MCLAG_MSG_TYPE_SET_FDB: + setFdbEntry(msg, (int)(hdr->msg_len - sizeof(mclag_msg_hdr_t))); + break; + + case MCLAG_MSG_TYPE_GET_FDB_CHANGES: + write = getFdbChange(m_messageBuffer_send); + if (write == 0) + throw MclagConnectionClosedException(); + if (write < 0) + throw system_error(errno, system_category()); + break; + + default: + break; + } + + start += msg_len; + } + + memmove(m_messageBuffer, m_messageBuffer + start, m_pos - start); + m_pos = m_pos - (uint32_t)start; + + return 0; +} + diff --git a/mclagsyncd/mclaglink.h b/mclagsyncd/mclaglink.h new file mode 100644 index 0000000000..48aee59f4c --- /dev/null +++ b/mclagsyncd/mclaglink.h @@ -0,0 +1,152 @@ +/* Copyright(c) 2016-2019 Nephos. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: Jim Jiang from nephos + */ + +#ifndef __MCLAGLINK__ +#define __MCLAGLINK__ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "producerstatetable.h" +#include "selectable.h" +#include "redisclient.h" +#include "mclagsyncd/mclag.h" + +namespace swss { + +#define ETHER_ADDR_STR_LEN 18 +#define MAX_L_PORT_NAME 20 + +struct mclag_fdb_info +{ + char mac[ETHER_ADDR_STR_LEN]; + unsigned int vid; + char port_name[MAX_L_PORT_NAME]; + short type; /*dynamic or static*/ + short op_type; /*add or del*/ +}; + +struct mclag_fdb +{ + std::string mac; + unsigned int vid; + std::string port_name; + std::string type;/*dynamic or static*/ + + mclag_fdb(std::string val_mac, unsigned int val_vid, std::string val_pname, + std::string val_type) : mac(val_mac), vid(val_vid), port_name(val_pname), type(val_type) + { + } + mclag_fdb() + { + } + + bool operator <(const mclag_fdb &fdb) const + { + if (mac != fdb.mac) + return mac < fdb.mac; + else if (vid != fdb.vid) + return vid < fdb.vid; + else + return port_name < fdb.port_name; + //else if (port_name != fdb.port_name) return port_name < fdb.port_name; + //else return type *p_old_fdb; + + MclagLink(int port = MCLAG_DEFAULT_PORT); + virtual ~MclagLink(); + + /* Wait for connection (blocking) */ + void accept(); + + int getFd() override; + uint64_t readData() override; + + /* readMe throws MclagConnectionClosedException when connection is lost */ + class MclagConnectionClosedException : public std::exception + { + }; + +private: + unsigned int m_bufSize; + char *m_messageBuffer; + char *m_messageBuffer_send; + unsigned int m_pos; + + bool m_connected; + bool m_server_up; + int m_server_socket; + int m_connection_socket; + + void getOidToPortNameMap(std::unordered_map & port_map); + void getBridgePortIdToAttrPortIdMap(std::map *oid_map); + void getVidByBvid(std::string &bvid, std::string &vlanid); + void getFdbSet(std::set *fdb_set); + void setPortIsolate(char *msg); + void setPortMacLearnMode(char *msg); + void setFdbFlush(); + void setFdbFlushByPort(char *msg); + void setIntfMac(char *msg); + void setFdbEntry(char *msg, int msg_len); + ssize_t getFdbChange(char *msg_buf); + void connectionLostHandlePortIsolate(); + void connectionLostHandlePortLearnMode(); + void connectionLost(); +}; + +} +#endif + diff --git a/mclagsyncd/mclagsyncd.cpp b/mclagsyncd/mclagsyncd.cpp new file mode 100644 index 0000000000..2bacf35ac2 --- /dev/null +++ b/mclagsyncd/mclagsyncd.cpp @@ -0,0 +1,98 @@ +/* Copyright(c) 2016-2019 Nephos. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintainer: Jim Jiang from nephos + */ +#include +#include "logger.h" +#include +#include "select.h" +#include "netdispatcher.h" +#include "mclagsyncd/mclaglink.h" +#include + +using namespace std; +using namespace swss; + +int main(int argc, char **argv) +{ + swss::Logger::linkToDbNative("mclagsyncd"); + DBConnector appl_db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector asic_db(ASIC_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector counters_db(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + ProducerStateTable port_tbl(&appl_db, APP_PORT_TABLE_NAME); + ProducerStateTable lag_tbl(&appl_db, APP_LAG_TABLE_NAME); + ProducerStateTable tnl_tbl(&appl_db, APP_VXLAN_TUNNEL_TABLE_NAME); + ProducerStateTable intf_tbl(&appl_db, APP_INTF_TABLE_NAME); + ProducerStateTable fdb_tbl(&appl_db, APP_FDB_TABLE_NAME); + ProducerStateTable acl_table_tbl(&appl_db, APP_ACL_TABLE_TABLE_NAME); + ProducerStateTable acl_rule_tbl(&appl_db, APP_ACL_RULE_TABLE_NAME); + RedisClient redisClient_to_asicDb(&asic_db); + RedisClient redisClient_to_countersDb(&counters_db); + map isolate; + RedisPipeline pipeline(&appl_db); + set old_fdb; + + while (1) + { + try + { + MclagLink mclag; + Select s; + + mclag.p_port_tbl = &port_tbl; + mclag.p_lag_tbl = &lag_tbl; + mclag.p_tnl_tbl = &tnl_tbl; + mclag.p_intf_tbl = &intf_tbl; + mclag.p_fdb_tbl = &fdb_tbl; + mclag.p_acl_table_tbl = &acl_table_tbl; + mclag.p_acl_rule_tbl = &acl_rule_tbl; + mclag.p_appl_db = &appl_db; + mclag.p_redisClient_to_asic = &redisClient_to_asicDb; + mclag.p_redisClient_to_counters = &redisClient_to_countersDb; + mclag.p_old_fdb = &old_fdb; + + cout << "Waiting for connection..." << endl; + mclag.accept(); + cout << "Connected!" << endl; + + s.addSelectable(&mclag); + + while (true) + { + Selectable *temps; + + /* Reading MCLAG messages forever (and calling "readData" to read them) */ + s.select(&temps); + pipeline.flush(); + SWSS_LOG_DEBUG("Pipeline flushed"); + } + } + catch (MclagLink::MclagConnectionClosedException &e) + { + cout << "Connection lost, reconnecting..." << endl; + } + catch (const exception& e) + { + cout << "Exception \"" << e.what() << "\" had been thrown in deamon" << endl; + return 0; + } + } + + return 1; +} +