libnxsl, libnxdb, libnxsrv, nxget, and nxdbmgr are now UNICODE compatible; libtre...
[public/netxms.git] / src / server / core / np.cpp
CommitLineData
5039dede
AK
1/*
2** NetXMS - Network Management System
3** Copyright (C) 2003, 2004, 2005, 2006 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: np.cpp
20**
21**/
22
23#include "nxcore.h"
24
25
26//
27// Externals
28//
29
30extern Queue g_nodePollerQueue;
31
32
33//
34// Discovery class
35//
36
37class NXSL_DiscoveryClass : public NXSL_Class
38{
39public:
40 NXSL_DiscoveryClass();
41
bb51576f 42 virtual NXSL_Value *getAttr(NXSL_Object *pObject, const TCHAR *pszAttr);
5039dede
AK
43};
44
45
46//
47// Implementation of discovery class
48//
49
50NXSL_DiscoveryClass::NXSL_DiscoveryClass()
51 :NXSL_Class()
52{
53 strcpy(m_szName, "NewNode");
54}
55
bb51576f 56NXSL_Value *NXSL_DiscoveryClass::getAttr(NXSL_Object *pObject, const TCHAR *pszAttr)
5039dede
AK
57{
58 DISCOVERY_FILTER_DATA *pData;
59 NXSL_Value *pValue = NULL;
60 char szBuffer[256];
61
bb5365ed 62 pData = (DISCOVERY_FILTER_DATA *)pObject->getData();
5039dede
AK
63 if (!strcmp(pszAttr, "ipAddr"))
64 {
65 IpToStr(pData->dwIpAddr, szBuffer);
66 pValue = new NXSL_Value(szBuffer);
67 }
68 else if (!strcmp(pszAttr, "netMask"))
69 {
70 IpToStr(pData->dwNetMask, szBuffer);
71 pValue = new NXSL_Value(szBuffer);
72 }
73 else if (!strcmp(pszAttr, "subnet"))
74 {
75 IpToStr(pData->dwSubnetAddr, szBuffer);
76 pValue = new NXSL_Value(szBuffer);
77 }
78 else if (!strcmp(pszAttr, "isAgent"))
79 {
80 pValue = new NXSL_Value((LONG)((pData->dwFlags & NNF_IS_AGENT) ? 1 : 0));
81 }
82 else if (!strcmp(pszAttr, "isSNMP"))
83 {
84 pValue = new NXSL_Value((LONG)((pData->dwFlags & NNF_IS_SNMP) ? 1 : 0));
85 }
86 else if (!strcmp(pszAttr, "isBridge"))
87 {
88 pValue = new NXSL_Value((LONG)((pData->dwFlags & NNF_IS_BRIDGE) ? 1 : 0));
89 }
90 else if (!strcmp(pszAttr, "isRouter"))
91 {
92 pValue = new NXSL_Value((LONG)((pData->dwFlags & NNF_IS_ROUTER) ? 1 : 0));
93 }
94 else if (!strcmp(pszAttr, "isPrinter"))
95 {
96 pValue = new NXSL_Value((LONG)((pData->dwFlags & NNF_IS_PRINTER) ? 1 : 0));
97 }
98 else if (!strcmp(pszAttr, "isCDP"))
99 {
100 pValue = new NXSL_Value((LONG)((pData->dwFlags & NNF_IS_CDP) ? 1 : 0));
101 }
102 else if (!strcmp(pszAttr, "isSONMP"))
103 {
104 pValue = new NXSL_Value((LONG)((pData->dwFlags & NNF_IS_SONMP) ? 1 : 0));
105 }
106 else if (!strcmp(pszAttr, "isLLDP"))
107 {
108 pValue = new NXSL_Value((LONG)((pData->dwFlags & NNF_IS_LLDP) ? 1 : 0));
109 }
110 else if (!strcmp(pszAttr, "snmpVersion"))
111 {
112 pValue = new NXSL_Value((LONG)pData->nSNMPVersion);
113 }
114 else if (!strcmp(pszAttr, "snmpOID"))
115 {
116 pValue = new NXSL_Value(pData->szObjectId);
117 }
118 else if (!strcmp(pszAttr, "agentVersion"))
119 {
120 pValue = new NXSL_Value(pData->szAgentVersion);
121 }
122 else if (!strcmp(pszAttr, "platformName"))
123 {
124 pValue = new NXSL_Value(pData->szPlatform);
125 }
126 return pValue;
127}
128
129
130//
131// Discovery class object
132//
133
134static NXSL_DiscoveryClass m_nxslDiscoveryClass;
135
136
137//
138// Poll new node for configuration
139// Returns pointer to new node object on success or NULL on failure
140//
141
07f6978d
VK
142Node *PollNewNode(DWORD dwIpAddr, DWORD dwNetMask, DWORD dwCreationFlags,
143 TCHAR *pszName, DWORD dwProxyNode, DWORD dwSNMPProxy,
144 Cluster *pCluster)
5039dede
AK
145{
146 Node *pNode;
147 char szIpAddr1[32], szIpAddr2[32];
148 DWORD dwFlags = 0;
149
150 DbgPrintf(4, "PollNode(%s,%s)",
151 IpToStr(dwIpAddr, szIpAddr1), IpToStr(dwNetMask, szIpAddr2));
152 // Check for node existence
153 if ((FindNodeByIP(dwIpAddr) != NULL) ||
154 (FindSubnetByIP(dwIpAddr) != NULL))
155 {
156 DbgPrintf(4, "PollNode: Node %s already exist in database",
157 IpToStr(dwIpAddr, szIpAddr1));
158 return NULL;
159 }
160
161 if (dwCreationFlags & NXC_NCF_DISABLE_ICMP)
162 dwFlags |= NF_DISABLE_ICMP;
163 if (dwCreationFlags & NXC_NCF_DISABLE_SNMP)
164 dwFlags |= NF_DISABLE_SNMP;
165 if (dwCreationFlags & NXC_NCF_DISABLE_NXCP)
166 dwFlags |= NF_DISABLE_NXCP;
167 pNode = new Node(dwIpAddr, dwFlags, dwProxyNode, dwSNMPProxy, 0);
168 NetObjInsert(pNode, TRUE);
169 if (pszName != NULL)
170 pNode->SetName(pszName);
171
172 // Bind node to cluster before first configuration poll
173 if (pCluster != NULL)
174 {
1da08bc6 175 pCluster->ApplyToNode(pNode);
5039dede
AK
176 }
177
178 if (dwCreationFlags & NXC_NCF_CREATE_UNMANAGED)
179 {
180 pNode->SetMgmtStatus(FALSE);
181 }
5039dede
AK
182 pNode->Unhide();
183 PostEvent(EVENT_NODE_ADDED, pNode->Id(), NULL);
184
185 // Add default DCIs
7c521895 186 pNode->addItem(new DCItem(CreateUniqueId(IDG_ITEM), _T("Status"), DS_INTERNAL, DCI_DT_INT, 60, 30, pNode));
5039dede
AK
187 return pNode;
188}
189
190
191//
192// Check if newly discovered node should be added
193//
194
195static BOOL AcceptNewNode(DWORD dwIpAddr, DWORD dwNetMask)
196{
197 DISCOVERY_FILTER_DATA data;
df8a4ca2 198 TCHAR szFilter[MAX_DB_STRING], szBuffer[256], szIpAddr[16];
5039dede
AK
199 DWORD dwTemp;
200 AgentConnection *pAgentConn;
201 NXSL_Program *pScript;
202 NXSL_Value *pValue;
203 BOOL bResult = FALSE;
204 SNMP_UDPTransport *pTransport;
205
206 IpToStr(dwIpAddr, szIpAddr);
207 if ((FindNodeByIP(dwIpAddr) != NULL) ||
208 (FindSubnetByIP(dwIpAddr) != NULL))
209 {
210 DbgPrintf(4, "AcceptNewNode(%s): node already exist in database", szIpAddr);
211 return FALSE; // Node already exist in database
212 }
213
214 // Read configuration
215 ConfigReadStr("DiscoveryFilter", szFilter, MAX_DB_STRING, "");
216 StrStrip(szFilter);
217
218 // Run filter script
219 if ((szFilter[0] == 0) || (!_tcsicmp(szFilter, "none")))
220 {
221 DbgPrintf(4, "AcceptNewNode(%s): no filtering, node accepted", szIpAddr);
222 return TRUE; // No filtering
223 }
224
225 // Initialize new node data
226 memset(&data, 0, sizeof(DISCOVERY_FILTER_DATA));
227 data.dwIpAddr = dwIpAddr;
228 data.dwNetMask = dwNetMask;
229 data.dwSubnetAddr = dwIpAddr & dwNetMask;
230
231 // Check SNMP support
232 DbgPrintf(4, "AcceptNewNode(%s): checking SNMP support", szIpAddr);
233 pTransport = new SNMP_UDPTransport;
c4366266 234 pTransport->createUDPTransport(NULL, htonl(dwIpAddr), 161);
df8a4ca2
VK
235 SNMP_SecurityContext *ctx = SnmpCheckCommSettings(pTransport, &data.nSNMPVersion, NULL);
236 if (ctx != NULL)
5039dede
AK
237 {
238 data.dwFlags |= NNF_IS_SNMP;
df8a4ca2 239 delete ctx;
5039dede
AK
240 }
241
242 // Check NetXMS agent support
243 DbgPrintf(4, "AcceptNewNode(%s): checking NetXMS agent", szIpAddr);
244 pAgentConn = new AgentConnection(htonl(dwIpAddr), AGENT_LISTEN_PORT,
245 AUTH_NONE, "");
7c521895 246 if (pAgentConn->connect(g_pServerKey))
5039dede
AK
247 {
248 data.dwFlags |= NNF_IS_AGENT;
249 pAgentConn->GetParameter("Agent.Version", MAX_AGENT_VERSION_LEN, data.szAgentVersion);
250 pAgentConn->GetParameter("System.PlatformName", MAX_PLATFORM_NAME_LEN, data.szPlatform);
251 }
252
253 // Check if node is a router
254 if (data.dwFlags & NNF_IS_SNMP)
255 {
5d2c5741 256 if (SnmpGet(data.nSNMPVersion, pTransport,
5039dede
AK
257 ".1.3.6.1.2.1.4.1.0", NULL, 0, &dwTemp, sizeof(DWORD),
258 FALSE, FALSE) == SNMP_ERR_SUCCESS)
259 {
260 if (dwTemp == 1)
261 data.dwFlags |= NNF_IS_ROUTER;
262 }
263 }
264 else if (data.dwFlags & NNF_IS_AGENT)
265 {
266 // Check IP forwarding status
267 if (pAgentConn->GetParameter("Net.IP.Forwarding", 16, szBuffer) == ERR_SUCCESS)
268 {
269 if (_tcstoul(szBuffer, NULL, 10) != 0)
270 data.dwFlags |= NNF_IS_ROUTER;
271 }
272 }
273
274 // Check various SNMP device capabilities
275 if (data.dwFlags & NNF_IS_SNMP)
276 {
277 // Get SNMP OID
5d2c5741 278 SnmpGet(data.nSNMPVersion, pTransport,
5039dede
AK
279 ".1.3.6.1.2.1.1.2.0", NULL, 0, data.szObjectId, MAX_OID_LEN * 4,
280 FALSE, FALSE);
281
282 // Check if node is a bridge
5d2c5741 283 if (SnmpGet(data.nSNMPVersion, pTransport,
5039dede
AK
284 ".1.3.6.1.2.1.17.1.1.0", NULL, 0, szBuffer, 256,
285 FALSE, FALSE) == SNMP_ERR_SUCCESS)
286 {
287 data.dwFlags |= NNF_IS_BRIDGE;
288 }
289
290 // Check for CDP (Cisco Discovery Protocol) support
5d2c5741 291 if (SnmpGet(data.nSNMPVersion, pTransport,
5039dede
AK
292 ".1.3.6.1.4.1.9.9.23.1.3.1.0", NULL, 0, &dwTemp, sizeof(DWORD),
293 FALSE, FALSE) == SNMP_ERR_SUCCESS)
294 {
295 if (dwTemp == 1)
296 data.dwFlags |= NNF_IS_CDP;
297 }
298
299 // Check for SONMP (Nortel topology discovery protocol) support
5d2c5741 300 if (SnmpGet(data.nSNMPVersion, pTransport,
5039dede
AK
301 ".1.3.6.1.4.1.45.1.6.13.1.2.0", NULL, 0, &dwTemp, sizeof(DWORD),
302 FALSE, FALSE) == SNMP_ERR_SUCCESS)
303 {
304 if (dwTemp == 1)
305 data.dwFlags |= NNF_IS_SONMP;
306 }
307
308 // Check for LLDP (Link Layer Discovery Protocol) support
5d2c5741 309 if (SnmpGet(data.nSNMPVersion, pTransport,
5039dede
AK
310 ".1.0.8802.1.1.2.1.3.2.0", NULL, 0, szBuffer, 256,
311 FALSE, FALSE) == SNMP_ERR_SUCCESS)
312 {
313 data.dwFlags |= NNF_IS_LLDP;
314 }
315 }
316
317 // Cleanup
318 delete pAgentConn;
319 delete pTransport;
320
321 // Check if we use simple filter instead of script
322 if (!_tcsicmp(szFilter, "auto"))
323 {
324 DWORD dwFlags;
325 DB_RESULT hResult;
326 int i, nRows, nType;
327
328 dwFlags = ConfigReadULong(_T("DiscoveryFilterFlags"), DFF_ALLOW_AGENT | DFF_ALLOW_SNMP);
329 DbgPrintf(4, "AcceptNewNode(%s): auto filter, dwFlags=%04X", szIpAddr, dwFlags);
330
331 if ((dwFlags & (DFF_ALLOW_AGENT | DFF_ALLOW_SNMP)) == 0)
332 {
333 bResult = TRUE;
334 }
335 else
336 {
337 if (dwFlags & DFF_ALLOW_AGENT)
338 {
339 if (data.dwFlags & NNF_IS_AGENT)
340 bResult = TRUE;
341 }
342
343 if (dwFlags & DFF_ALLOW_SNMP)
344 {
345 if (data.dwFlags & NNF_IS_SNMP)
346 bResult = TRUE;
347 }
348 }
349
350 // Check range
351 if ((dwFlags & DFF_ONLY_RANGE) && bResult)
352 {
353 DbgPrintf(4, "AcceptNewNode(%s): auto filter - checking range", szIpAddr);
354 hResult = DBSelect(g_hCoreDB, _T("SELECT addr_type,addr1,addr2 FROM address_lists WHERE list_type=2"));
355 if (hResult != NULL)
356 {
357 nRows = DBGetNumRows(hResult);
358 for(i = 0, bResult = FALSE; (i < nRows) && (!bResult); i++)
359 {
360 nType = DBGetFieldLong(hResult, i, 0);
361 if (nType == 0)
362 {
363 // Subnet
364 bResult = (data.dwIpAddr & DBGetFieldIPAddr(hResult, i, 2)) == DBGetFieldIPAddr(hResult, i, 1);
365 }
366 else
367 {
368 // Range
369 bResult = ((data.dwIpAddr >= DBGetFieldIPAddr(hResult, i, 1)) &&
370 (data.dwIpAddr <= DBGetFieldIPAddr(hResult, i, 2)));
371 }
372 }
373 DBFreeResult(hResult);
374 }
375 }
376 DbgPrintf(4, "AcceptNewNode(%s): auto filter - bResult=%d", szIpAddr, bResult);
377 }
378 else
379 {
bb5365ed
VK
380 g_pScriptLibrary->lock();
381 pScript = g_pScriptLibrary->findScript(szFilter);
5039dede
AK
382 if (pScript != NULL)
383 {
384 DbgPrintf(4, "AcceptNewNode(%s): Running filter script %s", szIpAddr, szFilter);
385 pValue = new NXSL_Value(new NXSL_Object(&m_nxslDiscoveryClass, &data));
13ceb0fb 386 if (pScript->run(new NXSL_ServerEnv, 1, &pValue) == 0)
5039dede 387 {
bb5365ed 388 bResult = (pScript->getResult()->getValueAsInt32() != 0) ? TRUE : FALSE;
5039dede
AK
389 DbgPrintf(4, "AcceptNewNode(%s): Filter script result: %d", szIpAddr, bResult);
390 }
391 else
392 {
393 DbgPrintf(4, "AcceptNewNode(%s): Filter script execution error: %s",
bb5365ed 394 szIpAddr, pScript->getErrorText());
5039dede 395 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, _T("ssd"), szFilter,
bb5365ed 396 pScript->getErrorText(), 0);
5039dede
AK
397 }
398 }
399 else
400 {
401 DbgPrintf(4, "AcceptNewNode(%s): Cannot find filter script %s", szIpAddr, szFilter);
402 }
bb5365ed 403 g_pScriptLibrary->unlock();
5039dede
AK
404 }
405
406 return bResult;
407}
408
409
410//
411// Node poller thread (poll new nodes and put them into the database)
412//
413
414THREAD_RESULT THREAD_CALL NodePoller(void *arg)
415{
416 NEW_NODE *pInfo;
417 TCHAR szIpAddr[16], szNetMask[16];
418
419 DbgPrintf(1, "Node poller started");
420
421 while(!ShutdownInProgress())
422 {
423 pInfo = (NEW_NODE *)g_nodePollerQueue.GetOrBlock();
424 if (pInfo == INVALID_POINTER_VALUE)
425 break; // Shutdown indicator received
426
427 DbgPrintf(4, "NodePoller: processing node %s/%s",
428 IpToStr(pInfo->dwIpAddr, szIpAddr),
429 IpToStr(pInfo->dwNetMask, szNetMask));
430 if (AcceptNewNode(pInfo->dwIpAddr, pInfo->dwNetMask))
07f6978d
VK
431 {
432 Node *node = PollNewNode(pInfo->dwIpAddr, pInfo->dwNetMask, 0, NULL, 0, 0, NULL);
433 if (node != NULL)
434 {
435 // We should do configuration poll before taking new node from the queue
436 // to prevent possible node duplication
58b3e451 437 node->configurationPoll(NULL, 0, -1, pInfo->dwNetMask);
07f6978d
VK
438 }
439 }
5039dede
AK
440 free(pInfo);
441 }
442 DbgPrintf(1, "Node poller thread terminated");
443 return THREAD_OK;
444}