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