Coverage Report

Created: 2024-02-25 06:37

/src/ntopng/src/SNMP.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *
3
 * (C) 2013-24 - ntop.org
4
 *
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software Foundation,
18
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
 *
20
 */
21
22
#include "ntop_includes.h"
23
24
/* Code needed by both implementations for batch mode */
25
extern "C" {
26
#include "../third-party/snmp/snmp.c"
27
#include "../third-party/snmp/asn1.c"
28
#include "../third-party/snmp/net.c"
29
};
30
31
#ifdef HAVE_LIBSNMP
32
33
/* ******************************* */
34
35
SNMPSession::SNMPSession() { session_ptr = NULL; }
36
37
/* ******************************* */
38
39
SNMPSession::~SNMPSession() {
40
  if (session_ptr) snmp_sess_close(session_ptr);
41
}
42
43
/* ******************************* */
44
45
SNMP::SNMP() {
46
  if(trace_new_delete) ntop->getTrace()->traceEvent(TRACE_NORMAL, "[new] %s", __FILE__);
47
  batch_mode = false;
48
#ifdef HAVE_LIBSNMP
49
  init_snmp("ntopng");
50
#endif
51
}
52
53
/* ******************************* */
54
55
SNMP::~SNMP() {
56
  for (unsigned int i = 0; i < sessions.size(); i++) delete sessions.at(i);
57
}
58
59
/* ******************************* */
60
61
/*
62
   http://www.net-snmp.org/docs/README.thread.html
63
   https://github.com/bluecmd/python3-netsnmp/blob/master/netsnmp/client_intf.c
64
*/
65
66
void SNMP::handle_async_response(struct snmp_pdu *pdu, const char *agent_ip) {
67
  netsnmp_variable_list *vp = pdu->variables;
68
  bool table_added = false;
69
70
  while (vp != NULL) {
71
    /* OID */
72
    char rsp_oid[256], buf[256];
73
    int offset = 0;
74
75
    switch (vp->type) {
76
      case SNMP_NOSUCHOBJECT:
77
      case SNMP_NOSUCHINSTANCE:
78
      case SNMP_ENDOFMIBVIEW:
79
        vp = vp->next_variable;
80
        continue; /* Error found */
81
        break;
82
    }
83
84
    if (batch_mode)
85
      snprintf(rsp_oid, sizeof(rsp_oid), "%s", agent_ip);
86
    else {
87
      for (u_int i = 0; i < vp->name_length; i++) {
88
        int rc = snprintf(&rsp_oid[offset], sizeof(rsp_oid) - offset, "%s%d",
89
                          (offset > 0) ? "." : "", (int)vp->name_loc[i]);
90
91
        if (rc > 0)
92
          offset += rc;
93
        else
94
          break;
95
      }
96
    }
97
98
    if (!table_added) lua_newtable(vm), table_added = true;
99
100
    switch (vp->type) {
101
      case ASN_INTEGER:
102
        /* case ASN_GAUGE: */ /* Alias of ASN_INTEGER */
103
#ifdef NATIVE_TYPE
104
        lua_push_int32_table_entry(vm, rsp_oid, (long)*vp->val.integer);
105
#else
106
        snprintf(buf, sizeof(buf), "%ld", (long)*vp->val.integer);
107
        lua_push_str_table_entry(vm, rsp_oid, buf);
108
#endif
109
        break;
110
111
      case ASN_UNSIGNED:
112
      case ASN_TIMETICKS:
113
      case ASN_COUNTER:
114
        // ntop->getTrace()->traceEvent(TRACE_WARNING, "%s = %d", rsp_oid,
115
        // vp->val.integer);
116
#ifdef NATIVE_TYPE
117
        lua_push_uint32_table_entry(vm, rsp_oid, (u_int32_t)*vp->val.integer);
118
#else
119
        snprintf(buf, sizeof(buf), "%u", (u_int32_t)*vp->val.integer);
120
        lua_push_str_table_entry(vm, rsp_oid, buf);
121
#endif
122
        break;
123
124
      case ASN_COUNTER64: {
125
        u_int64_t v =
126
            ((u_int64_t)vp->val.counter64->high << 32) + vp->val.counter64->low;
127
128
#ifdef NATIVE_TYPE
129
        lua_push_uint32_table_entry(vm, rsp_oid, v);
130
#else
131
        snprintf(buf, sizeof(buf), "%llu", (long long unsigned int)v);
132
        lua_push_str_table_entry(vm, rsp_oid, buf);
133
#endif
134
      } break;
135
136
      case ASN_OCTET_STR: {
137
        // ntop->getTrace()->traceEvent(TRACE_WARNING, "%s = %s", rsp_oid,
138
        // vp->val.string);
139
        char buf[512];
140
        bool is_printable = true;
141
        u_int i, len = min(sizeof(buf) - 1, vp->val_len);
142
143
        for (i = 0; i < len; i++) {
144
          if ((!isprint(vp->val.string[i])) && (!isspace(vp->val.string[i]))) {
145
            is_printable = false;
146
          }
147
        }
148
149
        if (is_printable) {
150
          strncpy(buf, (const char *)vp->val.string, len);
151
          buf[len] = '\0';
152
        } else if (len == 4) {
153
          snprintf(buf, sizeof(buf), "%u.%u.%u.%u", vp->val.string[0],
154
                   vp->val.string[1], vp->val.string[2], vp->val.string[3]);
155
        } else {
156
          u_int idx = 0;
157
158
          for (i = 0; i < len; i++) {
159
            int left = sizeof(buf) - idx - 1;
160
            int rc;
161
162
            if (left <= 2) break;
163
            rc = snprintf(&buf[idx], left, "%s%02X", (i == 0) ? "" : " ",
164
                          vp->val.string[i] & 0xFF);
165
166
            if (rc <= 0)
167
              break;
168
            else
169
              idx += rc;
170
          } /* for */
171
172
          buf[idx] = '\0';
173
        }
174
175
        lua_push_str_table_entry(vm, rsp_oid, buf);
176
      } break;
177
178
      case ASN_OBJECT_ID: {
179
        char response[128];
180
        int rsp_offset = 0;
181
182
        for (u_int i = 0; i < vp->val_len / 8; i++) {
183
          int rc = snprintf(&response[rsp_offset],
184
                            sizeof(response) - rsp_offset, "%s%d",
185
                            (rsp_offset > 0) ? "." : "", (int)vp->val.objid[i]);
186
187
          if (rc > 0)
188
            rsp_offset += rc;
189
          else
190
            break;
191
        }
192
193
        lua_push_str_table_entry(vm, rsp_oid, response);
194
      } break;
195
196
      case ASN_APPLICATION:
197
      case ASN_NULL:
198
        lua_push_nil_table_entry(vm, rsp_oid);
199
        break;
200
201
      default:
202
        ntop->getTrace()->traceEvent(TRACE_WARNING,
203
                                     "Missing %d type handler [agent: %s]",
204
                                     vp->type, agent_ip);
205
        lua_push_nil_table_entry(vm, rsp_oid);
206
        break;
207
    }
208
209
    vp = vp->next_variable;
210
  } /* while */
211
212
  if (!table_added) lua_pushnil(vm);
213
}
214
215
/* ******************************* */
216
217
int asynch_response(int operation, struct snmp_session *sp, int reqid,
218
                    struct snmp_pdu *pdu, void *magic) {
219
  SNMP *s = (SNMP *)magic;
220
  int rc = 0;
221
222
  switch (operation) {
223
    case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE:
224
      if (pdu->command == SNMP_MSG_RESPONSE) {
225
        sockaddr_in *sa = (sockaddr_in *)pdu->transport_data;
226
        char buf[32], *peer = sp->peername;
227
228
        if (peer == NULL) {
229
          if (sa->sin_family == 2) /* IPv4 */
230
            peer = Utils::intoaV4(ntohl(sa->sin_addr.s_addr), buf, sizeof(buf));
231
          else
232
            ntop->getTrace()->traceEvent(TRACE_WARNING, "Missing IPv6 support");
233
        }
234
235
        s->handle_async_response(pdu, peer), rc = 1;
236
      } else
237
        ntop->getTrace()->traceEvent(TRACE_WARNING, "Unhandled pdu command %d",
238
                                     pdu->command);
239
      break;
240
    case NETSNMP_CALLBACK_OP_TIMED_OUT:
241
      /* Reached when snmp_sess_timeout is called due to a timeout */
242
    default:
243
      break;
244
  }
245
246
  return (rc);
247
}
248
249
/* ******************************* */
250
251
/* See
252
 * https://raw.githubusercontent.com/winlibs/net-snmp/master/snmplib/snmp_client.c
253
 */
254
255
void SNMP::send_snmpv1v2c_request(char *agent_host, char *community,
256
                                  snmp_pdu_primitive pduType, u_int version,
257
                                  char *_oid[SNMP_MAX_NUM_OIDS],
258
                                  bool _batch_mode) {
259
  int rc, pdu_type = SNMP_MSG_GET;
260
  struct snmp_pdu *pdu;
261
  SNMPSession *snmpSession;
262
  bool initSession = false;
263
264
  batch_mode = _batch_mode;
265
266
  if (batch_mode) {
267
  create_snmp_session:
268
    try {
269
      snmpSession = new SNMPSession;
270
      sessions.push_back(snmpSession);
271
      initSession = true;
272
    } catch (std::bad_alloc &ba) {
273
      ntop->getTrace()->traceEvent(TRACE_WARNING,
274
                                   "Unable to allocate SNMP session");
275
      return;
276
    }
277
  } else {
278
    if (sessions.size() == 0) {
279
      goto create_snmp_session;
280
    } else {
281
      snmpSession = sessions.at(0);
282
    }
283
  }
284
285
  /* Initialize the session */
286
  if (initSession) {
287
    snmp_sess_init(&snmpSession->session);
288
    snmpSession->session.peername = agent_host;
289
290
    /* set the SNMP version number */
291
    snmpSession->session.version =
292
        (version == 0) ? SNMP_VERSION_1 : SNMP_VERSION_2c;
293
294
    /* set the SNMP community name used for authentication */
295
    snmpSession->session.community = (u_char *)community;
296
    snmpSession->session.community_len = strlen(community);
297
    snmpSession->session.callback = asynch_response;
298
    snmpSession->session.callback_magic = this;
299
300
    /* Open the session */
301
    snmpSession->session_ptr = snmp_sess_open(&snmpSession->session);
302
  }
303
304
  /* Create the PDU */
305
  switch (pduType) {
306
    case snmp_get_pdu:
307
      pdu_type = SNMP_MSG_GET;
308
      break;
309
    case snmp_get_next_pdu:
310
      pdu_type = SNMP_MSG_GETNEXT;
311
      break;
312
    case snmp_get_bulk_pdu:
313
      pdu_type =
314
          (version == 0 /* SNMPv1 */) ? SNMP_MSG_GETNEXT : SNMP_MSG_GETBULK;
315
      break;
316
    case snmp_set_pdu:
317
      pdu_type = SNMP_MSG_SET;
318
      break;
319
  }
320
321
  if ((pdu = snmp_pdu_create(pdu_type)) == NULL) {
322
    ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP PDU create error");
323
    return;
324
  }
325
326
  if (pdu_type == SNMP_MSG_GETBULK) {
327
    pdu->non_repeaters = 0;    /* GET      */
328
    pdu->max_repetitions = 10; /* GET-NEXT */
329
  }
330
331
  for (u_int i = 0; i < SNMP_MAX_NUM_OIDS; i++) {
332
    if (_oid[i] != NULL) {
333
      size_t name_length = MAX_OID_LEN;
334
      oid name[MAX_OID_LEN];
335
336
      if (snmp_parse_oid(_oid[i], name, &name_length))
337
        snmp_add_null_var(pdu, name, name_length);
338
    } else
339
      break;
340
  }
341
342
  /* Send the request */
343
  if ((rc = snmp_sess_send(snmpSession->session_ptr, pdu)) == 0) {
344
    snmp_free_pdu(pdu);
345
    snmp_perror("snmp_sess_send");
346
    ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP send error [rc: %d]", rc);
347
  }
348
349
  // snmp_free_pdu(pdu); /* TODO: this is apparently freed when we close the
350
  // session */
351
}
352
353
/* ******************************************* */
354
355
void SNMP::send_snmpv3_request(char *agent_host, char *level, char *username,
356
                               char *auth_protocol, char *auth_passphrase,
357
                               char *privacy_protocol, char *privacy_passphrase,
358
                               snmp_pdu_primitive pduType,
359
                               char *oid[SNMP_MAX_NUM_OIDS],
360
                               char value_types[SNMP_MAX_NUM_OIDS],
361
                               char *values[SNMP_MAX_NUM_OIDS],
362
                               bool _batch_mode) {
363
  send_snmp_request(agent_host, 2 /* SNMPv3 */, NULL, level, username,
364
                    auth_protocol, auth_passphrase, privacy_protocol,
365
                    privacy_passphrase, pduType, oid, value_types, values,
366
                    _batch_mode);
367
}
368
369
/* ******************************************* */
370
371
void SNMP::send_snmp_request(char *agent_host, u_int version, char *community,
372
                             char *level, char *username, char *auth_protocol,
373
                             char *auth_passphrase, char *privacy_protocol,
374
                             char *privacy_passphrase,
375
                             snmp_pdu_primitive pduType,
376
                             char *_oid[SNMP_MAX_NUM_OIDS],
377
                             char value_types[SNMP_MAX_NUM_OIDS],
378
                             char *values[SNMP_MAX_NUM_OIDS],
379
                             bool _batch_mode) {
380
  int rc, pdu_type;
381
  struct snmp_pdu *pdu;
382
  SNMPSession *snmpSession;
383
  bool initSession = false;
384
385
  batch_mode = _batch_mode;
386
387
  if (batch_mode) {
388
  create_snmp_session:
389
    try {
390
      snmpSession = new SNMPSession;
391
      sessions.push_back(snmpSession);
392
      initSession = true;
393
    } catch (std::bad_alloc &ba) {
394
      ntop->getTrace()->traceEvent(TRACE_WARNING,
395
                                   "Unable to allocate SNMP session");
396
      return;
397
    }
398
  } else {
399
    if (sessions.size() == 0) {
400
      goto create_snmp_session;
401
    } else {
402
      snmpSession = sessions.at(0);
403
    }
404
  }
405
406
  /* Initialize the session */
407
  if (initSession) {
408
    snmp_sess_init(&snmpSession->session);
409
    snmpSession->session.peername = agent_host;
410
411
    if (version <= 1) {
412
      /* SNMP v1/v2c */
413
      snmpSession->session.version =
414
          (version == 0) ? SNMP_VERSION_1 : SNMP_VERSION_2c;
415
416
      /* set the SNMP community name used for authentication */
417
      snmpSession->session.community = (u_char *)community;
418
      snmpSession->session.community_len = strlen(community);
419
    } else {
420
      /* SNMP v3 */
421
      snmpSession->session.version = SNMP_VERSION_3;
422
423
      if (!strcasecmp(level, "noAuthNoPriv")) {
424
        snmpSession->session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
425
        username = NULL;
426
        auth_protocol = NULL;
427
        privacy_protocol = NULL;
428
      } else {
429
        /* set the SNMPv3 user name */
430
        if (username) {
431
          snmpSession->session.securityName = strdup(username);
432
          snmpSession->session.securityNameLen =
433
              strlen(snmpSession->session.securityName);
434
        } else {
435
          ntop->getTrace()->traceEvent(TRACE_WARNING,
436
                                       "SNMP PDU no username specified");
437
          return;
438
        }
439
440
        if ((!strcasecmp(level, "authNoPriv")) ||
441
            (!strcasecmp(level, "authPriv"))) {
442
          if (!strcasecmp(auth_protocol, "md5")) {
443
            snmpSession->session.securityAuthProto = usmHMACMD5AuthProtocol;
444
            snmpSession->session.securityAuthProtoLen =
445
                sizeof(usmHMACMD5AuthProtocol) / sizeof(oid);
446
            snmpSession->session.securityAuthKeyLen = USM_AUTH_KU_LEN;
447
          } else if (!strcasecmp(auth_protocol, "sha")) {
448
            snmpSession->session.securityAuthProto = usmHMACSHA1AuthProtocol;
449
            snmpSession->session.securityAuthProtoLen =
450
                sizeof(usmHMACSHA1AuthProtocol) / sizeof(oid);
451
            snmpSession->session.securityAuthKeyLen =
452
                USM_AUTH_KU_LEN; /* CHECK */
453
          } else {
454
            ntop->getTrace()->traceEvent(
455
                TRACE_WARNING, "SNMP PDU invalid authentication protocol [%s]",
456
                auth_protocol);
457
            return;
458
          }
459
460
          if (generate_Ku(snmpSession->session.securityAuthProto,
461
                          snmpSession->session.securityAuthProtoLen,
462
                          (u_char *)auth_passphrase, strlen(auth_passphrase),
463
                          snmpSession->session.securityAuthKey,
464
                          &snmpSession->session.securityAuthKeyLen) !=
465
              SNMPERR_SUCCESS) {
466
            ntop->getTrace()->traceEvent(
467
                TRACE_WARNING, "SNMP PDU authentication pass phrase error");
468
            return;
469
          }
470
471
          if (!strcasecmp(level, "authPriv")) {
472
            snmpSession->session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
473
474
#ifndef NETSNMP_DISABLE_DES
475
            if (!strcasecmp(privacy_protocol, "DES")) {
476
              snmpSession->session.securityPrivProto = snmp_duplicate_objid(
477
                  usmDESPrivProtocol, USM_PRIV_PROTO_DES_LEN);
478
              snmpSession->session.securityPrivProtoLen =
479
                  USM_PRIV_PROTO_DES_LEN;
480
            } else
481
#endif
482
                if (!strncasecmp(privacy_protocol, "AES", 3)) {
483
              snmpSession->session.securityPrivProto = snmp_duplicate_objid(
484
                  usmAESPrivProtocol, USM_PRIV_PROTO_AES_LEN);
485
              snmpSession->session.securityPrivProtoLen =
486
                  USM_PRIV_PROTO_AES_LEN;
487
            }
488
489
            snmpSession->session.securityPrivKeyLen = USM_PRIV_KU_LEN;
490
            if (generate_Ku(snmpSession->session.securityAuthProto,
491
                            snmpSession->session.securityAuthProtoLen,
492
                            (u_char *)privacy_passphrase,
493
                            strlen(privacy_passphrase),
494
                            snmpSession->session.securityPrivKey,
495
                            &snmpSession->session.securityPrivKeyLen) !=
496
                SNMPERR_SUCCESS) {
497
              ntop->getTrace()->traceEvent(TRACE_WARNING,
498
                                           "SNMP PDU security privacy error");
499
              return;
500
            }
501
          } else
502
            snmpSession->session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
503
        }
504
      }
505
    }
506
507
    snmpSession->session.callback = asynch_response;
508
    snmpSession->session.callback_magic = this;
509
510
    /* Open the session */
511
    snmpSession->session_ptr = snmp_sess_open(&snmpSession->session);
512
  }
513
514
  /* Create the PDU */
515
  switch (pduType) {
516
    case snmp_get_pdu:
517
      pdu_type = SNMP_MSG_GET;
518
      break;
519
    case snmp_get_next_pdu:
520
      pdu_type = SNMP_MSG_GETNEXT;
521
      break;
522
    case snmp_get_bulk_pdu:
523
      pdu_type =
524
          (version == 0 /* SNMPv1 */) ? SNMP_MSG_GETNEXT : SNMP_MSG_GETBULK;
525
      break;
526
    case snmp_set_pdu:
527
      pdu_type = SNMP_MSG_SET;
528
      if (values == NULL) {
529
        ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP PDU create error");
530
        return;
531
      }
532
      break;
533
    default:
534
      ntop->getTrace()->traceEvent(TRACE_WARNING, "Unknown SNMP PDU type %u",
535
                                   pduType);
536
      pdu_type = SNMP_MSG_GET;
537
      break;
538
  }
539
540
  if ((pdu = snmp_pdu_create(pdu_type)) == NULL) {
541
    ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP PDU create error");
542
    return;
543
  }
544
545
  if (pdu_type == SNMP_MSG_GETBULK) {
546
    pdu->non_repeaters = 0;    /* GET      */
547
    pdu->max_repetitions = 10; /* GET-NEXT */
548
  }
549
550
  for (u_int i = 0; i < SNMP_MAX_NUM_OIDS; i++) {
551
    if (_oid[i] != NULL) {
552
      size_t name_length = MAX_OID_LEN;
553
      oid name[MAX_OID_LEN];
554
555
      if (snmp_parse_oid(_oid[i], name, &name_length)) {
556
        if ((values != NULL) && (values[i] != NULL))
557
          snmp_add_var(pdu, name, name_length, value_types[i], values[i]);
558
        else
559
          snmp_add_null_var(pdu, name, name_length);
560
      }
561
    } else
562
      break;
563
  }
564
565
  /* Send the request */
566
  if ((rc = snmp_sess_send(snmpSession->session_ptr, pdu)) == 0) {
567
    snmp_free_pdu(pdu);
568
    snmp_perror("snmp_sess_send");
569
    ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP send error [rc: %d]", rc);
570
  }
571
572
  // snmp_free_pdu(pdu); /* TODO: this is apparently freed when we close the
573
  // session */
574
}
575
576
/* ******************************************* */
577
578
void SNMP::send_snmp_set_request(char *agent_host, char *community,
579
                                 snmp_pdu_primitive pduType, u_int version,
580
                                 char *_oid[SNMP_MAX_NUM_OIDS],
581
                                 char value_types[SNMP_MAX_NUM_OIDS],
582
                                 char *values[SNMP_MAX_NUM_OIDS]) {
583
  int rc;
584
  struct snmp_pdu *pdu;
585
  SNMPSession *snmpSession;
586
  bool initSession = false;
587
588
  batch_mode = false;
589
590
  if (batch_mode) {
591
  create_snmp_session:
592
    try {
593
      snmpSession = new SNMPSession;
594
      sessions.push_back(snmpSession);
595
      initSession = true;
596
    } catch (std::bad_alloc &ba) {
597
      ntop->getTrace()->traceEvent(TRACE_WARNING,
598
                                   "Unable to allocate SNMP session");
599
      return;
600
    }
601
  } else {
602
    if (sessions.size() == 0) {
603
      goto create_snmp_session;
604
    } else {
605
      snmpSession = sessions.at(0);
606
    }
607
  }
608
609
  /* Initialize the session */
610
  if (initSession) {
611
    snmp_sess_init(&snmpSession->session);
612
    snmpSession->session.peername = agent_host;
613
614
    /* set the SNMP version number */
615
    snmpSession->session.version =
616
        (version == 0) ? SNMP_VERSION_1 : SNMP_VERSION_2c;
617
618
    /* set the SNMP community name used for authentication */
619
    snmpSession->session.community = (u_char *)community;
620
    snmpSession->session.community_len = strlen(community);
621
    snmpSession->session.callback = asynch_response;
622
    snmpSession->session.callback_magic = this;
623
624
    /* Open the session */
625
    snmpSession->session_ptr = snmp_sess_open(&snmpSession->session);
626
  }
627
628
  /* Create the PDU */
629
  if ((pdu = snmp_pdu_create(SNMP_MSG_SET)) == NULL) {
630
    ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP PDU create error");
631
    return;
632
  }
633
634
  for (u_int i = 0; i < SNMP_MAX_NUM_OIDS; i++) {
635
    if (_oid[i] != NULL) {
636
      size_t name_length = MAX_OID_LEN;
637
      oid name[MAX_OID_LEN];
638
639
      if (snmp_parse_oid(_oid[i], name, &name_length))
640
        snmp_add_var(pdu, name, name_length, value_types[i], values[i]);
641
    } else
642
      break;
643
  }
644
645
  /* Send the request */
646
  if ((rc = snmp_sess_send(snmpSession->session_ptr, pdu)) == 0) {
647
    snmp_free_pdu(pdu);
648
    snmp_perror("snmp_sess_send");
649
    ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP send error [rc: %d]", rc);
650
  }
651
}
652
653
/* ******************************************* */
654
655
void SNMP::snmp_fetch_responses(lua_State *_vm, u_int timeout) {
656
  bool add_nil = true;
657
658
  // ntop->getTrace()->traceEvent(TRACE_WARNING, "%s(%u)", __FUNCTION__,
659
  // batch_mode ? 1 : 0);
660
661
  for (unsigned int i = 0; i < sessions.size(); i++) {
662
    int numfds;
663
    fd_set fdset;
664
    struct timeval tvp;
665
    int count, block = 0;
666
    SNMPSession *snmpSession = sessions.at(i);
667
668
    numfds = 0;
669
    FD_ZERO(&fdset);
670
    tvp.tv_sec = timeout, tvp.tv_usec = 0;
671
672
    snmp_sess_select_info(snmpSession->session_ptr, &numfds, &fdset, &tvp,
673
                          &block);
674
675
    /*
676
      Experiments run have shown that:
677
678
      1. `tvp` is altered by `snmp_sess_select_info` into something >= 0
679
      2. `block`
680
          - When there's data, i.e, `count` > 0, then `block` == 0 always
681
          - Then there's  NO data, i.e., `count` == 0 then block == 0 but
682
      timeout is set to zero as well.
683
684
      So select can safely be called when block == 0 to have it non-blocking for
685
      `timeout` == 0. Examples:
686
      https://www.itcodet.com/cpp/cpp-snmp_sess_read-function-examples.html
687
    */
688
689
    if(timeout > 0 /* The caller is willing to wait up to a timeout */
690
       || block == 0 /* The caller doesn't want to wait so the select is only performed when it doesn't block */) {
691
      count = select(numfds, &fdset, NULL, NULL, &tvp);
692
693
      if (count > 0) {
694
        vm = _vm;
695
        snmp_sess_read(snmpSession->session_ptr,
696
                       &fdset); /* Will trigger asynch_response() */
697
698
        /* Add a nil in case no response was pushed in the stack */
699
        if (lua_gettop(vm) > 0) add_nil = false;
700
      } else if (timeout > 0) {
701
        /*
702
          If select(2) times out (that is, it returns zero), snmp_sess_timeout()
703
          should be called to see if a timeout has occurred on the SNMP session.
704
        */
705
        snmp_sess_timeout(snmpSession->session_ptr);
706
      }
707
    }
708
  }
709
710
  if (add_nil) lua_pushnil(_vm);
711
}
712
713
/* ******************************************* */
714
715
int SNMP::snmp_read_response(lua_State *_vm, u_int timeout) {
716
  snmp_fetch_responses(_vm, timeout);
717
  return (CONST_LUA_OK);
718
}
719
720
/* ******************************* */
721
/* ******************************* */
722
723
#else
724
725
/* ******************************* */
726
/* ******************************* */
727
728
/* Self-contained SNMP implementation */
729
730
/* ******************************************* */
731
732
0
int SNMP::snmp_read_response(lua_State *vm, u_int timeout) {
733
0
  int i = 0;
734
735
0
  if (ntop->getGlobals()->isShutdown() ||
736
0
      input_timeout(udp_sock, timeout) == 0) {
737
    /* Timeout or shutdown in progress */
738
0
    lua_pushnil(vm);
739
0
  } else {
740
0
    char buf[BUFLEN];
741
0
    SNMPMessage *message;
742
0
    char *sender_host, *oid_str, *value_str;
743
0
    int sender_port, added = 0, len;
744
745
    /* This receive doesn't block */
746
0
    len =
747
0
        receive_udp_datagram(buf, BUFLEN, udp_sock, &sender_host, &sender_port);
748
0
    message = snmp_parse_message(buf, len);
749
750
0
    i = 0;
751
0
    while (snmp_get_varbind_as_string(message, i, &oid_str, NULL, &value_str)) {
752
0
      if (!added) lua_newtable(vm), added = 1;
753
0
      lua_push_str_table_entry(vm, oid_str, value_str);
754
0
      if (value_str) free(value_str), value_str = NULL;
755
0
      i++;
756
0
    }
757
758
0
    snmp_destroy_message(message);
759
0
    free(message); /* malloc'd by snmp_parse_message */
760
761
0
    if (!added) lua_pushnil(vm);
762
0
  }
763
764
0
  return (CONST_LUA_OK);
765
0
}
766
767
/* ******************************************* */
768
769
void SNMP::send_snmpv1v2c_request(char *agent_host, char *community,
770
                                  snmp_pdu_primitive pduType, u_int version,
771
                                  char *oid[SNMP_MAX_NUM_OIDS],
772
0
                                  bool _batch_mode) {
773
0
  u_int agent_port = 161;
774
0
  int i = 0;
775
0
  SNMPMessage *message;
776
0
  int len;
777
0
  u_char buf[1500];
778
0
  int operation = (pduType == snmp_get_pdu) ? NTOP_SNMP_GET_REQUEST_TYPE
779
0
                                            : NTOP_SNMP_GETNEXT_REQUEST_TYPE;
780
781
0
  batch_mode = _batch_mode;
782
783
0
  if ((message = snmp_create_message())) {
784
0
    snmp_set_version(message, version);
785
0
    snmp_set_community(message, community);
786
0
    snmp_set_pdu_type(message, operation);
787
0
    snmp_set_request_id(message, request_id++);
788
0
    snmp_set_error(message, 0);
789
0
    snmp_set_error_index(message, 0);
790
791
0
    for (i = 0; i < SNMP_MAX_NUM_OIDS; i++) {
792
0
      if (oid[i] != NULL)
793
0
        snmp_add_varbind_null(message, oid[i]);
794
0
      else
795
0
        break;
796
0
    }
797
798
0
    len = snmp_message_length(message);
799
0
    snmp_render_message(message, buf);
800
0
    snmp_destroy_message(message);
801
0
    free(message); /* malloc'd by snmp_create_message */
802
803
0
    send_udp_datagram(buf, len, udp_sock, agent_host, agent_port);
804
0
  }
805
0
}
806
807
/* ******************************************* */
808
809
0
void SNMP::snmp_fetch_responses(lua_State *vm, u_int sec_timeout) {
810
0
  int i = 0;
811
812
0
  if (ntop->getGlobals()->isShutdown() ||
813
0
      input_timeout(udp_sock, sec_timeout) == 0) {
814
    /* Timeout or shutdown in progress */
815
0
  } else {
816
0
    char buf[BUFLEN];
817
0
    SNMPMessage *message;
818
0
    char *sender_host, *oid_str, *value_str = NULL;
819
0
    int sender_port, len;
820
821
0
    len =
822
0
        receive_udp_datagram(buf, BUFLEN, udp_sock, &sender_host, &sender_port);
823
824
0
    if ((message = snmp_parse_message(buf, len))) {
825
0
      bool table_added = false;
826
827
0
      i = 0;
828
829
0
      while (
830
0
          snmp_get_varbind_as_string(message, i, &oid_str, NULL, &value_str)) {
831
0
        if (value_str /* && (value_str[0] != '\0') */) {
832
0
          if (!table_added) lua_newtable(vm), table_added = true;
833
834
0
          if (batch_mode /* Used in batch mode */) {
835
            /*
836
              The key is the IP address as this is used when contacting multiple
837
              hosts so we need to know who has sent back the response
838
            */
839
0
            lua_push_str_table_entry(vm, sender_host /* Sender IP */,
840
0
                                     value_str);
841
0
          } else
842
0
            lua_push_str_table_entry(vm, oid_str, value_str);
843
844
0
          free(value_str),
845
0
              value_str = NULL; /* malloc'd by snmp_get_varbind_as_string */
846
0
        }
847
848
0
        i++;
849
0
      } /* while */
850
851
0
      snmp_destroy_message(message);
852
0
      free(message); /* malloc'd by snmp_parse_message */
853
0
      if (table_added) return;
854
0
    }
855
0
  }
856
857
0
  lua_pushnil(vm);
858
0
}
859
860
/* ******************************************* */
861
862
0
SNMP::SNMP() {
863
0
  char version[4] = {'\0'};
864
865
0
  ntop->getRedis()->get((char *)CONST_RUNTIME_PREFS_SNMP_PROTO_VERSION, version,
866
0
                        sizeof(version));
867
868
0
  if ((udp_sock = Utils::openSocket(AF_INET, SOCK_DGRAM, 0, "SNMP")) < 0)
869
0
    throw("Unable to start network discovery");
870
871
0
  Utils::maximizeSocketBuffer(udp_sock, true /* RX */, 2 /* MB */);
872
0
  snmp_version = atoi(version);
873
0
  if (snmp_version > 1 /* v2c */) snmp_version = 1;
874
0
  request_id = rand();  // Avoid overlaps with coroutines
875
0
}
876
877
/* ******************************************* */
878
879
0
SNMP::~SNMP() {
880
0
  Utils::closeSocket(udp_sock);
881
0
}
882
883
/* ******************************************* */
884
885
#endif /* HAVE_LIBSNMP */
886
887
/* Common code */
888
889
/* ******************************************* */
890
891
0
int SNMP::get(lua_State *vm, bool skip_first_param) {
892
0
  return (snmp_get_fctn(vm, snmp_get_pdu, skip_first_param, false));
893
0
}
894
895
/* ******************************************* */
896
897
0
int SNMP::getnext(lua_State *vm, bool skip_first_param) {
898
0
  return (snmp_get_fctn(vm, snmp_get_next_pdu, skip_first_param, false));
899
0
}
900
901
/* ******************************************* */
902
903
0
int SNMP::getnextbulk(lua_State *vm, bool skip_first_param) {
904
0
  return (snmp_get_fctn(vm,
905
#ifdef HAVE_LIBSNMP
906
                        snmp_get_bulk_pdu /* GET-BULK (next only) */,
907
#else
908
0
                        snmp_get_next_pdu /* GET-NEXT (no bulk) */,
909
0
#endif
910
0
                        skip_first_param, false));
911
0
}
912
913
/* ******************************************* */
914
915
0
int SNMP::set(lua_State *vm, bool skip_first_param) {
916
#ifdef HAVE_LIBSNMP
917
  return (snmp_get_fctn(vm, snmp_set_pdu, skip_first_param, false));
918
#else
919
0
  return (-1);
920
0
#endif
921
0
}
922
923
/* ******************************************* */
924
925
#ifdef HAVE_LIBSNMP
926
int SNMP::snmpv3_get_fctn(lua_State *vm, snmp_pdu_primitive pduType,
927
                          bool skip_first_param, bool _batch_mode) {
928
  char *agent_host, *level, *username, *auth_protocol, *auth_passphrase,
929
      *privacy_protocol, *privacy_passphrase;
930
  u_int timeout = 5, oid_idx = 0, idx = skip_first_param ? 2 : 1;
931
  char *oid[SNMP_MAX_NUM_OIDS] = {NULL},
932
       value_types[SNMP_MAX_NUM_OIDS] = {'\0'},
933
       *values[SNMP_MAX_NUM_OIDS] = {NULL};
934
935
  if (ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK)
936
    return (CONST_LUA_ERROR);
937
  agent_host = (char *)lua_tostring(vm, idx++);
938
939
  if (ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK)
940
    return (CONST_LUA_ERROR);
941
  level = (char *)lua_tostring(vm, idx++);
942
943
  if (ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK)
944
    return (CONST_LUA_ERROR);
945
  username = (char *)lua_tostring(vm, idx++);
946
947
  if (ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK)
948
    return (CONST_LUA_ERROR);
949
  auth_protocol = (char *)lua_tostring(vm, idx++);
950
951
  if (ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK)
952
    return (CONST_LUA_ERROR);
953
  auth_passphrase = (char *)lua_tostring(vm, idx++);
954
955
  if (ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK)
956
    return (CONST_LUA_ERROR);
957
  privacy_protocol = (char *)lua_tostring(vm, idx++);
958
959
  if (ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK)
960
    return (CONST_LUA_ERROR);
961
  privacy_passphrase = (char *)lua_tostring(vm, idx++);
962
963
  if (ntop_lua_check(vm, __FUNCTION__, idx, LUA_TNUMBER) != CONST_LUA_OK)
964
    return (CONST_LUA_ERROR);
965
  timeout = min(timeout, (u_int)lua_tointeger(vm, idx));
966
  idx++;  // Do not out idx++ above as min is a #define and on some platforms it
967
          // will increase idx twice
968
969
  /* Add OIDs */
970
  while ((oid_idx < SNMP_MAX_NUM_OIDS) && (lua_type(vm, idx) == LUA_TSTRING)) {
971
    if (pduType == snmp_set_pdu) {
972
      /* SET */
973
      oid[oid_idx] = (char *)lua_tostring(vm, idx);
974
975
      /* Types
976
         i: INTEGER, u: unsigned INTEGER, t: TIMETICKS, a: IPADDRESS
977
         o: OBJID, s: STRING, x: HEX STRING, d: DECIMAL STRING
978
         U: unsigned int64, I: signed int64, F: float, D: double
979
      */
980
      if (lua_type(vm, idx + 1) != LUA_TSTRING) return (CONST_LUA_ERROR);
981
      value_types[oid_idx] = ((char *)lua_tostring(vm, idx + 1))[0];
982
983
      if (lua_type(vm, idx + 2) != LUA_TSTRING) return (CONST_LUA_ERROR);
984
      values[oid_idx] = (char *)lua_tostring(vm, idx + 2);
985
986
      oid_idx += 3, idx += 3;
987
    } else {
988
      oid[oid_idx++] = (char *)lua_tostring(vm, idx);
989
      idx++;
990
    }
991
  }
992
993
  if (oid_idx == 0) {
994
    /* Missing OIDs */
995
    return (CONST_LUA_ERROR);
996
  }
997
998
  send_snmpv3_request(agent_host, level, username, auth_protocol,
999
                      auth_passphrase, privacy_protocol, privacy_passphrase,
1000
                      pduType, oid, value_types, values, _batch_mode);
1001
1002
  if (skip_first_param)
1003
    return (CONST_LUA_OK); /* This is an async call */
1004
  else
1005
    return (snmp_read_response(vm, timeout));
1006
}
1007
#endif
1008
1009
/* ******************************************* */
1010
1011
int SNMP::snmp_get_fctn(lua_State *vm, snmp_pdu_primitive pduType,
1012
0
                        bool skip_first_param, bool _batch_mode) {
1013
#ifdef HAVE_LIBSNMP
1014
  if (lua_type(vm, skip_first_param ? 5 : 4) != LUA_TNUMBER)
1015
    return (snmpv3_get_fctn(vm, pduType, skip_first_param, _batch_mode));
1016
  else
1017
#endif
1018
0
  {
1019
0
    char *agent_host, *community;
1020
0
    u_int timeout = 5, version = snmp_version, oid_idx = 0,
1021
0
          idx = skip_first_param ? 2 : 1;
1022
0
    char *oid[SNMP_MAX_NUM_OIDS] = {NULL};
1023
#ifdef HAVE_LIBSNMP
1024
    char value_types[SNMP_MAX_NUM_OIDS] = {'\0'},
1025
         *values[SNMP_MAX_NUM_OIDS] = {NULL};
1026
#endif
1027
1028
0
    if (ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK)
1029
0
      return (CONST_LUA_ERROR);
1030
0
    agent_host = (char *)lua_tostring(vm, idx++);
1031
1032
0
    if (ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK)
1033
0
      return (CONST_LUA_ERROR);
1034
0
    community = (char *)lua_tostring(vm, idx++);
1035
1036
0
    if (ntop_lua_check(vm, __FUNCTION__, idx, LUA_TNUMBER) != CONST_LUA_OK)
1037
0
      return (CONST_LUA_ERROR);
1038
0
    timeout = min(timeout, (u_int)lua_tointeger(vm, idx));
1039
0
    idx++;  // Do not out idx++ above as min is a #define and on some platforms
1040
            // it will increase idx twice
1041
1042
0
    if (ntop_lua_check(vm, __FUNCTION__, idx, LUA_TNUMBER) != CONST_LUA_OK)
1043
0
      return (CONST_LUA_ERROR);
1044
0
    version = (u_int)lua_tointeger(vm, idx++);
1045
1046
    /* Add OIDs */
1047
0
    while ((oid_idx < SNMP_MAX_NUM_OIDS) &&
1048
0
           (lua_type(vm, idx) == LUA_TSTRING)) {
1049
0
      if (pduType == snmp_set_pdu) {
1050
        /* SET */
1051
0
        oid[oid_idx] = (char *)lua_tostring(vm, idx);
1052
1053
        /* Types
1054
           i: INTEGER, u: unsigned INTEGER, t: TIMETICKS, a: IPADDRESS
1055
           o: OBJID, s: STRING, x: HEX STRING, d: DECIMAL STRING
1056
           U: unsigned int64, I: signed int64, F: float, D: double
1057
        */
1058
0
        if (lua_type(vm, idx + 1) != LUA_TSTRING) return (CONST_LUA_ERROR);
1059
#ifdef HAVE_LIBSNMP
1060
        value_types[oid_idx] = ((char *)lua_tostring(vm, idx + 1))[0];
1061
#endif
1062
1063
0
        if (lua_type(vm, idx + 2) != LUA_TSTRING) return (CONST_LUA_ERROR);
1064
#ifdef HAVE_LIBSNMP
1065
        values[oid_idx] = (char *)lua_tostring(vm, idx + 2);
1066
#endif
1067
1068
0
        oid_idx += 3, idx += 3;
1069
0
      } else {
1070
0
        oid[oid_idx++] = (char *)lua_tostring(vm, idx);
1071
0
        idx++;
1072
0
      }
1073
0
    }
1074
1075
0
    if (oid_idx == 0) {
1076
      /* Missing OIDs */
1077
0
      return (CONST_LUA_ERROR);
1078
0
    }
1079
1080
0
    if (pduType == snmp_set_pdu) {
1081
      /* SET */
1082
#ifdef HAVE_LIBSNMP
1083
      send_snmp_set_request(agent_host, community, pduType, version, oid,
1084
                            value_types, values);
1085
#else
1086
0
      return (CONST_LUA_ERROR); /* not supported */
1087
0
#endif
1088
0
    } else {
1089
0
      send_snmpv1v2c_request(agent_host, community, pduType, version, oid,
1090
0
                             _batch_mode);
1091
0
    }
1092
1093
0
    if (skip_first_param)
1094
0
      return (CONST_LUA_OK); /* This is an async call */
1095
0
    else
1096
0
      return (snmp_read_response(vm, timeout));
1097
0
  }
1098
0
}