added routing loop detection in Node::checkNetworkPath (no event correlation yet)
authorVictor Kirhenshtein <victor@netxms.org>
Sat, 11 Feb 2017 17:10:22 +0000 (19:10 +0200)
committerVictor Kirhenshtein <victor@netxms.org>
Sat, 11 Feb 2017 17:10:22 +0000 (19:10 +0200)
include/netxmsdb.h
include/nxevent.h
sql/events.in
src/server/core/console.cpp
src/server/core/correlate.cpp
src/server/core/node.cpp
src/server/core/tracert.cpp
src/server/include/nms_objects.h
src/server/include/nms_topo.h
src/server/libnxsrv/agent.cpp
src/server/tools/nxdbmgr/upgrade.cpp

index a5b5121..c08a7ff 100644 (file)
@@ -23,6 +23,6 @@
 #ifndef _netxmsdb_h
 #define _netxmsdb_h
 
-#define DB_FORMAT_VERSION   436
+#define DB_FORMAT_VERSION   437
 
 #endif
index 6e06d40..3fbd5a8 100644 (file)
 #define EVENT_IF_EXPECTED_STATE_UP         83
 #define EVENT_IF_EXPECTED_STATE_DOWN       84
 #define EVENT_IF_EXPECTED_STATE_IGNORE     85
+#define EVENT_ROUTING_LOOP_DETECTED        86
 
 #define EVENT_SNMP_UNMATCHED_TRAP          500
 #define EVENT_SNMP_COLD_START              501
index 78eb75e..79e7589 100644 (file)
@@ -1180,3 +1180,21 @@ INSERT INTO event_cfg (event_code,event_name,guid,severity,flags,message,descrip
                '   1) Interface index' CONCAT CRLF CONCAT
                '   2) Interface name'
        );
+INSERT INTO event_cfg (event_code,event_name,guid,severity,flags,message,description) VALUES
+       (
+               EVENT_ROUTING_LOOP_DETECTED, 'SYS_ROUTING_LOOP_DETECTED', '98276f42-dc85-41a5-b449-6ba83d1a71b7',
+               EVENT_SEVERITY_MAJOR, 1,
+               'Routing loop detected for destination %3 (selected route %6/%7 via %9)',
+               'Generated when server detects routing loop during network path trace.' CONCAT CRLF CONCAT
+               'Source of the event is node which routes packet back to already passed hop.' CONCAT CRLF CONCAT
+               'Parameters:' CONCAT CRLF CONCAT
+               '   1) Protocol (IPv4 or IPv6)' CONCAT CRLF CONCAT
+               '   2) Path trace destination node ID' CONCAT CRLF CONCAT
+               '   3) Path trace destination address' CONCAT CRLF CONCAT
+               '   4) Path trace source node ID' CONCAT CRLF CONCAT
+               '   5) Path trace source node address' CONCAT CRLF CONCAT
+               '   6) Routing prefix (subnet address)' CONCAT CRLF CONCAT
+               '   7) Routing prefix length (subnet mask length)' CONCAT CRLF CONCAT
+               '   8) Next hop node ID' CONCAT CRLF CONCAT
+               '   9) Next hop address'
+       );
index 2ccccd0..2ac6d9a 100644 (file)
@@ -1,6 +1,6 @@
 /*
 ** NetXMS - Network Management System
-** Copyright (C) 2003-2016 Raden Solutions
+** Copyright (C) 2003-2017 Raden Solutions
 **
 ** This program is free software; you can redistribute it and/or modify
 ** it under the terms of the GNU General Public License as published by
@@ -299,6 +299,10 @@ int ProcessConsoleCommand(const TCHAR *pszCmdLine, CONSOLE_CTX pCtx)
          {
             pollType = 1;
          }
+         else if (IsCommand(_T("ROUTING-TABLE"), szBuffer, 1))
+         {
+            pollType = 4;
+         }
          else if (IsCommand(_T("STATUS"), szBuffer, 1))
          {
             pollType = 2;
@@ -335,6 +339,10 @@ int ProcessConsoleCommand(const TCHAR *pszCmdLine, CONSOLE_CTX pCtx)
                         node->lockForTopologyPoll();
                         ThreadPoolExecute(g_pollerThreadPool, node, &Node::topologyPoll, RegisterPoller(POLLER_TYPE_TOPOLOGY, node));
                         break;
+                     case 4:
+                        node->lockForRoutePoll();
+                        ThreadPoolExecute(g_pollerThreadPool, node, &Node::routingTablePoll, RegisterPoller(POLLER_TYPE_ROUTING_TABLE, node));
+                        break;
                   }
                }
                else
index 60ec6c6..5863d1a 100644 (file)
@@ -134,7 +134,9 @@ static void C_SysNodeDown(Node *pNode, Event *pEvent)
       else
       {
          Interface *pInterface = ((Node *)hop->object)->findInterfaceByIndex(hop->ifIndex);
-         if ((pInterface != NULL) && ((pInterface->getStatus() == STATUS_CRITICAL) || (pInterface->getStatus() == STATUS_DISABLED)))
+         if ((pInterface != NULL) &&
+             ((pInterface->getAdminState() == IF_ADMIN_STATE_DOWN) || (pInterface->getAdminState() == IF_ADMIN_STATE_TESTING) ||
+              (pInterface->getOperState() == IF_OPER_STATE_DOWN) || (pInterface->getOperState() == IF_OPER_STATE_TESTING)))
          {
                                DbgPrintf(5, _T("C_SysNodeDown: upstream interface %s [%d] on node %s [%d] for current node %s [%d] is down"),
                                          pInterface->getName(), pInterface->getId(), hop->object->getName(), hop->object->getId(), pNode->getName(), pNode->getId());
@@ -184,6 +186,7 @@ void CorrelateEvent(Event *pEvent)
          }
          break;
       case EVENT_INTERFACE_DOWN:
+      case EVENT_INTERFACE_EXPECTED_DOWN:
          {
             Interface *pInterface = node->findInterfaceByIndex(pEvent->getParameterAsULong(4));
             if (pInterface != NULL)
@@ -220,6 +223,9 @@ void CorrelateEvent(Event *pEvent)
       case EVENT_NETWORK_CONNECTION_LOST:
          m_networkLostEventId = pEvent->getId();
          break;
+      case EVENT_ROUTING_LOOP_DETECTED:
+         node->setRoutingLoopEvent(InetAddress::parse(pEvent->getNamedParameter(_T("destAddress"))), pEvent->getNamedParameterAsULong(_T("destNodeId")), pEvent->getId());
+         break;
       default:
          break;
    }
index 8ae1122..645cd86 100644 (file)
@@ -84,7 +84,8 @@ Node::Node() : DataCollectionTarget()
    m_agentProxy = 0;
    m_snmpProxy = 0;
    m_icmpProxy = 0;
-   memset(m_qwLastEvents, 0, sizeof(QWORD) * MAX_LAST_EVENTS);
+   memset(m_lastEvents, 0, sizeof(QWORD) * MAX_LAST_EVENTS);
+   m_routingLoopEvents = new ObjectArray<RoutingLoopEvent>(0, 16, true);
    m_pRoutingTable = NULL;
    m_failTimeSNMP = 0;
    m_failTimeAgent = 0;
@@ -184,7 +185,8 @@ Node::Node(const InetAddress& addr, UINT32 dwFlags, UINT32 agentProxy, UINT32 sn
    m_agentProxy = agentProxy;
    m_snmpProxy = snmpProxy;
    m_icmpProxy = icmpProxy;
-   memset(m_qwLastEvents, 0, sizeof(QWORD) * MAX_LAST_EVENTS);
+   memset(m_lastEvents, 0, sizeof(QWORD) * MAX_LAST_EVENTS);
+   m_routingLoopEvents = new ObjectArray<RoutingLoopEvent>(0, 16, true);
    m_isHidden = true;
    m_pRoutingTable = NULL;
    m_failTimeSNMP = 0;
@@ -245,7 +247,7 @@ Node::~Node()
    delete m_smclpConnection;
    delete m_paramList;
    delete m_tableList;
-   safe_free(m_sysDescription);
+   free(m_sysDescription);
    DestroyRoutingTable(m_pRoutingTable);
    if (m_linkLayerNeighbors != NULL)
       m_linkLayerNeighbors->decRefCount();
@@ -263,9 +265,10 @@ Node::~Node()
    delete m_lldpLocalPortInfo;
    delete m_softwarePackages;
    delete m_winPerfObjects;
-   safe_free(m_sysName);
-   safe_free(m_sysContact);
-   safe_free(m_sysLocation);
+   free(m_sysName);
+   free(m_sysContact);
+   free(m_sysLocation);
+   delete m_routingLoopEvents;
 }
 
 /**
@@ -1592,12 +1595,12 @@ restart_agent_check:
                unlockChildList();
 
                // Clear delayed event queue
-               while(1)
+               while(true)
                {
-                  Event *pEvent = (Event *)pQueue->get();
-                  if (pEvent == NULL)
+                  Event *e = (Event *)pQueue->get();
+                  if (e == NULL)
                      break;
-                  delete pEvent;
+                  delete e;
                }
                delete_and_null(pQueue);
 
@@ -1893,12 +1896,43 @@ restart:
       if ((hop->object == NULL) || (hop->object == this) || (hop->object->getObjectClass() != OBJECT_NODE))
          continue;
 
-      DbgPrintf(6, _T("Node::checkNetworkPath(%s [%d]): checking upstream node %s [%d]"),
-                m_name, m_id, hop->object->getName(), hop->object->getId());
+      // Check for loops
+      if (i > 0)
+      {
+         for(int j = i - 1; j >= 0; j--)
+         {
+            HOP_INFO *prevHop = trace->getHopInfo(j);
+            if (prevHop->object == hop->object)
+            {
+               prevHop = trace->getHopInfo(i - 1);
+               nxlog_debug(5, _T("Node::checkNetworkPath(%s [%d]): routing loop detected on upstream node %s [%d]"),
+                           m_name, m_id, prevHop->object->getName(), prevHop->object->getId());
+               sendPollerMsg(dwRqId, POLLER_WARNING _T("   Routing loop detected on upstream node %s\r\n"), prevHop->object->getName());
+
+               static const TCHAR *names[] =
+                        { _T("protocol"), _T("destNodeId"), _T("destAddress"),
+                          _T("sourceNodeId"), _T("sourceAddress"), _T("prefix"),
+                          _T("prefixLength"), _T("nextHopNodeId"), _T("nextHopAddress")
+                        };
+               PostEventWithNames(EVENT_ROUTING_LOOP_DETECTED, prevHop->object->getId(), "siAiAAdiA", names,
+                     (trace->getSourceAddress().getFamily() == AF_INET6) ? _T("IPv6") : _T("IPv4"),
+                     m_id, &m_ipAddress, g_dwMgmtNode, &(trace->getSourceAddress()),
+                     &prevHop->route, prevHop->route.getMaskBits(), hop->object->getId(), &prevHop->nextHop);
+
+               pathProblemFound = true;
+               break;
+            }
+         }
+         if (pathProblemFound)
+            break;
+      }
+
+      nxlog_debug(6, _T("Node::checkNetworkPath(%s [%d]): checking upstream node %s [%d]"),
+                  m_name, m_id, hop->object->getName(), hop->object->getId());
       if (secondPass && !((Node *)hop->object)->isDown() && (((Node *)hop->object)->m_lastStatusPoll < now - 1))
       {
-         DbgPrintf(6, _T("Node::checkNetworkPath(%s [%d]): forced status poll on node %s [%d]"),
-                   m_name, m_id, hop->object->getName(), hop->object->getId());
+         nxlog_debug(6, _T("Node::checkNetworkPath(%s [%d]): forced status poll on node %s [%d]"),
+                     m_name, m_id, hop->object->getName(), hop->object->getId());
          PollerInfo *poller = RegisterPoller(POLLER_TYPE_STATUS, (Node *)hop->object);
          poller->startExecution();
          ((Node *)hop->object)->statusPoll(NULL, 0, poller);
@@ -1907,8 +1941,8 @@ restart:
 
       if (((Node *)hop->object)->isDown())
       {
-         DbgPrintf(5, _T("Node::checkNetworkPath(%s [%d]): upstream node %s [%d] is down"),
-                   m_name, m_id, hop->object->getName(), hop->object->getId());
+         nxlog_debug(5, _T("Node::checkNetworkPath(%s [%d]): upstream node %s [%d] is down"),
+                     m_name, m_id, hop->object->getName(), hop->object->getId());
          sendPollerMsg(dwRqId, POLLER_WARNING _T("   Upstream node %s is down\r\n"), hop->object->getName());
          pathProblemFound = true;
          break;
@@ -1916,7 +1950,7 @@ restart:
    }
    if (!secondPass && !pathProblemFound)
    {
-      DbgPrintf(5, _T("Node::checkNetworkPath(%s [%d]): will do second pass"), m_name, m_id);
+      nxlog_debug(5, _T("Node::checkNetworkPath(%s [%d]): will do second pass"), m_name, m_id);
       secondPass = true;
       goto restart;
    }
@@ -4278,8 +4312,9 @@ UINT32 Node::getInternalItem(const TCHAR *param, size_t bufSize, TCHAR *buffer)
             bool isVpn;
             UINT32 ifIndex;
             InetAddress nextHop;
+            InetAddress route;
             TCHAR name[MAX_OBJECT_NAME];
-            if (getNextHop(m_ipAddress, destAddr, &nextHop, &ifIndex, &isVpn, name))
+            if (getNextHop(m_ipAddress, destAddr, &nextHop, &route, &ifIndex, &isVpn, name))
             {
                nextHop.toString(buffer);
             }
@@ -5531,7 +5566,7 @@ bool Node::getOutwardInterface(const InetAddress& destAddr, InetAddress *srcAddr
 /**
  * Get next hop for given destination address
  */
-bool Node::getNextHop(const InetAddress& srcAddr, const InetAddress& destAddr, InetAddress *nextHop, UINT32 *ifIndex, bool *isVpn, TCHAR *name)
+bool Node::getNextHop(const InetAddress& srcAddr, const InetAddress& destAddr, InetAddress *nextHop, InetAddress *route, UINT32 *ifIndex, bool *isVpn, TCHAR *name)
 {
    bool nextHopFound = false;
    *name = 0;
@@ -5548,6 +5583,7 @@ bool Node::getNextHop(const InetAddress& srcAddr, const InetAddress& destAddr, I
              ((VPNConnector *)object)->isLocalAddr(srcAddr))
          {
             *nextHop = ((VPNConnector *)object)->getPeerGatewayAddr();
+            *route = InetAddress::INVALID;
             *ifIndex = object->getId();
             *isVpn = true;
             nx_strncpy(name, object->getName(), MAX_OBJECT_NAME);
@@ -5559,6 +5595,7 @@ bool Node::getNextHop(const InetAddress& srcAddr, const InetAddress& destAddr, I
                ((Interface *)object)->getIpAddressList()->findSameSubnetAddress(destAddr).isValid())
       {
          *nextHop = destAddr;
+         *route = InetAddress::INVALID;
          *ifIndex = ((Interface *)object)->getIfIndex();
          *isVpn = false;
          nx_strncpy(name, object->getName(), MAX_OBJECT_NAME);
@@ -5599,6 +5636,8 @@ bool Node::getNextHop(const InetAddress& srcAddr, const InetAddress& destAddr, I
             {
                *nextHop = m_pRoutingTable->pRoutes[i].dwNextHop;
             }
+            *route = m_pRoutingTable->pRoutes[i].dwDestAddr;
+            route->setMaskBits(BitsInMask(m_pRoutingTable->pRoutes[i].dwDestMask));
             *ifIndex = m_pRoutingTable->pRoutes[i].dwIfIndex;
             *isVpn = false;
             if (iface != NULL)
@@ -7754,3 +7793,20 @@ bool Node::isAgentCompressionAllowed()
       return ConfigReadInt(_T("DefaultAgentProtocolCompressionMode"), NODE_AGENT_COMPRESSION_ENABLED) == NODE_AGENT_COMPRESSION_ENABLED;
    return m_agentCompressionMode == NODE_AGENT_COMPRESSION_ENABLED;
 }
+
+/**
+ * Set routing loop event information
+ */
+void Node::setRoutingLoopEvent(const InetAddress& address, UINT32 nodeId, UINT64 eventId)
+{
+   for(int i = 0; i < m_routingLoopEvents->size(); i++)
+   {
+      RoutingLoopEvent *e = m_routingLoopEvents->get(i);
+      if ((e->getNodeId() == nodeId) || e->getAddress().equals(address))
+      {
+         m_routingLoopEvents->remove(i);
+         break;
+      }
+   }
+   m_routingLoopEvents->add(new RoutingLoopEvent(address, nodeId, eventId));
+}
index d3f25df..c269bd5 100644 (file)
@@ -47,7 +47,7 @@ NetworkPath::~NetworkPath()
 /**
  * Add hop to path
  */
-void NetworkPath::addHop(const InetAddress& nextHop, NetObj *currentObject, UINT32 ifIndex, bool isVpn, const TCHAR *name)
+void NetworkPath::addHop(const InetAddress& nextHop, const InetAddress& route, NetObj *currentObject, UINT32 ifIndex, bool isVpn, const TCHAR *name)
 {
        if (m_hopCount == m_allocated)
        {
@@ -55,6 +55,7 @@ void NetworkPath::addHop(const InetAddress& nextHop, NetObj *currentObject, UINT
                m_path = (HOP_INFO *)realloc(m_path, sizeof(HOP_INFO) * m_allocated);
        }
        m_path[m_hopCount].nextHop = nextHop;
+   m_path[m_hopCount].route = route;
        m_path[m_hopCount].object = currentObject;
        m_path[m_hopCount].ifIndex = ifIndex;
        m_path[m_hopCount].isVpn = isVpn;
@@ -100,12 +101,13 @@ NetworkPath *TraceRoute(Node *pSrc, Node *pDest)
    {
       UINT32 dwIfIndex;
       InetAddress nextHop;
+      InetAddress route;
       bool isVpn;
       TCHAR name[MAX_OBJECT_NAME];
-      if (pCurr->getNextHop(srcAddr, pDest->getIpAddress(), &nextHop, &dwIfIndex, &isVpn, name))
+      if (pCurr->getNextHop(srcAddr, pDest->getIpAddress(), &nextHop, &route, &dwIfIndex, &isVpn, name))
       {
                        pNext = FindNodeByIP(pSrc->getZoneId(), nextHop);
-                       path->addHop(nextHop, pCurr, dwIfIndex, isVpn, name);
+                       path->addHop(nextHop, route, pCurr, dwIfIndex, isVpn, name);
          if ((pNext == pCurr) || !nextHop.isValid())
             pNext = NULL;     // Directly connected subnet or too many hops, stop trace
       }
@@ -116,7 +118,7 @@ NetworkPath *TraceRoute(Node *pSrc, Node *pDest)
    }
        if (pCurr == pDest)
        {
-      path->addHop(InetAddress(), pCurr, 0, false, _T(""));
+      path->addHop(InetAddress::INVALID, InetAddress::INVALID, pCurr, 0, false, _T(""));
                path->setComplete();
        }
 
index a6f0021..4559cc9 100644 (file)
@@ -877,7 +877,7 @@ public:
                                 (!memcmp(m_macAddr, "\x00\x00\x00\x00\x00\x00", 6)); }
 
    UINT64 getLastDownEventId() const { return m_lastDownEventId; }
-   void setLastDownEventId(QWORD id) { m_lastDownEventId = id; }
+   void setLastDownEventId(UINT64 id) { m_lastDownEventId = id; }
 
    void setMacAddr(const BYTE *macAddr, bool updateMacDB);
    void setIpAddress(const InetAddress& addr);
@@ -1306,6 +1306,29 @@ enum NodeAgentCompressionMode
 };
 
 /**
+ * Routing loop event information
+ */
+class RoutingLoopEvent
+{
+private:
+   InetAddress m_address;
+   UINT32 m_nodeId;
+   UINT64 m_eventId;
+
+public:
+   RoutingLoopEvent(const InetAddress& address, UINT32 nodeId, UINT64 eventId)
+   {
+      m_address = address;
+      m_nodeId = nodeId;
+      m_eventId = eventId;
+   }
+
+   const InetAddress& getAddress() const { return m_address; }
+   UINT32 getNodeId() const { return m_nodeId; }
+   UINT64 getEventId() const { return m_eventId; }
+};
+
+/**
  * Node
  */
 class NXCORE_EXPORTABLE Node : public DataCollectionTarget
@@ -1392,7 +1415,8 @@ protected:
    UINT32 m_agentProxy;      // Node used as proxy for agent connection
        UINT32 m_snmpProxy;       // Node used as proxy for SNMP requests
    UINT32 m_icmpProxy;       // Node used as proxy for ICMP ping
-   UINT64 m_qwLastEvents[MAX_LAST_EVENTS];
+   UINT64 m_lastEvents[MAX_LAST_EVENTS];
+   ObjectArray<RoutingLoopEvent> *m_routingLoopEvents;
    ROUTING_TABLE *m_pRoutingTable;
        ForwardingDatabase *m_fdb;
        LinkLayerNeighbors *m_linkLayerNeighbors;
@@ -1592,7 +1616,7 @@ public:
    ROUTING_TABLE *getCachedRoutingTable() { return m_pRoutingTable; }
        LinkLayerNeighbors *getLinkLayerNeighbors();
        VlanList *getVlans();
-   bool getNextHop(const InetAddress& srcAddr, const InetAddress& destAddr, InetAddress *nextHop, UINT32 *ifIndex, bool *isVpn, TCHAR *name);
+   bool getNextHop(const InetAddress& srcAddr, const InetAddress& destAddr, InetAddress *nextHop, InetAddress *route, UINT32 *ifIndex, bool *isVpn, TCHAR *name);
    bool getOutwardInterface(const InetAddress& destAddr, InetAddress *srcAddr, UINT32 *srcIfIndex);
        ComponentTree *getComponents();
    bool getLldpLocalPortInfo(UINT32 idType, BYTE *id, size_t idLen, LLDP_LOCAL_PORT_INFO *port);
@@ -1681,8 +1705,9 @@ public:
    UINT32 checkNetworkService(UINT32 *pdwStatus, const InetAddress& ipAddr, int iServiceType, WORD wPort = 0,
                               WORD wProto = 0, TCHAR *pszRequest = NULL, TCHAR *pszResponse = NULL, UINT32 *responseTime = NULL);
 
-   QWORD getLastEventId(int nIndex) { return ((nIndex >= 0) && (nIndex < MAX_LAST_EVENTS)) ? m_qwLastEvents[nIndex] : 0; }
-   void setLastEventId(int nIndex, QWORD qwId) { if ((nIndex >= 0) && (nIndex < MAX_LAST_EVENTS)) m_qwLastEvents[nIndex] = qwId; }
+   UINT64 getLastEventId(int index) { return ((index >= 0) && (index < MAX_LAST_EVENTS)) ? m_lastEvents[index] : 0; }
+   void setLastEventId(int index, UINT64 eventId) { if ((index >= 0) && (index < MAX_LAST_EVENTS)) m_lastEvents[index] = eventId; }
+   void setRoutingLoopEvent(const InetAddress& address, UINT32 nodeId, UINT64 eventId);
 
    UINT32 callSnmpEnumerate(const TCHAR *pszRootOid,
       UINT32 (* pHandler)(SNMP_Variable *, SNMP_Transport *, void *), void *pArg, const TCHAR *context = NULL);
index 20a8b0b..1e057e6 100644 (file)
@@ -49,6 +49,7 @@ struct HOP_INFO
    NetObj *object;       // Current hop object
    UINT32 ifIndex;       // Interface index or VPN connector object ID
    bool isVpn;           // TRUE if next hop is behind VPN tunnel
+   InetAddress route;    // Route used (UNSPEC for VPN connectors and direct access)
    TCHAR name[MAX_OBJECT_NAME];
 };
 
@@ -68,7 +69,7 @@ public:
        NetworkPath(const InetAddress& srcAddr);
        ~NetworkPath();
 
-       void addHop(const InetAddress& nextHop, NetObj *currentObject, UINT32 ifIndex, bool isVpn, const TCHAR *name);
+       void addHop(const InetAddress& nextHop, const InetAddress& route, NetObj *currentObject, UINT32 ifIndex, bool isVpn, const TCHAR *name);
        void setComplete() { m_complete = true; }
 
    const InetAddress& getSourceAddress() { return m_sourceAddress; }
index 627b033..7fd6f11 100644 (file)
@@ -723,8 +723,13 @@ InterfaceList *AgentConnection::getInterfaceList()
                pSlash = defaultMask;
             }
             InetAddress addr = InetAddress::parse(pBuf);
-            addr.setMaskBits(_tcstol(pSlash, NULL, 10));
-            iface->ipAddrList.add(addr);
+            if (addr.isValid())
+            {
+               addr.setMaskBits(_tcstol(pSlash, NULL, 10));
+               // Agent may return 0.0.0.0/0 for interfaces without IP address
+               if ((addr.getFamily() != AF_INET) || (addr.getAddressV4() != 0))
+                  iface->ipAddrList.add(addr);
+            }
             pBuf = pChar + 1;
          }
 
index c3e4547..7dca491 100644 (file)
@@ -747,6 +747,32 @@ static bool SetSchemaVersion(int version)
 }
 
 /**
+ * Upgrade from V436 to V437
+ */
+static BOOL H_UpgradeFromV436(int currVersion, int newVersion)
+{
+   CHK_EXEC(
+      CreateEventTemplate(EVENT_ROUTING_LOOP_DETECTED, _T("SYS_ROUTING_LOOP_DETECTED"),
+         SEVERITY_MAJOR, EF_LOG, _T("98276f42-dc85-41a5-b449-6ba83d1a71b7"),
+         _T("Routing loop detected for destination %3 (selected route %6/%7 via %9)"),
+         _T("Generated when server detects routing loop during network path trace.\r\n")
+         _T("Source of the event is node which routes packet back to already passed hop.\r\n")
+         _T("Parameters:\r\n")
+         _T("   1) Protocol (IPv4 or IPv6)\r\n")
+         _T("   2) Path trace destination node ID\r\n")
+         _T("   3) Path trace destination address\r\n")
+         _T("   4) Path trace source node ID\r\n")
+         _T("   5) Path trace source node address\r\n")
+         _T("   6) Routing prefix (subnet address)\r\n")
+         _T("   7) Routing prefix length (subnet mask length)\r\n")
+         _T("   8) Next hop node ID\r\n")
+         _T("   9) Next hop address"))
+      );
+   CHK_EXEC(SetSchemaVersion(437));
+   return TRUE;
+}
+
+/**
  * Upgrade from V435 to V436
  */
 static BOOL H_UpgradeFromV435(int currVersion, int newVersion)
@@ -791,8 +817,8 @@ static BOOL H_UpgradeFromV433(int currVersion, int newVersion)
          _T("Generated when interface expected state set to UP.\r\n")
          _T("Please note that source of event is node, not an interface itself.\r\n")
          _T("Parameters:\r\n")
-         _T("    1) Interface index\r\n")
-         _T("    2) Interface name"))
+         _T("   1) Interface index\r\n")
+         _T("   2) Interface name"))
       );
 
    CHK_EXEC(
@@ -802,8 +828,8 @@ static BOOL H_UpgradeFromV433(int currVersion, int newVersion)
          _T("Generated when interface expected state set to DOWN.\r\n")
          _T("Please note that source of event is node, not an interface itself.\r\n")
          _T("Parameters:\r\n")
-         _T("    1) Interface index\r\n")
-         _T("    2) Interface name"))
+         _T("   1) Interface index\r\n")
+         _T("   2) Interface name"))
       );
 
    CHK_EXEC(
@@ -813,8 +839,8 @@ static BOOL H_UpgradeFromV433(int currVersion, int newVersion)
          _T("Generated when interface expected state set to IGNORE.\r\n")
          _T("Please note that source of event is node, not an interface itself.\r\n")
          _T("Parameters:\r\n")
-         _T("    1) Interface index\r\n")
-         _T("    2) Interface name"))
+         _T("   1) Interface index\r\n")
+         _T("   2) Interface name"))
       );
 
    CHK_EXEC(AddEventToEPPRule(_T("6f46d451-ee66-4563-8747-d129877df24d"), EVENT_IF_EXPECTED_STATE_DOWN));
@@ -11399,6 +11425,7 @@ static struct
    { 433, 434, H_UpgradeFromV433 },
    { 434, 435, H_UpgradeFromV434 },
    { 435, 436, H_UpgradeFromV435 },
+   { 436, 437, H_UpgradeFromV436 },
    { 0, 0, NULL }
 };