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