"Fossies" - the Fresh Open Source Software Archive

Member "netxms-3.8.166/src/server/core/fdb.cpp" (23 Feb 2021, 15094 Bytes) of package /linux/misc/netxms-3.8.166.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "fdb.cpp" see the Fossies "Dox" file reference documentation.

    1 /* 
    2 ** NetXMS - Network Management System
    3 ** Copyright (C) 2003-2020 Victor Kirhenshtein
    4 **
    5 ** This program is free software; you can redistribute it and/or modify
    6 ** it under the terms of the GNU General Public License as published by
    7 ** the Free Software Foundation; either version 2 of the License, or
    8 ** (at your option) any later version.
    9 **
   10 ** This program is distributed in the hope that it will be useful,
   11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
   12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13 ** GNU General Public License for more details.
   14 **
   15 ** You should have received a copy of the GNU General Public License
   16 ** along with this program; if not, write to the Free Software
   17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   18 **
   19 ** File: fdb.cpp
   20 **
   21 **/
   22 
   23 #include "nxcore.h"
   24 
   25 /**
   26  * Constructor
   27  */
   28 ForwardingDatabase::ForwardingDatabase(uint32_t nodeId)
   29 {
   30    m_nodeId = nodeId;
   31     m_fdb = nullptr;
   32     m_fdbSize = 0;
   33     m_fdbAllocated = 0;
   34     m_portMap = nullptr;
   35     m_pmSize = 0;
   36     m_pmAllocated = 0;
   37     m_timestamp = time(nullptr);
   38     m_currentVlanId = 0;
   39 }
   40 
   41 /**
   42  * Destructor
   43  */
   44 ForwardingDatabase::~ForwardingDatabase()
   45 {
   46     MemFree(m_fdb);
   47     MemFree(m_portMap);
   48 }
   49 
   50 /**
   51  * Add port mapping entry
   52  */
   53 void ForwardingDatabase::addPortMapping(PORT_MAPPING_ENTRY *entry)
   54 {
   55     if (m_pmSize == m_pmAllocated)
   56     {
   57         m_pmAllocated += 32;
   58         m_portMap = (PORT_MAPPING_ENTRY *)realloc(m_portMap, sizeof(PORT_MAPPING_ENTRY) * m_pmAllocated);
   59     }
   60     memcpy(&m_portMap[m_pmSize], entry, sizeof(PORT_MAPPING_ENTRY));
   61     m_pmSize++;
   62 }
   63 
   64 /**
   65  * Get interface index for given port number
   66  */
   67 uint32_t ForwardingDatabase::ifIndexFromPort(uint32_t port)
   68 {
   69     for(int i = 0; i < m_pmSize; i++)
   70         if (m_portMap[i].port == port)
   71             return m_portMap[i].ifIndex;
   72 
   73     // Try to lookup node interfaces because correct bridge port number may be set by driver
   74     shared_ptr<NetObj> node = FindObjectById(m_nodeId, OBJECT_NODE);
   75     if (node != nullptr)
   76     {
   77        shared_ptr<Interface> iface = static_cast<Node*>(node.get())->findBridgePort(port);
   78        if (iface != nullptr)
   79           return iface->getIfIndex();
   80     }
   81     return 0;
   82 }
   83 
   84 /**
   85  * Add entry
   86  */
   87 void ForwardingDatabase::addEntry(FDB_ENTRY *entry)
   88 {
   89     // Check for duplicate
   90     for(int i = 0; i < m_fdbSize; i++)
   91         if (!memcmp(m_fdb[i].macAddr, entry->macAddr, MAC_ADDR_LENGTH))
   92         {
   93             memcpy(&m_fdb[i], entry, sizeof(FDB_ENTRY));
   94             m_fdb[i].ifIndex = ifIndexFromPort(entry->port);
   95             return;
   96         }
   97 
   98     if (m_fdbSize == m_fdbAllocated)
   99     {
  100         m_fdbAllocated += 32;
  101         m_fdb = MemReallocArray(m_fdb, m_fdbAllocated);
  102     }
  103     memcpy(&m_fdb[m_fdbSize], entry, sizeof(FDB_ENTRY));
  104     m_fdb[m_fdbSize].ifIndex = ifIndexFromPort(entry->port);
  105     m_fdbSize++;
  106 }
  107 
  108 /**
  109  * FDB entry comparator
  110  */
  111 static int EntryComparator(const void *p1, const void *p2)
  112 {
  113     return memcmp(((FDB_ENTRY *)p1)->macAddr, ((FDB_ENTRY *)p2)->macAddr, MAC_ADDR_LENGTH);
  114 }
  115 
  116 /**
  117  * Find MAC address
  118  * Returns interface index or 0 if MAC address not found
  119  */
  120 uint32_t ForwardingDatabase::findMacAddress(const BYTE *macAddr, bool *isStatic)
  121 {
  122     FDB_ENTRY key;
  123     memcpy(key.macAddr, macAddr, MAC_ADDR_LENGTH);
  124     FDB_ENTRY *entry = (FDB_ENTRY *)bsearch(&key, m_fdb, m_fdbSize, sizeof(FDB_ENTRY), EntryComparator);
  125    if ((entry != nullptr) && (isStatic != nullptr))
  126       *isStatic = (entry->type == 5);
  127     return (entry != nullptr) ? entry->ifIndex : 0;
  128 }
  129 
  130 /**
  131  * Check if port has only one MAC in FDB
  132  * If macAddr parameter is not nullptr, MAC address found on port
  133  * copied into provided buffer
  134  */
  135 bool ForwardingDatabase::isSingleMacOnPort(uint32_t ifIndex, BYTE *macAddr)
  136 {
  137     int count = 0;
  138     for(int i = 0; i < m_fdbSize; i++)
  139         if (m_fdb[i].ifIndex == ifIndex)
  140         {
  141             count++;
  142             if (count > 1)
  143                 return false;
  144             if (macAddr != nullptr)
  145                 memcpy(macAddr, m_fdb[i].macAddr, MAC_ADDR_LENGTH);
  146         }
  147 
  148     return count == 1;
  149 }
  150 
  151 /**
  152  * Get number of MAC addresses on given port
  153  */
  154 int ForwardingDatabase::getMacCountOnPort(uint32_t ifIndex)
  155 {
  156     int count = 0;
  157     for(int i = 0; i < m_fdbSize; i++)
  158         if (m_fdb[i].ifIndex == ifIndex)
  159         {
  160             count++;
  161         }
  162 
  163     return count;
  164 }
  165 
  166 /**
  167  * Print to console
  168  */
  169 void ForwardingDatabase::print(CONSOLE_CTX ctx, Node *owner)
  170 {
  171    TCHAR macAddrStr[64];
  172 
  173    ConsolePrintf(ctx, _T("\x1b[1mMAC address\x1b[0m       | \x1b[1mIfIndex\x1b[0m | \x1b[1mInterface\x1b[0m            | \x1b[1mPort\x1b[0m | \x1b[1mType\x1b[0m    | \x1b[1mNode\x1b[0m  | \x1b[1mNode name\x1b[0m\n"));
  174    ConsolePrintf(ctx, _T("------------------+---------+----------------------+------+-------+-----------------------------\n"));
  175     for(int i = 0; i < m_fdbSize; i++)
  176    {
  177       shared_ptr<NetObj> node = FindObjectById(m_fdb[i].nodeObject, OBJECT_NODE);
  178       shared_ptr<Interface> iface = owner->findInterfaceByIndex(m_fdb[i].ifIndex);
  179       ConsolePrintf(ctx, _T("%s | %7d | %-20s | %4d | %-7s | %5d | %s\n"), MACToStr(m_fdb[i].macAddr, macAddrStr),
  180          m_fdb[i].ifIndex, (iface != nullptr) ? iface->getName() : _T("\x1b[31;1mUNKNOWN\x1b[0m"),
  181          m_fdb[i].port, (m_fdb[i].type == 3) ? _T("dynamic") : ((m_fdb[i].type == 5) ? _T("static") : _T("unknown")),
  182          m_fdb[i].nodeObject, (node != nullptr) ? node->getName() : _T("\x1b[31;1mUNKNOWN\x1b[0m"));
  183    }
  184    ConsolePrintf(ctx, _T("\n%d entries\n\n"), m_fdbSize);
  185 }
  186 
  187 String ForwardingDatabase::interfaceIndexToName(const shared_ptr<NetObj>& node, uint32_t index)
  188 {
  189    StringBuffer name;
  190    shared_ptr<Interface> iface = (node != nullptr) ? static_cast<Node*>(node.get())->findInterfaceByIndex(index) : shared_ptr<Interface>();
  191    if (iface != nullptr)
  192    {
  193      if (iface->getParentInterfaceId() != 0)
  194      {
  195         shared_ptr<Interface> parentIface = static_pointer_cast<Interface>(FindObjectById(iface->getParentInterfaceId(), OBJECT_INTERFACE));
  196         if ((parentIface != nullptr) &&
  197             ((parentIface->getIfType() == IFTYPE_ETHERNET_CSMACD) || (parentIface->getIfType() == IFTYPE_IEEE8023ADLAG)))
  198         {
  199            name = parentIface->getName();
  200         }
  201         else
  202         {
  203            name = iface->getName();
  204         }
  205      }
  206      else
  207      {
  208         name = iface->getName();
  209      }
  210    }
  211    else
  212    {
  213       name.appendFormattedString(_T("[%d]"), index);
  214    }
  215    return name;
  216 }
  217 
  218 /**
  219  * Fill NXCP message with FDB data
  220  */
  221 void ForwardingDatabase::fillMessage(NXCPMessage *msg)
  222 {
  223    shared_ptr<NetObj> node = FindObjectById(m_nodeId, OBJECT_NODE);
  224    msg->setField(VID_NUM_ELEMENTS, (UINT32)m_fdbSize);
  225    UINT32 fieldId = VID_ELEMENT_LIST_BASE;
  226     for(int i = 0; i < m_fdbSize; i++)
  227    {
  228       msg->setField(fieldId++, m_fdb[i].macAddr, MAC_ADDR_LENGTH);
  229       msg->setField(fieldId++, m_fdb[i].ifIndex);
  230       msg->setField(fieldId++, m_fdb[i].port);
  231       msg->setField(fieldId++, m_fdb[i].nodeObject);
  232       msg->setField(fieldId++, m_fdb[i].vlanId);
  233       msg->setField(fieldId++, m_fdb[i].type);
  234       msg->setField(fieldId++, interfaceIndexToName(node, m_fdb[i].ifIndex));
  235       fieldId += 3;
  236    }
  237 }
  238 
  239 /**
  240  * Get Table filled with switch forwarding database information
  241  */
  242 Table *ForwardingDatabase::getAsTable()
  243 {
  244    Table *result = new Table();
  245    result->addColumn(_T("MAC_ADDRESS"), DCI_DT_STRING, _T("Mac address"), true);
  246    result->addColumn(_T("PORT"), DCI_DT_UINT, _T("Port"));
  247    result->addColumn(_T("IF_INDEX"), DCI_DT_UINT, _T("Interface index"));
  248    result->addColumn(_T("IF_NAME"), DCI_DT_STRING, _T("Interface name"));
  249    result->addColumn(_T("VLAN"), DCI_DT_UINT, _T("VLAN"));
  250    result->addColumn(_T("NODE_ID"), DCI_DT_UINT, _T("Node id"));
  251    result->addColumn(_T("NODE_NAME"), DCI_DT_STRING, _T("Node name"));
  252    result->addColumn(_T("ZONE_UIN"), DCI_DT_INT, _T("Zone UIN"));
  253    result->addColumn(_T("ZONE_NAME"), DCI_DT_STRING, _T("Zone name"));
  254    result->addColumn(_T("TYPE"), DCI_DT_STRING, _T("Type"));
  255 
  256    shared_ptr<NetObj> sourceNode = FindObjectById(m_nodeId, OBJECT_NODE);
  257    for(int i = 0; i < m_fdbSize; i++)
  258    {
  259       result->addRow();
  260 
  261       TCHAR mac[64];
  262       result->set(0, MACToStr(m_fdb[i].macAddr, mac));
  263       result->set(1, m_fdb[i].port);
  264       result->set(2, m_fdb[i].ifIndex);
  265       result->set(3, interfaceIndexToName(sourceNode, m_fdb[i].ifIndex));
  266       result->set(4, m_fdb[i].vlanId);
  267       result->set(5, m_fdb[i].nodeObject);
  268       shared_ptr<Node> node = static_pointer_cast<Node>(FindObjectById(m_fdb[i].nodeObject, OBJECT_NODE));
  269       if (node != nullptr)
  270       {
  271          result->set(6, node->getName());
  272          result->set(7, node->getZoneUIN());
  273          shared_ptr<Zone> zone = FindZoneByUIN(node->getZoneUIN());
  274          if (zone != nullptr)
  275          {
  276             result->set(8, zone->getName());
  277          }
  278          else
  279          {
  280             result->set(8, _T(""));
  281          }
  282       }
  283       else
  284       {
  285          result->set(6, _T(""));
  286          result->set(7, _T(""));
  287          result->set(8, _T(""));
  288       }
  289       result->set(9, (m_fdb[i].type) == 3 ? _T("Dynamic") : ((m_fdb[i].type == 5) ? _T("Static") : _T("Unknown")));
  290    }
  291 
  292    return result;
  293 }
  294 
  295 /**
  296  * Sort FDB
  297  */
  298 void ForwardingDatabase::sort()
  299 {
  300     qsort(m_fdb, m_fdbSize, sizeof(FDB_ENTRY), EntryComparator);
  301 }
  302 
  303 /**
  304  * FDB walker's callback
  305  */
  306 static UINT32 FDBHandler(SNMP_Variable *pVar, SNMP_Transport *pTransport, void *arg)
  307 {
  308    SNMP_ObjectId oid(pVar->getName());
  309 
  310     // Get port number and status
  311    SNMP_PDU *pRqPDU = new SNMP_PDU(SNMP_GET_REQUEST, SnmpNewRequestId(), pTransport->getSnmpVersion());
  312 
  313     oid.changeElement(10, 2);   // .1.3.6.1.2.1.17.4.3.1.2 - port number
  314     pRqPDU->bindVariable(new SNMP_Variable(oid));
  315 
  316     oid.changeElement(10, 3);   // .1.3.6.1.2.1.17.4.3.1.3 - status
  317     pRqPDU->bindVariable(new SNMP_Variable(oid));
  318 
  319    SNMP_PDU *pRespPDU;
  320    UINT32 rcc = pTransport->doRequest(pRqPDU, &pRespPDU, SnmpGetDefaultTimeout(), 3);
  321     delete pRqPDU;
  322 
  323     if (rcc == SNMP_ERR_SUCCESS)
  324    {
  325       SNMP_Variable *varPort = pRespPDU->getVariable(0);
  326       SNMP_Variable *varStatus = pRespPDU->getVariable(1);
  327       if (varPort != nullptr && varStatus != nullptr)
  328       {
  329          int port = varPort->getValueAsInt();
  330          int status = varStatus->getValueAsInt();
  331          if ((port > 0) && ((status == 3) || (status == 5)))  // status: 3 == learned, 5 == static
  332          {
  333             FDB_ENTRY entry;
  334 
  335             memset(&entry, 0, sizeof(FDB_ENTRY));
  336             entry.port = (UINT32)port;
  337             pVar->getRawValue(entry.macAddr, MAC_ADDR_LENGTH);
  338             shared_ptr<Node> node = FindNodeByMAC(entry.macAddr);
  339             entry.nodeObject = (node != nullptr) ? node->getId() : 0;
  340             entry.vlanId = ((ForwardingDatabase *)arg)->getCurrentVlanId();
  341             entry.type = (UINT16)status;
  342             ((ForwardingDatabase *)arg)->addEntry(&entry);
  343          }
  344       }
  345       delete pRespPDU;
  346     }
  347 
  348     return rcc;
  349 }
  350 
  351 /**
  352  * dot1qTpFdbEntry walker's callback
  353  */
  354 static UINT32 Dot1qTpFdbHandler(SNMP_Variable *pVar, SNMP_Transport *pTransport, void *arg)
  355 {
  356     int port = pVar->getValueAsInt();
  357     if (port == 0)
  358         return SNMP_ERR_SUCCESS;
  359 
  360     // Get port number and status
  361    SNMP_PDU *pRqPDU = new SNMP_PDU(SNMP_GET_REQUEST, SnmpNewRequestId(), pTransport->getSnmpVersion());
  362 
  363    SNMP_ObjectId oid(pVar->getName());
  364    oid.changeElement(12, 3);    // .1.3.6.1.2.1.17.7.1.2.2.1.3 - status
  365     pRqPDU->bindVariable(new SNMP_Variable(oid));
  366 
  367    SNMP_PDU *pRespPDU;
  368    UINT32 rcc = pTransport->doRequest(pRqPDU, &pRespPDU, SnmpGetDefaultTimeout(), 3);
  369     delete pRqPDU;
  370 
  371     if (rcc == SNMP_ERR_SUCCESS)
  372    {
  373         int status = pRespPDU->getVariable(0)->getValueAsInt();
  374         if ((status == 3) || (status == 5)) // status: 3 == learned, 5 == static
  375         {
  376             FDB_ENTRY entry;
  377 
  378             memset(&entry, 0, sizeof(FDB_ENTRY));
  379             entry.port = (UINT32)port;
  380             size_t oidLen = oid.length();
  381             for(size_t i = oidLen - MAC_ADDR_LENGTH, j = 0; i < oidLen; i++)
  382                 entry.macAddr[j++] = oid.getElement(i);
  383             shared_ptr<Node> node = FindNodeByMAC(entry.macAddr);
  384             entry.nodeObject = (node != nullptr) ? node->getId() : 0;
  385          entry.vlanId = (UINT16)oid.getElement(oidLen - MAC_ADDR_LENGTH - 1);
  386          entry.type = (UINT16)status;
  387             ((ForwardingDatabase *)arg)->addEntry(&entry);
  388         }
  389       delete pRespPDU;
  390     }
  391 
  392     return rcc;
  393 }
  394 
  395 /**
  396  * dot1dBasePortTable walker's callback
  397  */
  398 static UINT32 Dot1dPortTableHandler(SNMP_Variable *pVar, SNMP_Transport *pTransport, void *arg)
  399 {
  400    const SNMP_ObjectId& oid = pVar->getName();
  401     PORT_MAPPING_ENTRY pm;
  402     pm.port = oid.value()[oid.length() - 1];
  403     pm.ifIndex = pVar->getValueAsUInt();
  404     ((ForwardingDatabase *)arg)->addPortMapping(&pm);
  405     return SNMP_ERR_SUCCESS;
  406 }
  407 
  408 #define FDB_CHECK_FAILURE(s) if ((s) != SNMP_ERR_SUCCESS) { delete fdb; return nullptr; }
  409 
  410 /**
  411  * Get switch forwarding database from node
  412  */
  413 ForwardingDatabase *GetSwitchForwardingDatabase(Node *node)
  414 {
  415     if (!node->isBridge())
  416         return nullptr;
  417 
  418     ForwardingDatabase *fdb = new ForwardingDatabase(node->getId());
  419 
  420     FDB_CHECK_FAILURE(node->callSnmpEnumerate(_T(".1.3.6.1.2.1.17.1.4.1.2"), Dot1dPortTableHandler, fdb, nullptr, true));
  421    if (node->isPerVlanFdbSupported())
  422    {
  423       shared_ptr<VlanList> vlans = node->getVlans();
  424       if (vlans != nullptr)
  425       {
  426          for(int i = 0; i < vlans->size(); i++)
  427          {
  428             TCHAR context[16];
  429             _sntprintf(context, 16, _T("%s%d"), (node->getSNMPVersion() < SNMP_VERSION_3) ? _T("") : _T("vlan-"), vlans->get(i)->getVlanId());
  430             if (node->callSnmpEnumerate(_T(".1.3.6.1.2.1.17.1.4.1.2"), Dot1dPortTableHandler, fdb, context, true) != SNMP_ERR_SUCCESS)
  431             {
  432                // Some Cisco switches may not return data for certain system VLANs
  433                nxlog_debug_tag(DEBUG_TAG_TOPO_FDB, 5, _T("FDB: cannot read port table in context %s"), context);
  434             }
  435          }
  436       }
  437    }
  438 
  439    FDB_CHECK_FAILURE(node->callSnmpEnumerate(_T(".1.3.6.1.2.1.17.7.1.2.2.1.2"), Dot1qTpFdbHandler, fdb, nullptr, true));
  440    int size = fdb->getSize();
  441    nxlog_debug_tag(DEBUG_TAG_TOPO_FDB, 5, _T("FDB: %d entries read from dot1qTpFdbTable"), size);
  442 
  443    fdb->setCurrentVlanId(1);
  444    FDB_CHECK_FAILURE(node->callSnmpEnumerate(_T(".1.3.6.1.2.1.17.4.3.1.1"), FDBHandler, fdb, nullptr, true));
  445    nxlog_debug_tag(DEBUG_TAG_TOPO_FDB, 5, _T("FDB: %d entries read from dot1dTpFdbTable"), fdb->getSize() - size);
  446    size = fdb->getSize();
  447 
  448     if (node->isPerVlanFdbSupported())
  449     {
  450         shared_ptr<VlanList> vlans = node->getVlans();
  451         if (vlans != nullptr)
  452         {
  453             for(int i = 0; i < vlans->size(); i++)
  454             {
  455                 TCHAR context[16];
  456                 _sntprintf(context, 16, _T("%s%d"), (node->getSNMPVersion() < SNMP_VERSION_3) ? _T("") : _T("vlan-"), vlans->get(i)->getVlanId());
  457             fdb->setCurrentVlanId((UINT16)vlans->get(i)->getVlanId());
  458                 if (node->callSnmpEnumerate(_T(".1.3.6.1.2.1.17.4.3.1.1"), FDBHandler, fdb, context) != SNMP_ERR_SUCCESS)
  459                 {
  460                // Some Cisco switches may not return data for certain system VLANs
  461                    nxlog_debug_tag(DEBUG_TAG_TOPO_FDB, 5, _T("FDB: cannot read  FDB in context %s"), context);
  462             }
  463             else
  464             {
  465                nxlog_debug_tag(DEBUG_TAG_TOPO_FDB, 5, _T("FDB: %d entries read from dot1dTpFdbTable in context %s"), fdb->getSize() - size, context);
  466             }
  467                 size = fdb->getSize();
  468             }
  469         }
  470     }
  471 
  472     fdb->sort();
  473     return fdb;
  474 }