4e006530ffb798c5a040031807a736e434599a2c
[public/netxms.git] / src / server / core / snmp.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2016 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: snmp.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Handler for ARP enumeration
27 */
28 static UINT32 HandlerArp(SNMP_Variable *pVar, SNMP_Transport *pTransport, void *pArg)
29 {
30 UINT32 oidName[MAX_OID_LEN], dwIndex = 0;
31 BYTE bMac[64];
32 UINT32 dwResult;
33
34 size_t nameLen = pVar->getName().length();
35 memcpy(oidName, pVar->getName().value(), nameLen * sizeof(UINT32));
36
37 oidName[nameLen - 6] = 1; // Retrieve interface index
38 dwResult = SnmpGetEx(pTransport, NULL, oidName, nameLen, &dwIndex, sizeof(UINT32), 0, NULL);
39 if (dwResult != SNMP_ERR_SUCCESS)
40 return dwResult;
41
42 oidName[nameLen - 6] = 2; // Retrieve MAC address for this IP
43 dwResult = SnmpGetEx(pTransport, NULL, oidName, nameLen, bMac, 64, SG_RAW_RESULT, NULL);
44 if (dwResult == SNMP_ERR_SUCCESS)
45 {
46 ((ARP_CACHE *)pArg)->dwNumEntries++;
47 ((ARP_CACHE *)pArg)->pEntries = (ARP_ENTRY *)realloc(((ARP_CACHE *)pArg)->pEntries,
48 sizeof(ARP_ENTRY) * ((ARP_CACHE *)pArg)->dwNumEntries);
49 ((ARP_CACHE *)pArg)->pEntries[((ARP_CACHE *)pArg)->dwNumEntries - 1].ipAddr = ntohl(pVar->getValueAsUInt());
50 memcpy(((ARP_CACHE *)pArg)->pEntries[((ARP_CACHE *)pArg)->dwNumEntries - 1].bMacAddr, bMac, 6);
51 ((ARP_CACHE *)pArg)->pEntries[((ARP_CACHE *)pArg)->dwNumEntries - 1].dwIndex = dwIndex;
52 }
53 return dwResult;
54 }
55
56 /**
57 * Get ARP cache via SNMP
58 */
59 ARP_CACHE *SnmpGetArpCache(UINT32 dwVersion, SNMP_Transport *pTransport)
60 {
61 ARP_CACHE *pArpCache;
62
63 pArpCache = (ARP_CACHE *)malloc(sizeof(ARP_CACHE));
64 if (pArpCache == NULL)
65 return NULL;
66
67 pArpCache->dwNumEntries = 0;
68 pArpCache->pEntries = NULL;
69
70 if (SnmpWalk(pTransport, _T(".1.3.6.1.2.1.4.22.1.3"), HandlerArp, pArpCache) != SNMP_ERR_SUCCESS)
71 {
72 DestroyArpCache(pArpCache);
73 pArpCache = NULL;
74 }
75 return pArpCache;
76 }
77
78 /**
79 * Handler for route enumeration
80 */
81 static UINT32 HandlerRoute(SNMP_Variable *pVar, SNMP_Transport *pTransport, void *pArg)
82 {
83 UINT32 oidName[MAX_OID_LEN], dwResult;
84 ROUTE route;
85 ROUTING_TABLE *rt = (ROUTING_TABLE *)pArg;
86
87 size_t nameLen = pVar->getName().length();
88 if ((nameLen < 5) || (nameLen > MAX_OID_LEN))
89 {
90 DbgPrintf(4, _T("HandlerRoute(): strange nameLen %d (name=%s)"), nameLen, (const TCHAR *)pVar->getName().toString());
91 return SNMP_ERR_SUCCESS;
92 }
93 memcpy(oidName, pVar->getName().value(), nameLen * sizeof(UINT32));
94 route.dwDestAddr = ntohl(pVar->getValueAsUInt());
95
96 oidName[nameLen - 5] = 2; // Interface index
97 if ((dwResult = SnmpGetEx(pTransport, NULL, oidName, nameLen,
98 &route.dwIfIndex, sizeof(UINT32), 0, NULL)) != SNMP_ERR_SUCCESS)
99 return dwResult;
100
101 oidName[nameLen - 5] = 7; // Next hop
102 if ((dwResult = SnmpGetEx(pTransport, NULL, oidName, nameLen,
103 &route.dwNextHop, sizeof(UINT32), 0, NULL)) != SNMP_ERR_SUCCESS)
104 return dwResult;
105
106 oidName[nameLen - 5] = 8; // Route type
107 if ((dwResult = SnmpGetEx(pTransport, NULL, oidName, nameLen,
108 &route.dwRouteType, sizeof(UINT32), 0, NULL)) != SNMP_ERR_SUCCESS)
109 return dwResult;
110
111 oidName[nameLen - 5] = 11; // Destination mask
112 if ((dwResult = SnmpGetEx(pTransport, NULL, oidName, nameLen,
113 &route.dwDestMask, sizeof(UINT32), 0, NULL)) != SNMP_ERR_SUCCESS)
114 return dwResult;
115
116 rt->iNumEntries++;
117 rt->pRoutes = (ROUTE *)realloc(rt->pRoutes, sizeof(ROUTE) * rt->iNumEntries);
118 memcpy(&rt->pRoutes[rt->iNumEntries - 1], &route, sizeof(ROUTE));
119 return SNMP_ERR_SUCCESS;
120 }
121
122 /**
123 * Get routing table via SNMP
124 */
125 ROUTING_TABLE *SnmpGetRoutingTable(UINT32 dwVersion, SNMP_Transport *pTransport)
126 {
127 ROUTING_TABLE *pRT;
128
129 pRT = (ROUTING_TABLE *)malloc(sizeof(ROUTING_TABLE));
130 if (pRT == NULL)
131 return NULL;
132
133 pRT->iNumEntries = 0;
134 pRT->pRoutes = NULL;
135
136 if (SnmpWalk(pTransport, _T(".1.3.6.1.2.1.4.21.1.1"), HandlerRoute, pRT) != SNMP_ERR_SUCCESS)
137 {
138 DestroyRoutingTable(pRT);
139 pRT = NULL;
140 }
141 return pRT;
142 }
143
144 /**
145 * Send request and check if we get any response
146 */
147 static bool SnmpTestRequest(SNMP_Transport *snmp, StringList *testOids)
148 {
149 bool success = false;
150 SNMP_PDU request(SNMP_GET_REQUEST, SnmpNewRequestId(), snmp->getSnmpVersion());
151 for(int i = 0; i < testOids->size(); i++)
152 request.bindVariable(new SNMP_Variable(testOids->get(i)));
153 SNMP_PDU *response;
154 UINT32 rc = snmp->doRequest(&request, &response, SnmpGetDefaultTimeout(), 3);
155 if (rc == SNMP_ERR_SUCCESS)
156 {
157 success = (response->getErrorCode() == SNMP_PDU_ERR_SUCCESS) || (response->getErrorCode() == SNMP_PDU_ERR_NO_SUCH_NAME);
158 delete response;
159 }
160 return success;
161 }
162
163 /**
164 * Check SNMP v3 connectivity
165 */
166 bool SnmpCheckV3CommSettings(SNMP_Transport *pTransport, SNMP_SecurityContext *originalContext, StringList *testOids, const TCHAR *id)
167 {
168 pTransport->setSnmpVersion(SNMP_VERSION_3);
169
170 // Check original SNMP V3 settings, if set
171 if ((originalContext != NULL) && (originalContext->getSecurityModel() == SNMP_SECURITY_MODEL_USM))
172 {
173 DbgPrintf(5, _T("SnmpCheckV3CommSettings(%s): trying %hs/%d:%d"), id, originalContext->getUser(),
174 originalContext->getAuthMethod(), originalContext->getPrivMethod());
175 pTransport->setSecurityContext(new SNMP_SecurityContext(originalContext));
176 if (SnmpTestRequest(pTransport, testOids))
177 {
178 DbgPrintf(5, _T("SnmpCheckV3CommSettings(%s): success"), id);
179 return true;
180 }
181 }
182
183 // Try pre-configured SNMP v3 USM credentials
184 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
185 DB_RESULT hResult = DBSelect(hdb, _T("SELECT user_name,auth_method,priv_method,auth_password,priv_password FROM usm_credentials"));
186 if (hResult != NULL)
187 {
188 int count = DBGetNumRows(hResult);
189 ObjectArray<SNMP_SecurityContext> contexts(count, 16, false);
190 for(int i = 0; i < count; i++)
191 {
192 char name[MAX_DB_STRING], authPasswd[MAX_DB_STRING], privPasswd[MAX_DB_STRING];
193 DBGetFieldA(hResult, i, 0, name, MAX_DB_STRING);
194 DBGetFieldA(hResult, i, 3, authPasswd, MAX_DB_STRING);
195 DBGetFieldA(hResult, i, 4, privPasswd, MAX_DB_STRING);
196 contexts.add(new SNMP_SecurityContext(name, authPasswd, privPasswd, DBGetFieldLong(hResult, i, 1), DBGetFieldLong(hResult, i, 2)));
197 }
198
199 DBFreeResult(hResult);
200 DBConnectionPoolReleaseConnection(hdb);
201
202 bool found = false;
203 for(int i = 0; (i < contexts.size()) && !found && !IsShutdownInProgress(); i++)
204 {
205 SNMP_SecurityContext *ctx = contexts.get(i);
206 pTransport->setSecurityContext(ctx);
207 DbgPrintf(5, _T("SnmpCheckV3CommSettings(%s): trying %hs/%d:%d"), id, ctx->getUser(), ctx->getAuthMethod(), ctx->getPrivMethod());
208 if (SnmpTestRequest(pTransport, testOids))
209 {
210 DbgPrintf(5, _T("SnmpCheckV3CommSettings(%s): success"), id);
211 found = true;
212
213 // Delete unused contexts
214 for(int j = i + 1; j < contexts.size(); j++)
215 delete contexts.get(j);
216
217 break;
218 }
219 }
220
221 if (found)
222 return true;
223 }
224 else
225 {
226 DBConnectionPoolReleaseConnection(hdb);
227 DbgPrintf(3, _T("SnmpCheckV3CommSettings(%s): DBSelect() failed"), id);
228 }
229
230 DbgPrintf(5, _T("SnmpCheckV3CommSettings(%s): failed"), id);
231 return false;
232 }
233
234 /**
235 * Determine SNMP parameters for node
236 * On success, returns new security context object (dynamically created).
237 * On failure, returns NULL
238 */
239 SNMP_Transport *SnmpCheckCommSettings(UINT32 snmpProxy, const InetAddress& ipAddr, INT16 *version, UINT16 originalPort, SNMP_SecurityContext *originalContext, StringList *testOids)
240 {
241 DbgPrintf(5, _T("SnmpCheckCommSettings(%s): starting check (proxy=%d, originalPort=%d)"), (const TCHAR *)ipAddr.toString(), snmpProxy, (int)originalPort);
242
243 SNMP_Transport *pTransport = NULL;
244 StringList *communities = NULL;
245
246 TCHAR tmp[MAX_CONFIG_VALUE];
247 ConfigReadStr(_T("SNMPPorts"), tmp, MAX_CONFIG_VALUE, _T("161"));
248 StringList *ports = new StringList(tmp, _T(","));
249 for(int j = -1; (j < ports->size()) && !IsShutdownInProgress(); j++)
250 {
251 UINT16 port;
252 if (j == -1)
253 {
254 if (originalPort == 0)
255 continue;
256 port = originalPort;
257 }
258 else
259 {
260 port = (UINT16)_tcstoul(ports->get(j), NULL, 10);
261 if ((port == originalPort) || (port == 0))
262 continue;
263 }
264
265 DbgPrintf(5, _T("SnmpCheckCommSettings(%s): checking port %d"), (const TCHAR *)ipAddr.toString(), (int)port);
266
267 AgentConnection *pConn = NULL;
268 if (snmpProxy != 0)
269 {
270 Node *proxyNode = (Node *)FindObjectById(snmpProxy, OBJECT_NODE);
271 if (proxyNode == NULL)
272 {
273 DbgPrintf(5, _T("SnmpCheckCommSettings(%s): invalid proxy node ID %d"), (const TCHAR *)ipAddr.toString(), snmpProxy);
274 goto fail;
275 }
276 pConn = proxyNode->createAgentConnection();
277 if (pConn == NULL)
278 {
279 DbgPrintf(5, _T("SnmpCheckCommSettings(%s): cannot create proxy connection"), (const TCHAR *)ipAddr.toString());
280 goto fail;
281 }
282 pTransport = new SNMP_ProxyTransport(pConn, ipAddr, port);
283 }
284 else
285 {
286 pTransport = new SNMP_UDPTransport();
287 ((SNMP_UDPTransport *)pTransport)->createUDPTransport(ipAddr, port);
288 }
289
290 // Check for V3 USM
291 if (SnmpCheckV3CommSettings(pTransport, originalContext, testOids, (const TCHAR *)ipAddr.toString()))
292 {
293 *version = SNMP_VERSION_3;
294 goto success;
295 }
296
297 if (IsShutdownInProgress())
298 {
299 delete pTransport;
300 goto fail;
301 }
302
303 pTransport->setSnmpVersion(SNMP_VERSION_2C);
304 restart_check:
305 // Check current community first
306 if ((originalContext != NULL) && (originalContext->getSecurityModel() != SNMP_SECURITY_MODEL_USM))
307 {
308 DbgPrintf(5, _T("SnmpCheckCommSettings(%s): trying version %d community '%hs'"),
309 (const TCHAR *)ipAddr.toString(), pTransport->getSnmpVersion(), originalContext->getCommunity());
310 pTransport->setSecurityContext(new SNMP_SecurityContext(originalContext));
311 if (SnmpTestRequest(pTransport, testOids))
312 {
313 *version = pTransport->getSnmpVersion();
314 goto success;
315 }
316 }
317
318 // Check community from list
319 if (communities == NULL)
320 {
321 communities = new StringList();
322 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
323 DB_RESULT hResult = DBSelect(hdb, _T("SELECT community FROM snmp_communities"));
324 if (hResult != NULL)
325 {
326 int count = DBGetNumRows(hResult);
327 for(int i = 0; i < count; i++)
328 communities->addPreallocated(DBGetField(hResult, i, 0, NULL, 0));
329 DBFreeResult(hResult);
330 }
331 DBConnectionPoolReleaseConnection(hdb);
332 }
333
334 for(int i = 0; (i < communities->size()) && !IsShutdownInProgress(); i++)
335 {
336 #ifdef UNICODE
337 char *community = MBStringFromWideString(communities->get(i));
338 #else
339 const char *community = communities->get(i);
340 #endif
341 if ((originalContext == NULL) ||
342 (originalContext->getSecurityModel() == SNMP_SECURITY_MODEL_USM) ||
343 strcmp(community, originalContext->getCommunity()))
344 {
345 DbgPrintf(5, _T("SnmpCheckCommSettings(%s): trying version %d community '%hs'"),
346 (const TCHAR *)ipAddr.toString(), pTransport->getSnmpVersion(), community);
347 pTransport->setSecurityContext(new SNMP_SecurityContext(community));
348 #ifdef UNICODE
349 free(community);
350 #endif
351 if (SnmpTestRequest(pTransport, testOids))
352 {
353 *version = pTransport->getSnmpVersion();
354 goto success;
355 }
356 }
357 #ifdef UNICODE
358 else
359 {
360 free(community);
361 }
362 #endif
363 }
364
365 if ((pTransport->getSnmpVersion() == SNMP_VERSION_2C) && !IsShutdownInProgress())
366 {
367 pTransport->setSnmpVersion(SNMP_VERSION_1);
368 goto restart_check;
369 }
370 delete pTransport;
371 }
372
373 fail:
374 delete communities;
375 delete ports;
376 DbgPrintf(5, _T("SnmpCheckCommSettings(%s): failed"), (const TCHAR *)ipAddr.toString());
377 return NULL;
378
379 success:
380 delete communities;
381 delete ports;
382 DbgPrintf(5, _T("SnmpCheckCommSettings(%s): success (version=%d)"), (const TCHAR *)ipAddr.toString(), pTransport->getSnmpVersion());
383 return pTransport;
384 }