API for reading FDB
[public/netxms.git] / src / server / core / fdb.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2013 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()
29 {
30 m_fdb = NULL;
31 m_fdbSize = 0;
32 m_fdbAllocated = 0;
33 m_portMap = NULL;
34 m_pmSize = 0;
35 m_pmAllocated = 0;
36 m_timestamp = time(NULL);
37 }
38
39 /**
40 * Destructor
41 */
42 ForwardingDatabase::~ForwardingDatabase()
43 {
44 safe_free(m_fdb);
45 safe_free(m_portMap);
46 }
47
48 /**
49 * Add port mapping entry
50 */
51 void ForwardingDatabase::addPortMapping(PORT_MAPPING_ENTRY *entry)
52 {
53 if (m_pmSize == m_pmAllocated)
54 {
55 m_pmAllocated += 32;
56 m_portMap = (PORT_MAPPING_ENTRY *)realloc(m_portMap, sizeof(PORT_MAPPING_ENTRY) * m_pmAllocated);
57 }
58 memcpy(&m_portMap[m_pmSize], entry, sizeof(PORT_MAPPING_ENTRY));
59 m_pmSize++;
60 }
61
62 /**
63 * Get interface index for given port number
64 */
65 UINT32 ForwardingDatabase::ifIndexFromPort(UINT32 port)
66 {
67 for(int i = 0; i < m_pmSize; i++)
68 if (m_portMap[i].port == port)
69 return m_portMap[i].ifIndex;
70 return 0;
71 }
72
73 /**
74 * Add entry
75 */
76 void ForwardingDatabase::addEntry(FDB_ENTRY *entry)
77 {
78 // Check for duplicate
79 for(int i = 0; i < m_fdbSize; i++)
80 if (!memcmp(m_fdb[i].macAddr, entry->macAddr, MAC_ADDR_LENGTH))
81 {
82 memcpy(&m_fdb[i], entry, sizeof(FDB_ENTRY));
83 m_fdb[i].ifIndex = ifIndexFromPort(entry->port);
84 return;
85 }
86
87 if (m_fdbSize == m_fdbAllocated)
88 {
89 m_fdbAllocated += 32;
90 m_fdb = (FDB_ENTRY *)realloc(m_fdb, sizeof(FDB_ENTRY) * m_fdbAllocated);
91 }
92 memcpy(&m_fdb[m_fdbSize], entry, sizeof(FDB_ENTRY));
93 m_fdb[m_fdbSize].ifIndex = ifIndexFromPort(entry->port);
94 m_fdbSize++;
95 }
96
97 /**
98 * FDB entry comparator
99 */
100 static int EntryComparator(const void *p1, const void *p2)
101 {
102 return memcmp(((FDB_ENTRY *)p1)->macAddr, ((FDB_ENTRY *)p2)->macAddr, MAC_ADDR_LENGTH);
103 }
104
105 /**
106 * Find MAC address
107 * Returns interface index or 0 if MAC address not found
108 */
109 UINT32 ForwardingDatabase::findMacAddress(const BYTE *macAddr)
110 {
111 FDB_ENTRY key;
112 memcpy(key.macAddr, macAddr, MAC_ADDR_LENGTH);
113 FDB_ENTRY *entry = (FDB_ENTRY *)bsearch(&key, m_fdb, m_fdbSize, sizeof(FDB_ENTRY), EntryComparator);
114 return (entry != NULL) ? entry->ifIndex : 0;
115 }
116
117 /**
118 * Check if port has only one MAC in FDB
119 * If macAddr parameter is not NULL, MAC address found on port
120 * copied into provided buffer
121 */
122 bool ForwardingDatabase::isSingleMacOnPort(UINT32 ifIndex, BYTE *macAddr)
123 {
124 int count = 0;
125 for(int i = 0; i < m_fdbSize; i++)
126 if (m_fdb[i].ifIndex == ifIndex)
127 {
128 count++;
129 if (count > 1)
130 return false;
131 if (macAddr != NULL)
132 memcpy(macAddr, m_fdb[i].macAddr, MAC_ADDR_LENGTH);
133 }
134
135 return count == 1;
136 }
137
138 /**
139 * Get number of MAC addresses on given port
140 */
141 int ForwardingDatabase::getMacCountOnPort(UINT32 ifIndex)
142 {
143 int count = 0;
144 for(int i = 0; i < m_fdbSize; i++)
145 if (m_fdb[i].ifIndex == ifIndex)
146 {
147 count++;
148 }
149
150 return count;
151 }
152
153 /**
154 * Print to console
155 */
156 void ForwardingDatabase::print(CONSOLE_CTX ctx, Node *owner)
157 {
158 TCHAR macAddrStr[24];
159
160 ConsolePrintf(ctx, _T("\x1b[1mMAC address\x1b[0m | \x1b[1mIfIndex\x1b[0m | \x1b[1mInterface\x1b[0m | \x1b[1mPort\x1b[0m | \x1b[1mNode\x1b[0m | \x1b[1mNode name\x1b[0m\n"));
161 ConsolePrintf(ctx, _T("------------------+---------+----------------------+------+-------+-----------------------------\n"));
162 for(int i = 0; i < m_fdbSize; i++)
163 {
164 Node *node = (Node *)FindObjectById(m_fdb[i].nodeObject, OBJECT_NODE);
165 Interface *iface = owner->findInterface(m_fdb[i].ifIndex, INADDR_ANY);
166 ConsolePrintf(ctx, _T("%s | %7d | %-20s | %4d | %5d | %s\n"), MACToStr(m_fdb[i].macAddr, macAddrStr),
167 m_fdb[i].ifIndex, (iface != NULL) ? iface->Name() : _T("\x1b[31;1mUNKNOWN\x1b[0m"),
168 m_fdb[i].port, m_fdb[i].nodeObject, (node != NULL) ? node->Name() : _T("\x1b[31;1mUNKNOWN\x1b[0m"));
169 }
170 ConsolePrintf(ctx, _T("\n%d entries\n\n"), m_fdbSize);
171 }
172
173 /**
174 * Fill NXCP message with FDB data
175 */
176 void ForwardingDatabase::fillMessage(CSCPMessage *msg)
177 {
178 msg->SetVariable(VID_NUM_ELEMENTS, (UINT32)m_fdbSize);
179 UINT32 fieldId = VID_ELEMENT_LIST_BASE;
180 for(int i = 0; i < m_fdbSize; i++)
181 {
182 msg->SetVariable(fieldId++, m_fdb[i].macAddr, MAC_ADDR_LENGTH);
183 msg->SetVariable(fieldId++, m_fdb[i].ifIndex);
184 msg->SetVariable(fieldId++, m_fdb[i].port);
185 msg->SetVariable(fieldId++, m_fdb[i].nodeObject);
186 msg->SetVariable(fieldId++, m_fdb[i].vlanId);
187 msg->SetVariable(fieldId++, m_fdb[i].type);
188 fieldId += 4;
189 }
190 }
191
192 /**
193 * Sort FDB
194 */
195 void ForwardingDatabase::sort()
196 {
197 qsort(m_fdb, m_fdbSize, sizeof(FDB_ENTRY), EntryComparator);
198 }
199
200 /**
201 * FDB walker's callback
202 */
203 static UINT32 FDBHandler(UINT32 dwVersion, SNMP_Variable *pVar, SNMP_Transport *pTransport, void *arg)
204 {
205 SNMP_ObjectId *pOid = pVar->getName();
206 size_t oidLen = pOid->getLength();
207 UINT32 oid[MAX_OID_LEN];
208 memcpy(oid, pOid->getValue(), oidLen * sizeof(UINT32));
209
210 // Get port number and status
211 SNMP_PDU *pRqPDU = new SNMP_PDU(SNMP_GET_REQUEST, SnmpNewRequestId(), dwVersion);
212
213 oid[10] = 2; // .1.3.6.1.2.1.17.4.3.1.2 - port number
214 pRqPDU->bindVariable(new SNMP_Variable(oid, oidLen));
215
216 oid[10] = 3; // .1.3.6.1.2.1.17.4.3.1.3 - status
217 pRqPDU->bindVariable(new SNMP_Variable(oid, oidLen));
218
219 SNMP_PDU *pRespPDU;
220 UINT32 rcc = pTransport->doRequest(pRqPDU, &pRespPDU, g_dwSNMPTimeout, 3);
221 delete pRqPDU;
222
223 if (rcc == SNMP_ERR_SUCCESS)
224 {
225 SNMP_Variable *varPort = pRespPDU->getVariable(0);
226 SNMP_Variable *varStatus = pRespPDU->getVariable(1);
227 if (varPort != NULL && varStatus != NULL)
228 {
229 int port = varPort->getValueAsInt();
230 int status = varStatus->getValueAsInt();
231 if ((port > 0) && (status == 3)) // status: 3 == learned
232 {
233 FDB_ENTRY entry;
234
235 memset(&entry, 0, sizeof(FDB_ENTRY));
236 entry.port = (UINT32)port;
237 pVar->getRawValue(entry.macAddr, MAC_ADDR_LENGTH);
238 Node *node = FindNodeByMAC(entry.macAddr);
239 entry.nodeObject = (node != NULL) ? node->Id() : 0;
240 entry.vlanId = ((ForwardingDatabase *)arg)->getCurrentVlanId();
241 entry.type = (UINT16)status;
242 ((ForwardingDatabase *)arg)->addEntry(&entry);
243 }
244 }
245 delete pRespPDU;
246 }
247
248 return rcc;
249 }
250
251 /**
252 * dot1qTpFdbEntry walker's callback
253 */
254 static UINT32 Dot1qTpFdbHandler(UINT32 dwVersion, SNMP_Variable *pVar, SNMP_Transport *pTransport, void *arg)
255 {
256 int port = pVar->getValueAsInt();
257 if (port == 0)
258 return SNMP_ERR_SUCCESS;
259
260 SNMP_ObjectId *pOid = pVar->getName();
261 size_t oidLen = pOid->getLength();
262 UINT32 oid[MAX_OID_LEN];
263 memcpy(oid, pOid->getValue(), oidLen * sizeof(UINT32));
264
265 // Get port number and status
266 SNMP_PDU *pRqPDU = new SNMP_PDU(SNMP_GET_REQUEST, SnmpNewRequestId(), dwVersion);
267
268 oid[12] = 3; // .1.3.6.1.2.1.17.7.1.2.2.1.3 - status
269 pRqPDU->bindVariable(new SNMP_Variable(oid, oidLen));
270
271 SNMP_PDU *pRespPDU;
272 UINT32 rcc = pTransport->doRequest(pRqPDU, &pRespPDU, g_dwSNMPTimeout, 3);
273 delete pRqPDU;
274
275 if (rcc == SNMP_ERR_SUCCESS)
276 {
277 int status = pRespPDU->getVariable(0)->getValueAsInt();
278 if (status == 3) // status: 3 == learned
279 {
280 FDB_ENTRY entry;
281
282 memset(&entry, 0, sizeof(FDB_ENTRY));
283 entry.port = (UINT32)port;
284 for(size_t i = oidLen - MAC_ADDR_LENGTH, j = 0; i < oidLen; i++)
285 entry.macAddr[j++] = (BYTE)oid[i];
286 Node *node = FindNodeByMAC(entry.macAddr);
287 entry.nodeObject = (node != NULL) ? node->Id() : 0;
288 entry.vlanId = (UINT16)oid[oidLen - MAC_ADDR_LENGTH - 1];
289 entry.type = (UINT16)status;
290 ((ForwardingDatabase *)arg)->addEntry(&entry);
291 }
292 delete pRespPDU;
293 }
294
295 return rcc;
296 }
297
298 /**
299 * dot1dBasePortTable walker's callback
300 */
301 static UINT32 Dot1dPortTableHandler(UINT32 dwVersion, SNMP_Variable *pVar, SNMP_Transport *pTransport, void *arg)
302 {
303 SNMP_ObjectId *pOid = pVar->getName();
304 PORT_MAPPING_ENTRY pm;
305 pm.port = pOid->getValue()[pOid->getLength() - 1];
306 pm.ifIndex = pVar->getValueAsUInt();
307 ((ForwardingDatabase *)arg)->addPortMapping(&pm);
308 return SNMP_ERR_SUCCESS;
309 }
310
311 /**
312 * Get switch forwarding database from node
313 */
314 ForwardingDatabase *GetSwitchForwardingDatabase(Node *node)
315 {
316 if (!node->isBridge())
317 return NULL;
318
319 ForwardingDatabase *fdb = new ForwardingDatabase();
320 node->callSnmpEnumerate(_T(".1.3.6.1.2.1.17.1.4.1.2"), Dot1dPortTableHandler, fdb);
321 node->callSnmpEnumerate(_T(".1.3.6.1.2.1.17.7.1.2.2.1.2"), Dot1qTpFdbHandler, fdb);
322 int size = fdb->getSize();
323 DbgPrintf(5, _T("FDB: %d entries read from dot1qTpFdbTable"), size);
324
325 fdb->setCurrentVlanId(1);
326 node->callSnmpEnumerate(_T(".1.3.6.1.2.1.17.4.3.1.1"), FDBHandler, fdb);
327 DbgPrintf(5, _T("FDB: %d entries read from dot1dTpFdbTable"), fdb->getSize() - size);
328 size = fdb->getSize();
329
330 if (node->isPerVlanFdbSupported())
331 {
332 VlanList *vlans = node->getVlans();
333 if (vlans != NULL)
334 {
335 for(int i = 0; i < vlans->getSize(); i++)
336 {
337 TCHAR context[16];
338 _sntprintf(context, 16, _T("%s%d"), (node->getSNMPVersion() < SNMP_VERSION_3) ? _T("") : _T("vlan-"), vlans->get(i)->getVlanId());
339 fdb->setCurrentVlanId((UINT16)vlans->get(i)->getVlanId());
340 node->callSnmpEnumerate(_T(".1.3.6.1.2.1.17.4.3.1.1"), FDBHandler, fdb, context);
341 DbgPrintf(5, _T("FDB: %d entries read from dot1dTpFdbTable in context %s"), fdb->getSize() - size, context);
342 size = fdb->getSize();
343 }
344 vlans->decRefCount();
345 }
346 }
347
348 fdb->sort();
349 return fdb;
350 }