lldp topology discovery partially working; old BuildL2Topology call rewritten as...
[public/netxms.git] / src / server / core / lldp.cpp
CommitLineData
eec253a8
VK
1/*
2** NetXMS - Network Management System
3** Copyright (C) 2003-2011 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: lldp.cpp
20**
21**/
22
23#include "nxcore.h"
24
25
26//
27// Find remote interface
28//
29
30static Interface *FindRemoteInterface(Node *node, DWORD idType, BYTE *id, size_t idLen)
31{
040c45fa
VK
32 TCHAR ifName[128];
33 Interface *ifc;
34
eec253a8
VK
35 switch(idType)
36 {
37 case 3: // MAC address
38 return node->findInterfaceByMAC(id);
39 case 4: // Network address
40 if (id[0] == 1) // IPv4
41 {
42 DWORD ipAddr;
43 memcpy(&ipAddr, &id[1], sizeof(DWORD));
44 return node->findInterfaceByIP(ntohl(ipAddr));
45 }
46 return NULL;
040c45fa
VK
47 case 5: // Interface name
48#ifdef UNICODE
49 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char *)id, idLen, ifName, 128);
50 ifName[min(idLen, 127)] = 0;
51#else
52 int len = min(idLen, 127);
53 memcpy(ifName, id, len);
54 ifName[len] = 0;
55#endif
56 ifc = node->findInterface(ifName); /* TODO: find by cached ifName value */
57 if (ifc == NULL)
58 {
59 // Hack for Extreme Networks switches
60 // Must be moved into driver
61 memmove(&ifName[2], ifName, (_tcslen(ifName) - 1) * sizeof(TCHAR));
62 ifName[0] = _T('1');
63 ifName[1] = _T(':');
64 Interface *ifc = node->findInterface(ifName);
65 }
66 return ifc;
eec253a8
VK
67 default:
68 return NULL;
69 }
70}
71
72
73//
74// Topology table walker's callback for LLDP topology table
75//
76
77static DWORD LLDPTopoHandler(DWORD snmpVersion, SNMP_Variable *var, SNMP_Transport *transport, void *arg)
78{
79 LinkLayerNeighbors *nbs = (LinkLayerNeighbors *)arg;
80 Node *node = (Node *)nbs->getData();
81 SNMP_ObjectId *oid = var->GetName();
82
83 // Get additional info for current record
84 DWORD newOid[128];
85 memcpy(newOid, oid->GetValue(), oid->Length() * sizeof(DWORD));
86 SNMP_PDU *pRqPDU = new SNMP_PDU(SNMP_GET_REQUEST, SnmpNewRequestId(), snmpVersion);
87
88 newOid[oid->Length() - 4] = 4; // lldpRemChassisIdSubtype
89 pRqPDU->bindVariable(new SNMP_Variable(newOid, oid->Length()));
90
91 newOid[oid->Length() - 4] = 7; // lldpRemPortId
92 pRqPDU->bindVariable(new SNMP_Variable(newOid, oid->Length()));
93
94 newOid[oid->Length() - 4] = 6; // lldpRemPortIdSubtype
95 pRqPDU->bindVariable(new SNMP_Variable(newOid, oid->Length()));
96
97 SNMP_PDU *pRespPDU;
98 DWORD rcc = transport->doRequest(pRqPDU, &pRespPDU, g_dwSNMPTimeout, 3);
99 delete pRqPDU;
100 if (rcc == SNMP_ERR_SUCCESS)
101 {
102 // Build LLDP ID for remote system
103 TCHAR remoteId[256];
104 _sntprintf(remoteId, 256, _T("%d@"), (int)pRespPDU->getVariable(0)->GetValueAsInt());
105 BinToStr(var->GetValue(), var->GetValueLength(), &remoteId[_tcslen(remoteId)]);
106
107 Node *remoteNode = FindNodeByLLDPId(remoteId);
108 if (remoteNode != NULL)
109 {
110 BYTE remoteIfId[1024];
111 size_t remoteIfIdLen = pRespPDU->getVariable(1)->getRawValue(remoteIfId, 1024);
112 Interface *ifRemote = FindRemoteInterface(remoteNode, pRespPDU->getVariable(2)->GetValueAsUInt(), remoteIfId, remoteIfIdLen);
113
114 LL_NEIGHBOR_INFO info;
115
116 info.objectId = remoteNode->Id();
117 info.ifRemote = (ifRemote != NULL) ? ifRemote->getIfIndex() : 0;
118 info.isPtToPt = true;
119 info.protocol = LL_PROTO_LLDP;
120
121 // Index to lldpRemTable is lldpRemTimeMark, lldpRemLocalPortNum, lldpRemIndex
122 DWORD localPort = oid->GetValue()[oid->Length() - 2];
123
124 // Determine interface index from local port number. It can be
125 // either ifIndex or dot1dBasePort, as described in LLDP MIB:
126 // A port number has no mandatory relationship to an
127 // InterfaceIndex object (of the interfaces MIB, IETF RFC 2863).
128 // If the LLDP agent is a IEEE 802.1D, IEEE 802.1Q bridge, the
129 // LldpPortNumber will have the same value as the dot1dBasePort
130 // object (defined in IETF RFC 1493) associated corresponding
131 // bridge port. If the system hosting LLDP agent is not an
132 // IEEE 802.1D or an IEEE 802.1Q bridge, the LldpPortNumber
133 // will have the same value as the corresponding interface's
134 // InterfaceIndex object.
135 if (node->isBridge())
136 {
137 Interface *localIf = node->findBridgePort(localPort);
138 if (localIf != NULL)
139 info.ifLocal = localIf->getIfIndex();
140 }
141 else
142 {
143 info.ifLocal = localPort;
144 }
145
146 nbs->addConnection(&info);
147 }
148 }
149 return SNMP_ERR_SUCCESS;
150}
151
152
153//
154// Add LLDP-discovered neighbors
155//
156
157void AddLLDPNeighbors(Node *node, LinkLayerNeighbors *nbs)
158{
159 if (!(node->getFlags() & NF_IS_LLDP))
160 return;
161
162 nbs->setData(node);
163 node->CallSnmpEnumerate(_T(".1.0.8802.1.1.2.1.4.1.1.5"), LLDPTopoHandler, nbs);
164}