created SDK installer; other minor changes
[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{
ee97f663 32 TCHAR ifName[130];
040c45fa
VK
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
8d7a71be 49 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char *)id, (int)idLen, ifName, 128);
040c45fa
VK
50 ifName[min(idLen, 127)] = 0;
51#else
608d0550
VK
52 {
53 int len = min(idLen, 127);
54 memcpy(ifName, id, len);
55 ifName[len] = 0;
56 }
040c45fa
VK
57#endif
58 ifc = node->findInterface(ifName); /* TODO: find by cached ifName value */
ee97f663 59 if ((ifc == NULL) && !_tcsncmp(node->getObjectId(), _T(".1.3.6.1.4.1.1916.2"), 19))
040c45fa
VK
60 {
61 // Hack for Extreme Networks switches
62 // Must be moved into driver
ee97f663 63 memmove(&ifName[2], ifName, (_tcslen(ifName) + 1) * sizeof(TCHAR));
040c45fa
VK
64 ifName[0] = _T('1');
65 ifName[1] = _T(':');
ee97f663 66 ifc = node->findInterface(ifName);
040c45fa
VK
67 }
68 return ifc;
eec253a8
VK
69 default:
70 return NULL;
71 }
72}
73
74
75//
76// Topology table walker's callback for LLDP topology table
77//
78
79static DWORD LLDPTopoHandler(DWORD snmpVersion, SNMP_Variable *var, SNMP_Transport *transport, void *arg)
80{
81 LinkLayerNeighbors *nbs = (LinkLayerNeighbors *)arg;
82 Node *node = (Node *)nbs->getData();
83 SNMP_ObjectId *oid = var->GetName();
84
85 // Get additional info for current record
86 DWORD newOid[128];
87 memcpy(newOid, oid->GetValue(), oid->Length() * sizeof(DWORD));
88 SNMP_PDU *pRqPDU = new SNMP_PDU(SNMP_GET_REQUEST, SnmpNewRequestId(), snmpVersion);
89
90 newOid[oid->Length() - 4] = 4; // lldpRemChassisIdSubtype
91 pRqPDU->bindVariable(new SNMP_Variable(newOid, oid->Length()));
92
93 newOid[oid->Length() - 4] = 7; // lldpRemPortId
94 pRqPDU->bindVariable(new SNMP_Variable(newOid, oid->Length()));
95
96 newOid[oid->Length() - 4] = 6; // lldpRemPortIdSubtype
97 pRqPDU->bindVariable(new SNMP_Variable(newOid, oid->Length()));
98
99 SNMP_PDU *pRespPDU;
100 DWORD rcc = transport->doRequest(pRqPDU, &pRespPDU, g_dwSNMPTimeout, 3);
101 delete pRqPDU;
102 if (rcc == SNMP_ERR_SUCCESS)
103 {
104 // Build LLDP ID for remote system
105 TCHAR remoteId[256];
106 _sntprintf(remoteId, 256, _T("%d@"), (int)pRespPDU->getVariable(0)->GetValueAsInt());
107 BinToStr(var->GetValue(), var->GetValueLength(), &remoteId[_tcslen(remoteId)]);
108
109 Node *remoteNode = FindNodeByLLDPId(remoteId);
110 if (remoteNode != NULL)
111 {
112 BYTE remoteIfId[1024];
113 size_t remoteIfIdLen = pRespPDU->getVariable(1)->getRawValue(remoteIfId, 1024);
114 Interface *ifRemote = FindRemoteInterface(remoteNode, pRespPDU->getVariable(2)->GetValueAsUInt(), remoteIfId, remoteIfIdLen);
115
116 LL_NEIGHBOR_INFO info;
117
118 info.objectId = remoteNode->Id();
119 info.ifRemote = (ifRemote != NULL) ? ifRemote->getIfIndex() : 0;
120 info.isPtToPt = true;
121 info.protocol = LL_PROTO_LLDP;
122
123 // Index to lldpRemTable is lldpRemTimeMark, lldpRemLocalPortNum, lldpRemIndex
124 DWORD localPort = oid->GetValue()[oid->Length() - 2];
125
126 // Determine interface index from local port number. It can be
127 // either ifIndex or dot1dBasePort, as described in LLDP MIB:
128 // A port number has no mandatory relationship to an
129 // InterfaceIndex object (of the interfaces MIB, IETF RFC 2863).
130 // If the LLDP agent is a IEEE 802.1D, IEEE 802.1Q bridge, the
131 // LldpPortNumber will have the same value as the dot1dBasePort
132 // object (defined in IETF RFC 1493) associated corresponding
133 // bridge port. If the system hosting LLDP agent is not an
134 // IEEE 802.1D or an IEEE 802.1Q bridge, the LldpPortNumber
135 // will have the same value as the corresponding interface's
136 // InterfaceIndex object.
137 if (node->isBridge())
138 {
139 Interface *localIf = node->findBridgePort(localPort);
140 if (localIf != NULL)
141 info.ifLocal = localIf->getIfIndex();
142 }
143 else
144 {
145 info.ifLocal = localPort;
146 }
147
148 nbs->addConnection(&info);
149 }
150 }
151 return SNMP_ERR_SUCCESS;
152}
153
154
155//
156// Add LLDP-discovered neighbors
157//
158
159void AddLLDPNeighbors(Node *node, LinkLayerNeighbors *nbs)
160{
161 if (!(node->getFlags() & NF_IS_LLDP))
162 return;
163
e16f5a14 164 DbgPrintf(5, _T("LLDP: collecting topology information for node %s [%d]"), node->Name(), node->Id());
eec253a8
VK
165 nbs->setData(node);
166 node->CallSnmpEnumerate(_T(".1.0.8802.1.1.2.1.4.1.1.5"), LLDPTopoHandler, nbs);
e16f5a14 167 DbgPrintf(5, _T("LLDP: finished collecting topology information for node %s [%d]"), node->Name(), node->Id());
eec253a8 168}