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