0843a81808ee50cad282d53a49c2300af1667ead
[public/netxms.git] / src / server / core / accesspoint.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2017 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: accesspoint.cpp
20 **/
21
22 #include "nxcore.h"
23
24 /**
25 * Default constructor
26 */
27 AccessPoint::AccessPoint() : DataCollectionTarget()
28 {
29 m_nodeId = 0;
30 m_index = 0;
31 memset(m_macAddr, 0, MAC_ADDR_LENGTH);
32 m_vendor = NULL;
33 m_model = NULL;
34 m_serialNumber = NULL;
35 m_radioInterfaces = NULL;
36 m_apState = AP_ADOPTED;
37 m_prevState = m_apState;
38 }
39
40 /**
41 * Constructor for creating new access point object
42 */
43 AccessPoint::AccessPoint(const TCHAR *name, UINT32 index, const BYTE *macAddr) : DataCollectionTarget(name)
44 {
45 m_nodeId = 0;
46 m_index = index;
47 memcpy(m_macAddr, macAddr, MAC_ADDR_LENGTH);
48 m_vendor = NULL;
49 m_model = NULL;
50 m_serialNumber = NULL;
51 m_radioInterfaces = NULL;
52 m_apState = AP_ADOPTED;
53 m_prevState = m_apState;
54 m_isHidden = true;
55 }
56
57 /**
58 * Destructor
59 */
60 AccessPoint::~AccessPoint()
61 {
62 safe_free(m_vendor);
63 safe_free(m_model);
64 safe_free(m_serialNumber);
65 delete m_radioInterfaces;
66 }
67
68 /**
69 * Create object from database data
70 */
71 bool AccessPoint::loadFromDatabase(DB_HANDLE hdb, UINT32 dwId)
72 {
73 m_id = dwId;
74
75 if (!loadCommonProperties(hdb))
76 {
77 DbgPrintf(2, _T("Cannot load common properties for access point object %d"), dwId);
78 return false;
79 }
80
81 TCHAR query[256];
82 _sntprintf(query, 256, _T("SELECT mac_address,vendor,model,serial_number,node_id,ap_state,ap_index FROM access_points WHERE id=%d"), (int)m_id);
83 DB_RESULT hResult = DBSelect(hdb, query);
84 if (hResult == NULL)
85 return false;
86
87 DBGetFieldByteArray2(hResult, 0, 0, m_macAddr, MAC_ADDR_LENGTH, 0);
88 m_vendor = DBGetField(hResult, 0, 1, NULL, 0);
89 m_model = DBGetField(hResult, 0, 2, NULL, 0);
90 m_serialNumber = DBGetField(hResult, 0, 3, NULL, 0);
91 m_nodeId = DBGetFieldULong(hResult, 0, 4);
92 m_apState = (AccessPointState)DBGetFieldLong(hResult, 0, 5);
93 m_prevState = (m_apState != AP_DOWN) ? m_apState : AP_ADOPTED;
94 m_index = DBGetFieldULong(hResult, 0, 6);
95 DBFreeResult(hResult);
96
97 // Load DCI and access list
98 loadACLFromDB(hdb);
99 loadItemsFromDB(hdb);
100 for(int i = 0; i < m_dcObjects->size(); i++)
101 if (!m_dcObjects->get(i)->loadThresholdsFromDB(hdb))
102 return false;
103
104 // Link access point to node
105 bool success = false;
106 if (!m_isDeleted)
107 {
108 NetObj *object = FindObjectById(m_nodeId);
109 if (object == NULL)
110 {
111 nxlog_write(MSG_INVALID_NODE_ID, EVENTLOG_ERROR_TYPE, "dd", dwId, m_nodeId);
112 }
113 else if (object->getObjectClass() != OBJECT_NODE)
114 {
115 nxlog_write(MSG_NODE_NOT_NODE, EVENTLOG_ERROR_TYPE, "dd", dwId, m_nodeId);
116 }
117 else
118 {
119 object->addChild(this);
120 addParent(object);
121 success = true;
122 }
123 }
124 else
125 {
126 success = true;
127 }
128
129 return success;
130 }
131
132 /**
133 * Save object to database
134 */
135 bool AccessPoint::saveToDatabase(DB_HANDLE hdb)
136 {
137 // Lock object's access
138 lockProperties();
139
140 saveCommonProperties(hdb);
141
142 bool bResult;
143 DB_STATEMENT hStmt;
144 if (IsDatabaseRecordExist(hdb, _T("access_points"), _T("id"), m_id))
145 hStmt = DBPrepare(hdb, _T("UPDATE access_points SET mac_address=?,vendor=?,model=?,serial_number=?,node_id=?,ap_state=?,ap_index=? WHERE id=?"));
146 else
147 hStmt = DBPrepare(hdb, _T("INSERT INTO access_points (mac_address,vendor,model,serial_number,node_id,ap_state,ap_index,id) VALUES (?,?,?,?,?,?,?,?)"));
148 if (hStmt != NULL)
149 {
150 TCHAR macStr[16];
151 DBBind(hStmt, 1, DB_SQLTYPE_VARCHAR, BinToStr(m_macAddr, MAC_ADDR_LENGTH, macStr), DB_BIND_STATIC);
152 DBBind(hStmt, 2, DB_SQLTYPE_VARCHAR, CHECK_NULL_EX(m_vendor), DB_BIND_STATIC);
153 DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, CHECK_NULL_EX(m_model), DB_BIND_STATIC);
154 DBBind(hStmt, 4, DB_SQLTYPE_VARCHAR, CHECK_NULL_EX(m_serialNumber), DB_BIND_STATIC);
155 DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, m_nodeId);
156 DBBind(hStmt, 6, DB_SQLTYPE_INTEGER, (INT32)m_apState);
157 DBBind(hStmt, 7, DB_SQLTYPE_INTEGER, m_index);
158 DBBind(hStmt, 8, DB_SQLTYPE_INTEGER, m_id);
159
160 bResult = DBExecute(hStmt);
161
162 DBFreeStatement(hStmt);
163 }
164 else
165 {
166 bResult = false;
167 }
168
169 // Save data collection items
170 if (bResult)
171 {
172 lockDciAccess(false);
173 for(int i = 0; i < m_dcObjects->size(); i++)
174 m_dcObjects->get(i)->saveToDatabase(hdb);
175 unlockDciAccess();
176 }
177
178 // Save access list
179 saveACLToDB(hdb);
180
181 // Clear modifications flag and unlock object
182 if (bResult)
183 m_isModified = false;
184 unlockProperties();
185
186 return bResult;
187 }
188
189 /**
190 * Delete object from database
191 */
192 bool AccessPoint::deleteFromDatabase(DB_HANDLE hdb)
193 {
194 bool success = DataCollectionTarget::deleteFromDatabase(hdb);
195 if (success)
196 success = executeQueryOnObject(hdb, _T("DELETE FROM access_points WHERE id=?"));
197 return success;
198 }
199
200 /**
201 * Create CSCP message with object's data
202 */
203 void AccessPoint::fillMessageInternal(NXCPMessage *msg)
204 {
205 DataCollectionTarget::fillMessageInternal(msg);
206 msg->setField(VID_IP_ADDRESS, m_ipAddress);
207 msg->setField(VID_NODE_ID, m_nodeId);
208 msg->setField(VID_MAC_ADDR, m_macAddr, MAC_ADDR_LENGTH);
209 msg->setField(VID_VENDOR, CHECK_NULL_EX(m_vendor));
210 msg->setField(VID_MODEL, CHECK_NULL_EX(m_model));
211 msg->setField(VID_SERIAL_NUMBER, CHECK_NULL_EX(m_serialNumber));
212 msg->setField(VID_STATE, (UINT16)m_apState);
213 msg->setField(VID_AP_INDEX, m_index);
214
215 if (m_radioInterfaces != NULL)
216 {
217 msg->setField(VID_RADIO_COUNT, (WORD)m_radioInterfaces->size());
218 UINT32 varId = VID_RADIO_LIST_BASE;
219 for(int i = 0; i < m_radioInterfaces->size(); i++)
220 {
221 RadioInterfaceInfo *rif = m_radioInterfaces->get(i);
222 msg->setField(varId++, (UINT32)rif->index);
223 msg->setField(varId++, rif->name);
224 msg->setField(varId++, rif->macAddr, MAC_ADDR_LENGTH);
225 msg->setField(varId++, rif->channel);
226 msg->setField(varId++, (UINT32)rif->powerDBm);
227 msg->setField(varId++, (UINT32)rif->powerMW);
228 varId += 4;
229 }
230 }
231 else
232 {
233 msg->setField(VID_RADIO_COUNT, (WORD)0);
234 }
235 }
236
237 /**
238 * Modify object from message
239 */
240 UINT32 AccessPoint::modifyFromMessageInternal(NXCPMessage *msg)
241 {
242 return DataCollectionTarget::modifyFromMessageInternal(msg);
243 }
244
245 /**
246 * Attach access point to node
247 */
248 void AccessPoint::attachToNode(UINT32 nodeId)
249 {
250 if (m_nodeId == nodeId)
251 return;
252
253 if (m_nodeId != 0)
254 {
255 Node *currNode = (Node *)FindObjectById(m_nodeId, OBJECT_NODE);
256 if (currNode != NULL)
257 {
258 currNode->deleteChild(this);
259 deleteParent(currNode);
260 }
261 }
262
263 Node *newNode = (Node *)FindObjectById(nodeId, OBJECT_NODE);
264 if (newNode != NULL)
265 {
266 newNode->addChild(this);
267 addParent(newNode);
268 }
269
270 lockProperties();
271 m_nodeId = nodeId;
272 setModified();
273 unlockProperties();
274 }
275
276 /**
277 * Update radio interfaces information
278 */
279 void AccessPoint::updateRadioInterfaces(const ObjectArray<RadioInterfaceInfo> *ri)
280 {
281 lockProperties();
282 if (m_radioInterfaces == NULL)
283 m_radioInterfaces = new ObjectArray<RadioInterfaceInfo>(ri->size(), 4, true);
284 m_radioInterfaces->clear();
285 for(int i = 0; i < ri->size(); i++)
286 {
287 RadioInterfaceInfo *info = new RadioInterfaceInfo;
288 memcpy(info, ri->get(i), sizeof(RadioInterfaceInfo));
289 m_radioInterfaces->add(info);
290 }
291 unlockProperties();
292 }
293
294 /**
295 * Check if given radio interface index (radio ID) is on this access point
296 */
297 bool AccessPoint::isMyRadio(int rfIndex)
298 {
299 bool result = false;
300 lockProperties();
301 if (m_radioInterfaces != NULL)
302 {
303 for(int i = 0; i < m_radioInterfaces->size(); i++)
304 {
305 if (m_radioInterfaces->get(i)->index == rfIndex)
306 {
307 result = true;
308 break;
309 }
310 }
311 }
312 unlockProperties();
313 return result;
314 }
315
316 /**
317 * Check if given radio MAC address (BSSID) is on this access point
318 */
319 bool AccessPoint::isMyRadio(const BYTE *macAddr)
320 {
321 bool result = false;
322 lockProperties();
323 if (m_radioInterfaces != NULL)
324 {
325 for(int i = 0; i < m_radioInterfaces->size(); i++)
326 {
327 if (!memcmp(m_radioInterfaces->get(i)->macAddr, macAddr, MAC_ADDR_LENGTH))
328 {
329 result = true;
330 break;
331 }
332 }
333 }
334 unlockProperties();
335 return result;
336 }
337
338 /**
339 * Get radio name
340 */
341 void AccessPoint::getRadioName(int rfIndex, TCHAR *buffer, size_t bufSize)
342 {
343 buffer[0] = 0;
344 lockProperties();
345 if (m_radioInterfaces != NULL)
346 {
347 for(int i = 0; i < m_radioInterfaces->size(); i++)
348 {
349 if (m_radioInterfaces->get(i)->index == rfIndex)
350 {
351 nx_strncpy(buffer, m_radioInterfaces->get(i)->name, bufSize);
352 break;
353 }
354 }
355 }
356 unlockProperties();
357 }
358
359 /**
360 * Get access point's parent node
361 */
362 Node *AccessPoint::getParentNode()
363 {
364 return (Node *)FindObjectById(m_nodeId, OBJECT_NODE);
365 }
366
367 /**
368 * Update access point information
369 */
370 void AccessPoint::updateInfo(const TCHAR *vendor, const TCHAR *model, const TCHAR *serialNumber)
371 {
372 lockProperties();
373
374 free(m_vendor);
375 m_vendor = (vendor != NULL) ? _tcsdup(vendor) : NULL;
376
377 free(m_model);
378 m_model = (model != NULL) ? _tcsdup(model) : NULL;
379
380 free(m_serialNumber);
381 m_serialNumber = (serialNumber != NULL) ? _tcsdup(serialNumber) : NULL;
382
383 setModified();
384 unlockProperties();
385 }
386
387 /**
388 * Update access point state
389 */
390 void AccessPoint::updateState(AccessPointState state)
391 {
392 if (state == m_apState)
393 return;
394
395 lockProperties();
396 if (state == AP_DOWN)
397 m_prevState = m_apState;
398 m_apState = state;
399 if (m_status != STATUS_UNMANAGED)
400 {
401 switch(state)
402 {
403 case AP_ADOPTED:
404 m_status = STATUS_NORMAL;
405 break;
406 case AP_UNADOPTED:
407 m_status = STATUS_MAJOR;
408 break;
409 case AP_DOWN:
410 m_status = STATUS_CRITICAL;
411 break;
412 default:
413 m_status = STATUS_UNKNOWN;
414 break;
415 }
416 }
417 setModified();
418 unlockProperties();
419
420 if ((state == AP_ADOPTED) || (state == AP_UNADOPTED) || (state == AP_DOWN))
421 {
422 static const TCHAR *names[] = { _T("id"), _T("name"), _T("macAddr"), _T("ipAddr"), _T("vendor"), _T("model"), _T("serialNumber") };
423 PostEventWithNames((state == AP_ADOPTED) ? EVENT_AP_ADOPTED : ((state == AP_UNADOPTED) ? EVENT_AP_UNADOPTED : EVENT_AP_DOWN),
424 m_nodeId, "ishAsss", names,
425 m_id, m_name, m_macAddr, &m_ipAddress,
426 CHECK_NULL_EX(m_vendor), CHECK_NULL_EX(m_model), CHECK_NULL_EX(m_serialNumber));
427 }
428 }
429
430 /**
431 * Do status poll
432 */
433 void AccessPoint::statusPollFromController(ClientSession *session, UINT32 rqId, Queue *eventQueue, Node *controller, SNMP_Transport *snmpTransport)
434 {
435 m_pollRequestor = session;
436
437 sendPollerMsg(rqId, _T(" Starting status poll on access point %s\r\n"), m_name);
438 sendPollerMsg(rqId, _T(" Current access point status is %s\r\n"), GetStatusAsText(m_status, true));
439
440 AccessPointState state = controller->getAccessPointState(this, snmpTransport);
441 if ((state == AP_UNKNOWN) && m_ipAddress.isValid())
442 {
443 DbgPrintf(6, _T("AccessPoint::statusPoll(%s [%d]): unable to get AP state from driver"), m_name, m_id);
444 sendPollerMsg(rqId, POLLER_WARNING _T(" Unable to get AP state from controller\r\n"));
445
446 UINT32 icmpProxy = 0;
447
448 if (IsZoningEnabled() && (controller->getZoneUIN() != 0))
449 {
450 Zone *zone = FindZoneByUIN(controller->getZoneUIN());
451 if (zone != NULL)
452 {
453 icmpProxy = zone->getProxyNodeId();
454 }
455 }
456
457 if (icmpProxy != 0)
458 {
459 sendPollerMsg(rqId, _T(" Starting ICMP ping via proxy\r\n"));
460 DbgPrintf(7, _T("AccessPoint::StatusPoll(%d,%s): ping via proxy [%u]"), m_id, m_name, icmpProxy);
461 Node *proxyNode = (Node *)g_idxNodeById.get(icmpProxy);
462 if ((proxyNode != NULL) && proxyNode->isNativeAgent() && !proxyNode->isDown())
463 {
464 DbgPrintf(7, _T("AccessPoint::StatusPoll(%d,%s): proxy node found: %s"), m_id, m_name, proxyNode->getName());
465 AgentConnection *conn = proxyNode->createAgentConnection();
466 if (conn != NULL)
467 {
468 TCHAR parameter[64], buffer[64];
469
470 _sntprintf(parameter, 64, _T("Icmp.Ping(%s)"), m_ipAddress.toString(buffer));
471 if (conn->getParameter(parameter, 64, buffer) == ERR_SUCCESS)
472 {
473 DbgPrintf(7, _T("AccessPoint::StatusPoll(%d,%s): proxy response: \"%s\""), m_id, m_name, buffer);
474 TCHAR *eptr;
475 long value = _tcstol(buffer, &eptr, 10);
476 if ((*eptr == 0) && (value >= 0))
477 {
478 m_pingLastTimeStamp = time(NULL);
479 m_pingTime = value;
480 if (value < 10000)
481 {
482 sendPollerMsg(rqId, POLLER_ERROR _T(" responded to ICMP ping\r\n"));
483 if (state == AP_DOWN)
484 state = m_prevState; /* FIXME: get actual AP state here */
485 }
486 else
487 {
488 sendPollerMsg(rqId, POLLER_ERROR _T(" no response to ICMP ping\r\n"));
489 state = AP_DOWN;
490 }
491 }
492 }
493 conn->disconnect();
494 conn->decRefCount();
495 }
496 else
497 {
498 DbgPrintf(7, _T("AccessPoint::StatusPoll(%d,%s): cannot connect to agent on proxy node"), m_id, m_name);
499 sendPollerMsg(rqId, POLLER_ERROR _T(" Unable to establish connection with proxy node\r\n"));
500 }
501 }
502 else
503 {
504 DbgPrintf(7, _T("AccessPoint::StatusPoll(%d,%s): proxy node not available"), m_id, m_name);
505 sendPollerMsg(rqId, POLLER_ERROR _T(" ICMP proxy not available\r\n"));
506 }
507 }
508 else // not using ICMP proxy
509 {
510 TCHAR buffer[64];
511 sendPollerMsg(rqId, _T(" Starting ICMP ping\r\n"));
512 DbgPrintf(7, _T("AccessPoint::StatusPoll(%d,%s): calling IcmpPing(%s,3,%d,NULL,%d)"), m_id, m_name, m_ipAddress.toString(buffer), g_icmpPingTimeout, g_icmpPingSize);
513 UINT32 dwPingStatus = IcmpPing(m_ipAddress, 3, g_icmpPingTimeout, &m_pingTime, g_icmpPingSize);
514 m_pingLastTimeStamp = time(NULL);
515 if (dwPingStatus == ICMP_SUCCESS)
516 {
517 sendPollerMsg(rqId, POLLER_ERROR _T(" responded to ICMP ping\r\n"));
518 if (state == AP_DOWN)
519 state = m_prevState; /* FIXME: get actual AP state here */
520 }
521 else
522 {
523 sendPollerMsg(rqId, POLLER_ERROR _T(" no response to ICMP ping\r\n"));
524 state = AP_DOWN;
525 m_pingTime = PING_TIME_TIMEOUT;
526 }
527 DbgPrintf(7, _T("AccessPoint::StatusPoll(%d,%s): ping result %d, state=%d"), m_id, m_name, dwPingStatus, state);
528 }
529 }
530
531 updateState(state);
532
533 sendPollerMsg(rqId, _T(" Access point status after poll is %s\r\n"), GetStatusAsText(m_status, true));
534 sendPollerMsg(rqId, _T(" Finished status poll on access point %s\r\n"), m_name);
535 }
536
537 /**
538 * Updates last ping time and ping time
539 */
540 void AccessPoint::updatePingData()
541 {
542 Node *pNode = getParentNode();
543 if (pNode == NULL)
544 {
545 DbgPrintf(7, _T("AccessPoint::updatePingData: Can't find parent node"));
546 return;
547 }
548 UINT32 icmpProxy = pNode->getIcmpProxy();
549
550 if (IsZoningEnabled() && (pNode->getZoneUIN() != 0) && (icmpProxy == 0))
551 {
552 Zone *zone = FindZoneByUIN(pNode->getZoneUIN());
553 if (zone != NULL)
554 {
555 icmpProxy = zone->getProxyNodeId();
556 }
557 }
558
559 if (icmpProxy != 0)
560 {
561 DbgPrintf(7, _T("AccessPoint::updatePingData: ping via proxy [%u]"), icmpProxy);
562 Node *proxyNode = (Node *)g_idxNodeById.get(icmpProxy);
563 if ((proxyNode != NULL) && proxyNode->isNativeAgent() && !proxyNode->isDown())
564 {
565 DbgPrintf(7, _T("AccessPoint::updatePingData: proxy node found: %s"), proxyNode->getName());
566 AgentConnection *conn = proxyNode->createAgentConnection();
567 if (conn != NULL)
568 {
569 TCHAR parameter[64], buffer[64];
570
571 _sntprintf(parameter, 64, _T("Icmp.Ping(%s)"), m_ipAddress.toString(buffer));
572 if (conn->getParameter(parameter, 64, buffer) == ERR_SUCCESS)
573 {
574 DbgPrintf(7, _T("AccessPoint::updatePingData: proxy response: \"%s\""), buffer);
575 TCHAR *eptr;
576 long value = _tcstol(buffer, &eptr, 10);
577 m_pingLastTimeStamp = time(NULL);
578 if ((*eptr == 0) && (value >= 0) && (value < 10000))
579 {
580 m_pingTime = value;
581 }
582 else
583 {
584 m_pingTime = PING_TIME_TIMEOUT;
585 DbgPrintf(7, _T("AccessPoint::updatePingData: incorrect value: %d or error while parsing: %s"), value, eptr);
586 }
587 }
588 conn->disconnect();
589 conn->decRefCount();
590 }
591 else
592 {
593 DbgPrintf(7, _T("AccessPoint::updatePingData: cannot connect to agent on proxy node [%u]"), icmpProxy);
594 }
595 }
596 else
597 {
598 DbgPrintf(7, _T("AccessPoint::updatePingData: proxy node not available [%u]"), icmpProxy);
599 }
600 }
601 else // not using ICMP proxy
602 {
603 UINT32 dwPingStatus = IcmpPing(m_ipAddress, 3, g_icmpPingTimeout, &m_pingTime, g_icmpPingSize);
604 if (dwPingStatus != ICMP_SUCCESS)
605 {
606 DbgPrintf(7, _T("AccessPoint::updatePingData: error getting ping %d"), dwPingStatus);
607 m_pingTime = PING_TIME_TIMEOUT;
608 }
609 m_pingLastTimeStamp = time(NULL);
610 }
611 }
612
613 /**
614 * Serialize object to JSON
615 */
616 json_t *AccessPoint::toJson()
617 {
618 json_t *root = DataCollectionTarget::toJson();
619 json_object_set_new(root, "index", json_integer(m_index));
620 json_object_set_new(root, "ipAddress", m_ipAddress.toJson());
621 json_object_set_new(root, "nodeId", json_integer(m_nodeId));
622 char macAddrText[64];
623 json_object_set_new(root, "macAddr", json_string_a(BinToStrA(m_macAddr, sizeof(m_macAddr), macAddrText)));
624 json_object_set_new(root, "vendor", json_string_t(m_vendor));
625 json_object_set_new(root, "model", json_string_t(m_model));
626 json_object_set_new(root, "serialNumber", json_string_t(m_serialNumber));
627 json_object_set_new(root, "radioInterfaces", json_object_array(m_radioInterfaces));
628 json_object_set_new(root, "state", json_integer(m_apState));
629 json_object_set_new(root, "prevState", json_integer(m_prevState));
630 return root;
631 }