574b0bcbb7935bcc9bd66b996822ea6c1fecb14e
[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 * Sort FDB
175 */
176 void ForwardingDatabase::sort()
177 {
178 qsort(m_fdb, m_fdbSize, sizeof(FDB_ENTRY), EntryComparator);
179 }
180
181 /**
182 * FDB walker's callback
183 */
184 static UINT32 FDBHandler(UINT32 dwVersion, SNMP_Variable *pVar, SNMP_Transport *pTransport, void *arg)
185 {
186 SNMP_ObjectId *pOid = pVar->getName();
187 size_t oidLen = pOid->getLength();
188 UINT32 oid[MAX_OID_LEN];
189 memcpy(oid, pOid->getValue(), oidLen * sizeof(UINT32));
190
191 // Get port number and status
192 SNMP_PDU *pRqPDU = new SNMP_PDU(SNMP_GET_REQUEST, SnmpNewRequestId(), dwVersion);
193
194 oid[10] = 2; // .1.3.6.1.2.1.17.4.3.1.2 - port number
195 pRqPDU->bindVariable(new SNMP_Variable(oid, oidLen));
196
197 oid[10] = 3; // .1.3.6.1.2.1.17.4.3.1.3 - status
198 pRqPDU->bindVariable(new SNMP_Variable(oid, oidLen));
199
200 SNMP_PDU *pRespPDU;
201 UINT32 rcc = pTransport->doRequest(pRqPDU, &pRespPDU, g_dwSNMPTimeout, 3);
202 delete pRqPDU;
203
204 if (rcc == SNMP_ERR_SUCCESS)
205 {
206 SNMP_Variable *varPort = pRespPDU->getVariable(0);
207 SNMP_Variable *varStatus = pRespPDU->getVariable(1);
208 if (varPort != NULL && varStatus != NULL)
209 {
210 int port = varPort->getValueAsInt();
211 int status = varStatus->getValueAsInt();
212 if ((port > 0) && (status == 3)) // status 3 == learned
213 {
214 FDB_ENTRY entry;
215
216 memset(&entry, 0, sizeof(FDB_ENTRY));
217 entry.port = (UINT32)port;
218 pVar->getRawValue(entry.macAddr, MAC_ADDR_LENGTH);
219 Node *node = FindNodeByMAC(entry.macAddr);
220 entry.nodeObject = (node != NULL) ? node->Id() : 0;
221 ((ForwardingDatabase *)arg)->addEntry(&entry);
222 }
223 }
224 delete pRespPDU;
225 }
226
227 return rcc;
228 }
229
230 /**
231 * dot1qTpFdbEntry walker's callback
232 */
233 static UINT32 Dot1qTpFdbHandler(UINT32 dwVersion, SNMP_Variable *pVar, SNMP_Transport *pTransport, void *arg)
234 {
235 int port = pVar->getValueAsInt();
236 if (port == 0)
237 return SNMP_ERR_SUCCESS;
238
239 SNMP_ObjectId *pOid = pVar->getName();
240 size_t oidLen = pOid->getLength();
241 UINT32 oid[MAX_OID_LEN];
242 memcpy(oid, pOid->getValue(), oidLen * sizeof(UINT32));
243
244 // Get port number and status
245 SNMP_PDU *pRqPDU = new SNMP_PDU(SNMP_GET_REQUEST, SnmpNewRequestId(), dwVersion);
246
247 oid[12] = 3; // .1.3.6.1.2.1.17.7.1.2.2.1.3 - status
248 pRqPDU->bindVariable(new SNMP_Variable(oid, oidLen));
249
250 SNMP_PDU *pRespPDU;
251 UINT32 rcc = pTransport->doRequest(pRqPDU, &pRespPDU, g_dwSNMPTimeout, 3);
252 delete pRqPDU;
253
254 if (rcc == SNMP_ERR_SUCCESS)
255 {
256 int status = pRespPDU->getVariable(0)->getValueAsInt();
257 if (status == 3) // status 3 == learned
258 {
259 FDB_ENTRY entry;
260
261 memset(&entry, 0, sizeof(FDB_ENTRY));
262 entry.port = (UINT32)port;
263 for(size_t i = oidLen - MAC_ADDR_LENGTH, j = 0; i < oidLen; i++)
264 entry.macAddr[j++] = (BYTE)oid[i];
265 Node *node = FindNodeByMAC(entry.macAddr);
266 entry.nodeObject = (node != NULL) ? node->Id() : 0;
267 ((ForwardingDatabase *)arg)->addEntry(&entry);
268 }
269 delete pRespPDU;
270 }
271
272 return rcc;
273 }
274
275 /**
276 * dot1dBasePortTable walker's callback
277 */
278 static UINT32 Dot1dPortTableHandler(UINT32 dwVersion, SNMP_Variable *pVar, SNMP_Transport *pTransport, void *arg)
279 {
280 SNMP_ObjectId *pOid = pVar->getName();
281 PORT_MAPPING_ENTRY pm;
282 pm.port = pOid->getValue()[pOid->getLength() - 1];
283 pm.ifIndex = pVar->getValueAsUInt();
284 ((ForwardingDatabase *)arg)->addPortMapping(&pm);
285 return SNMP_ERR_SUCCESS;
286 }
287
288 /**
289 * Get switch forwarding database from node
290 */
291 ForwardingDatabase *GetSwitchForwardingDatabase(Node *node)
292 {
293 if (!node->isBridge())
294 return NULL;
295
296 ForwardingDatabase *fdb = new ForwardingDatabase();
297 node->callSnmpEnumerate(_T(".1.3.6.1.2.1.17.1.4.1.2"), Dot1dPortTableHandler, fdb);
298 node->callSnmpEnumerate(_T(".1.3.6.1.2.1.17.7.1.2.2.1.2"), Dot1qTpFdbHandler, fdb);
299 int size = fdb->getSize();
300 DbgPrintf(5, _T("FDB: %d entries read from dot1qTpFdbTable"), size);
301
302 node->callSnmpEnumerate(_T(".1.3.6.1.2.1.17.4.3.1.1"), FDBHandler, fdb);
303 DbgPrintf(5, _T("FDB: %d entries read from dot1dTpFdbTable"), fdb->getSize() - size);
304 size = fdb->getSize();
305
306 if (node->isPerVlanFdbSupported())
307 {
308 VlanList *vlans = node->getVlans();
309 if (vlans != NULL)
310 {
311 for(int i = 0; i < vlans->getSize(); i++)
312 {
313 TCHAR context[16];
314 _sntprintf(context, 16, _T("%s%d"), (node->getSNMPVersion() < SNMP_VERSION_3) ? _T("") : _T("vlan-"), vlans->get(i)->getVlanId());
315 node->callSnmpEnumerate(_T(".1.3.6.1.2.1.17.4.3.1.1"), FDBHandler, fdb, context);
316 DbgPrintf(5, _T("FDB: %d entries read from dot1dTpFdbTable in context %s"), fdb->getSize() - size, context);
317 size = fdb->getSize();
318 }
319 vlans->decRefCount();
320 }
321 }
322
323 fdb->sort();
324 return fdb;
325 }