1 /*
2 * Copyright (c) 2008, Stefan Walter
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above
10 * copyright notice, this list of conditions and the
11 * following disclaimer.
12 * * Redistributions in binary form must reproduce the
13 * above copyright notice, this list of conditions and
14 * the following disclaimer in the documentation and/or
15 * other materials provided with the distribution.
16 * * The names of contributors to this software may not be
17 * used to endorse or promote products derived from this
18 * software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 * DAMAGE.
32 *
33 *
34 * CONTRIBUTORS
35 * Stef Walter <stef@memberwebs.com>
36 *
37 */
38
39 #include "usuals.h"
40
41 #include "async-resolver.h"
42 #include "hash.h"
43 #include "log.h"
44 #include "server-mainloop.h"
45 #include "snmp-engine.h"
46
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <assert.h>
50 #include <errno.h>
51 #include <unistd.h>
52 #include <syslog.h>
53 #include <err.h>
54 #include <arpa/inet.h>
55
56 #include <bsnmp/asn1.h>
57 #include <bsnmp/snmp.h>
58 #include <mib/mib-parser.h>
59
60 struct host;
61 struct request;
62
63 typedef uint64_t mstime;
64
65 /* Forward declarations */
66 static void request_release (struct request *req);
67
68 /* ------------------------------------------------------------------------------
69 * HOSTS
70 */
71
72 struct host {
73 /* The hash key is hostname:options:community */
74 char key[128];
75
76 char *hostname;
77 char *portnum;
78 char *community;
79 int version;
80
81 mstime interval;
82
83 /* Host resolving and book keeping */
84 struct sockaddr_storage address;
85 socklen_t address_len;
86 mstime resolve_interval;
87 mstime last_resolve_try;
88 mstime last_resolved;
89 int is_resolved;
90 int is_resolving;
91 int must_resolve;
92
93 /* Requests that are queued of this host */
94 struct request *prepared;
95
96 /* Next in list of hosts */
97 struct host *next;
98 };
99
100 /* All hosts we've allocated */
101 static struct host *host_list = NULL;
102
103 /* Hosts hashed by the host:version:community string */
104 static hsh_t *host_by_key = NULL;
105
106 static void
resolve_cb(int ecode,struct addrinfo * ai,void * arg)107 resolve_cb (int ecode, struct addrinfo *ai, void *arg)
108 {
109 struct host *host = (struct host*)arg;
110 host->is_resolving = 0;
111
112 if (ecode) {
113 log_warnx ("couldn't resolve host name: %s: %s",
114 host->hostname, gai_strerror (ecode));
115 return;
116 }
117
118 if (ai->ai_addrlen > sizeof (host->address)) {
119 log_warnx ("resolved address is too long for host name: %s",
120 host->hostname);
121 return;
122 }
123
124 /* A successful resolve */
125 memcpy (&host->address, ai->ai_addr, ai->ai_addrlen);
126 host->address_len = ai->ai_addrlen;
127 host->last_resolved = server_get_time ();
128 host->is_resolved = 1;
129
130 log_debug ("resolved host: %s", host->hostname);
131 }
132
133 static void
host_resolve(struct host * host,mstime when)134 host_resolve (struct host *host, mstime when)
135 {
136 struct addrinfo hints;
137
138 if (host->is_resolving)
139 return;
140
141 memset (&hints, 0, sizeof (hints));
142 hints.ai_family = PF_UNSPEC;
143 hints.ai_socktype = SOCK_DGRAM;
144 hints.ai_flags = AI_NUMERICSERV;
145
146 /* Automatically strips port number */
147 log_debug ("resolving host: %s", host->hostname);
148 host->last_resolve_try = when;
149 host->is_resolving = 0;
150 async_resolver_queue (host->hostname, host->portnum, &hints, resolve_cb, host);
151 }
152
153 static int
host_resolve_timer(mstime when,void * arg)154 host_resolve_timer (mstime when, void* arg)
155 {
156 struct host *h;
157
158 /* Go through hosts and see which ones need resolving */
159 for (h = host_list; h; h = h->next) {
160
161 /* No need to resolve? */
162 if (!h->must_resolve)
163 continue;
164
165 ASSERT (h->resolve_interval);
166
167 if (when - h->resolve_interval > h->last_resolve_try)
168 host_resolve (h, when);
169
170 /* When the last 3 resolves have failed, set to unresolved */
171 if (h->is_resolved && when - (h->resolve_interval * 3) > h->last_resolved) {
172 log_debug ("host address expired, and was not resolved: %s", h->hostname);
173 h->is_resolved = 0;
174 }
175 }
176
177 return 1;
178 }
179
180 static void
host_update_interval(struct host * host,mstime interval)181 host_update_interval (struct host *host, mstime interval)
182 {
183 mstime resint;
184
185 if (!host->must_resolve)
186 return;
187
188 /* When less than three minutes, resolve once per minute */
189 if (interval <= 180000)
190 resint = 60000;
191
192 /* When between 3 and 10 minutes resolve once per cycle */
193 else if(interval <= 600000)
194 resint = interval;
195
196 /* Otherwise resolve thrice per cycle */
197 else
198 resint = interval / 3;
199
200 /* The lowest interval (since hosts can be shared by pollers) wins */
201 if (!host->resolve_interval || host->resolve_interval > resint) {
202 host->resolve_interval = resint;
203 log_debug ("will resolve host '%s' every %d seconds", host->hostname, resint / 1000);
204 }
205 }
206
207 static struct host*
host_instance(const char * hostname,const char * portnum,const char * community,int version,mstime interval)208 host_instance (const char *hostname, const char *portnum,
209 const char *community, int version, mstime interval)
210 {
211 struct addrinfo hints, *ai;
212 struct host *host;
213 char key[128];
214 int r;
215
216 ASSERT (hostname);
217
218 if (!portnum)
219 portnum = "161";
220
221 /*
222 * Build a lookup key. We can only combine requests for the same
223 * host when the version and community match.
224 */
225 community = community ? community : "public";
226 snprintf (key, sizeof(key), "%s:%d:%s", hostname, version, community);
227 key[sizeof(key) - 1] = 0;
228
229 /* See if we can find an associated host */
230 host = hsh_get (host_by_key, key, -1);
231 if (!host) {
232
233 memset (&hints, 0, sizeof (hints));
234 hints.ai_family = PF_UNSPEC;
235 hints.ai_socktype = SOCK_DGRAM;
236 hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST;
237
238 r = getaddrinfo (hostname, portnum, &hints, &ai);
239
240 /* Ignore error and try to resolve again later */
241 if (r == EAI_NONAME || r == EAI_AGAIN || r == EAI_MEMORY) {
242 ai = NULL;
243
244 /* Real errors */
245 } else if (r != 0) {
246 log_warnx ("couldn't parse host address (ignoring): %s: %s",
247 hostname, gai_strerror (r));
248 return NULL;
249
250 /* Strango address */
251 } else if (ai->ai_addrlen > sizeof (host->address)) {
252 log_warnx ("parsed host address is too big (ignoring): %s", hostname);
253 freeaddrinfo (ai);
254 return NULL;
255 }
256
257 host = calloc (1, sizeof (struct host));
258 if (!host) {
259 log_errorx ("out of memory");
260 if (ai != NULL)
261 freeaddrinfo (ai);
262 return NULL;
263 }
264
265 if (ai != NULL) {
266 memcpy (&host->address, ai->ai_addr, ai->ai_addrlen);
267 host->address_len = ai->ai_addrlen;
268 freeaddrinfo (ai);
269 host->must_resolve = 0;
270 host->is_resolved = 1;
271 } else {
272 host->must_resolve = 1;
273 host->is_resolved = 0;
274 }
275
276 /* And into the hash table */
277 memcpy (&host->key, key, sizeof (host->key));
278 if (!hsh_set (host_by_key, host->key, -1, host)) {
279 log_errorx ("out of memory");
280 free (host);
281 return NULL;
282 }
283
284 /* And add it to the list */
285 host->next = host_list;
286 host_list = host;
287
288 host->version = version;
289 host->hostname = strdup (hostname);
290 host->portnum = strdup (portnum);
291 host->community = strdup (community);
292 host->resolve_interval = 0;
293 host->last_resolved = 0;
294 host->last_resolve_try = 0;
295
296 /* Start the resolving process */
297 if (!host->is_resolved)
298 host_resolve (host, server_get_time ());
299 }
300
301 /* Update the host's resolve interval based on the poll interval requested */
302 host_update_interval (host, interval);
303
304 return host;
305 }
306
307 static void
host_initialize(void)308 host_initialize (void)
309 {
310 /* Initialize stuff if necessary */
311 host_by_key = hsh_create ();
312 if (!host_by_key)
313 err (1, "out of memory");
314
315 /* resolve timer goes once per second */
316 if (server_timer (1000, host_resolve_timer, NULL) == -1)
317 err (1, "couldn't setup resolve timer");
318 }
319
320 static void
host_cleanup(void)321 host_cleanup (void)
322 {
323 struct host *next, *host;
324
325 if (host_by_key)
326 hsh_free (host_by_key);
327 host_by_key = NULL;
328
329 for (host = host_list; host; host = next) {
330 next = host->next;
331 if (host->hostname)
332 free (host->hostname);
333 if (host->portnum)
334 free (host->portnum);
335 if (host->community)
336 free (host->community);
337 if (host->prepared)
338 request_release (host->prepared);
339 free (host);
340 }
341
342 host_list = NULL;
343 }
344
345 /* ------------------------------------------------------------------------------
346 * ASYNC REQUEST PROCESSING
347 */
348
349 #define MAKE_REQUEST_ID(snmp, cb) \
350 ((((snmp) & 0xFFFFFF) << 8) | (cb & 0xFF))
351 #define REQUEST_ID_SNMP(id) \
352 ((id) >> 8)
353 #define REQUEST_ID_CB(id) \
354 ((id) & 0xFF)
355
356 struct request
357 {
358 /* The SNMP request identifier */
359 uint snmp_id;
360
361 mstime next_send; /* Time of the next packet send */
362 mstime last_sent; /* Time last sent */
363 mstime retry_interval; /* How long between retries */
364 mstime when_timeout; /* When this request times out */
365 uint num_sent; /* How many times we've sent */
366
367 struct host *host; /* Host associated with this request */
368
369 /* One callback entry for each binding */
370 struct {
371 snmp_response func;
372 void *arg;
373 } callbacks[SNMP_MAX_BINDINGS];
374
375 /* One flag for each binding */
376 int is_duplicate[SNMP_MAX_BINDINGS];
377
378 int duplicates; /* Total number of duplicate bindings */
379
380 /* The actual request data */
381 struct snmp_pdu pdu;
382 };
383
384 struct socket
385 {
386 int fd; /* The SNMP socket we're communicating on */
387 struct sockaddr_storage addr; /* Local address of this socket */
388 socklen_t addr_len; /* Length of addr */
389 struct socket *next; /* Linked list of socket structures */
390 };
391
392 #define MAX_SNMP_REQUEST_ID 0x800000
393
394 /* The number of SNMP packet retries */
395 static int snmp_retries = 3;
396
397 /* The next request id */
398 static uint snmp_request_id = 1;
399
400 /* The sockets we communicate on */
401 static struct socket *snmp_sockets = NULL;
402
403 /* Since we only deal with one packet at a time, global buffer */
404 static unsigned char snmp_buffer[0x1000];
405
406 /* Hash table of all requests being processed */
407 static hsh_t *snmp_processing = NULL;
408
409 /* Hash table of all requests being prepared */
410 static hsh_t *snmp_preparing = NULL;
411
412 /* A flush of prepared packets is pending */
413 static int snmp_flush_pending = 0;
414
415 static void
request_release_all(hsh_t * hsh_req)416 request_release_all (hsh_t * hsh_req)
417 {
418 struct request *req;
419 hsh_index_t *i;
420 void *val;
421
422 /* Go through all request packets */
423 for (i = hsh_first (hsh_req); i; ) {
424
425 req = hsh_this (i, NULL, NULL);
426 ASSERT (req);
427
428 /* Move to the next, as we delete below */
429 i = hsh_next (i);
430
431 val = hsh_rem (hsh_req, &req->snmp_id, sizeof (req->snmp_id));
432 ASSERT (val == req);
433
434 if (req->host && req->host->prepared == req)
435 req->host->prepared = NULL;
436
437 /* And free the request */
438 request_release (req);
439 }
440 }
441
442 static void
request_release(struct request * req)443 request_release (struct request *req)
444 {
445 /* It should no longer be referred to any of these places */
446 ASSERT (!hsh_get (snmp_preparing, &req->snmp_id, sizeof (req->snmp_id)));
447 ASSERT (!hsh_get (snmp_processing, &req->snmp_id, sizeof (req->snmp_id)));
448
449 snmp_pdu_clear (&req->pdu);
450 free (req);
451 }
452
453 static void
request_send(struct request * req,mstime when)454 request_send (struct request* req, mstime when)
455 {
456 struct socket *sock;
457 struct asn_buf b;
458 ssize_t ret;
459 struct snmp_pdu *pdu, unique_pdu;
460 int i;
461
462 ASSERT (snmp_sockets != NULL);
463
464 /* Update our bookkeeping */
465 req->num_sent++;
466 if (req->num_sent <= snmp_retries)
467 req->next_send = when + req->retry_interval;
468 else
469 req->next_send = 0;
470 req->last_sent = when;
471
472 if (!req->host->is_resolved) {
473 if (req->num_sent <= 1)
474 log_debug ("skipping request #%d for: %s@%s: host not resolved",
475 req->snmp_id, req->host->community, req->host->hostname);
476 return;
477 }
478
479 /* Select a good socket to use, based on address family */
480 for (sock = snmp_sockets; sock; sock = sock->next) {
481 if (sock->addr.ss_family == req->host->address.ss_family)
482 break;
483 }
484
485 if (sock == NULL) {
486 log_warnx ("couldn't send snmp packet to: %s: %s",
487 req->host->hostname, "no local address of relevant protocol family");
488 return;
489 }
490
491 b.asn_ptr = snmp_buffer;
492 b.asn_len = sizeof (snmp_buffer);
493
494 /* Remove any duplicates from the request */
495 pdu = &req->pdu;
496 if (req->duplicates > 0) {
497 memcpy (&unique_pdu, &req->pdu, sizeof (struct snmp_pdu));
498 unique_pdu.nbindings = 0;
499 for (i = 0; i < req->pdu.nbindings; ++i) {
500 if (!req->is_duplicate[i]) {
501 unique_pdu.bindings[unique_pdu.nbindings] = req->pdu.bindings[i];
502 unique_pdu.nbindings++;
503 }
504 }
505 ASSERT (req->pdu.nbindings - req->duplicates == unique_pdu.nbindings);
506 pdu = &unique_pdu;
507 }
508
509 if (snmp_pdu_encode (pdu, &b)) {
510 log_error("couldn't encode snmp buffer");
511 } else {
512 ret = sendto (sock->fd, snmp_buffer, b.asn_ptr - snmp_buffer, 0,
513 (struct sockaddr*)&req->host->address, req->host->address_len);
514 if (ret == -1)
515 log_error ("couldn't send snmp packet to: %s", req->host->hostname);
516 else
517 log_debug ("sent request #%d to: %s", req->snmp_id, req->host->hostname);
518 }
519 }
520
521 static void
request_failure(struct request * req,int code)522 request_failure (struct request *req, int code)
523 {
524 void *val;
525 int j;
526 int snmp_id;
527
528 ASSERT (req);
529 ASSERT (code != 0);
530 ASSERT (hsh_get (snmp_processing, &req->snmp_id, sizeof (req->snmp_id)) == req);
531
532 log_debug ("failed request #%d to '%s' with code %d", req->snmp_id, req->host->hostname, code);
533
534 /* Remember snmp_id in case req is freed by the callback */
535 snmp_id = req->snmp_id;
536
537 /* For each request SNMP value... */
538 for (j = 0; j < req->pdu.nbindings; ++j) {
539
540 if (!req->callbacks[j].func)
541 continue;
542
543 /* ... let callback know */
544 (req->callbacks[j].func) (MAKE_REQUEST_ID (req->snmp_id, j),
545 code, NULL, req->callbacks[j].arg);
546
547 /*
548 * Request could have been freed by the callback, by calling the cancel
549 * function, check and bail if so.
550 */
551 if (hsh_get (snmp_processing, &snmp_id, sizeof (snmp_id)) != req)
552 return;
553 }
554
555 /* Remove from the processing list */
556 val = hsh_rem (snmp_processing, &req->snmp_id, sizeof (req->snmp_id));
557 ASSERT (val == req);
558
559 /* And free the request */
560 request_release (req);
561 }
562
563 static void
request_get_dispatch(struct request * req,struct snmp_pdu * pdu)564 request_get_dispatch (struct request* req, struct snmp_pdu* pdu)
565 {
566 struct snmp_value *pvalue;
567 struct snmp_value *rvalue;
568 int i, j, missed, processed;
569 void *val;
570
571 ASSERT (req);
572 ASSERT (pdu);
573 ASSERT (req->snmp_id == pdu->request_id);
574 ASSERT (pdu->error_status == SNMP_ERR_NOERROR);
575 ASSERT (req->pdu.type == SNMP_PDU_GET);
576 ASSERT (hsh_get (snmp_processing, &req->snmp_id, sizeof (req->snmp_id)) == req);
577
578 /*
579 * For SNMP GET requests we check that the values that came back
580 * were in fact for the same values we requested, and fix any
581 * ordering issues etc. See also request_prep_instance deduplication.
582 */
583 missed = 0;
584 for (j = 0; j < req->pdu.nbindings; ++j) {
585
586 if (!req->callbacks[j].func)
587 continue;
588
589 rvalue = &(req->pdu.bindings[j]);
590 processed = 0;
591
592 /* ... dig out matching value from response */
593 for (i = 0; i < pdu->nbindings; ++i) {
594 pvalue = &(pdu->bindings[i]);
595
596 if (asn_compare_oid (&(rvalue->var), &(pvalue->var)) != 0)
597 continue;
598
599 (req->callbacks[j].func) (MAKE_REQUEST_ID (req->snmp_id, j),
600 SNMP_ERR_NOERROR, pvalue, req->callbacks[j].arg);
601 processed = 1;
602
603 /*
604 * Request could have been freed by the callback, by calling the cancel
605 * function, check and bail if so.
606 */
607 if (hsh_get (snmp_processing, &req->snmp_id, sizeof (req->snmp_id)) != req)
608 return;
609
610 req->callbacks[j].func = NULL;
611 req->callbacks[j].arg = NULL;
612 break;
613 }
614
615 /* Make note that we didn't find a match for at least one binding */
616 if (!processed && !missed) {
617 missed = 1;
618 }
619 }
620
621 /* All done? */
622 if (!missed)
623 log_debug ("request #%d is complete", req->snmp_id);
624
625 if (hsh_get (snmp_processing, &req->snmp_id, sizeof (req->snmp_id)) == req) {
626 val = hsh_rem (snmp_processing, &req->snmp_id, sizeof (req->snmp_id));
627 ASSERT (val == req);
628 request_release (req);
629 }
630 }
631
632 static void
request_other_dispatch(struct request * req,struct snmp_pdu * pdu)633 request_other_dispatch (struct request* req, struct snmp_pdu* pdu)
634 {
635 int snmp_id;
636 void *val;
637
638 ASSERT (req);
639 ASSERT (pdu);
640 ASSERT (req->snmp_id == pdu->request_id);
641 ASSERT (pdu->error_status == SNMP_ERR_NOERROR);
642 ASSERT (req->pdu.type != SNMP_PDU_GET);
643
644 /* Remember snmp_id in case req is freed by the callback */
645 snmp_id = req->snmp_id;
646
647 /*
648 * For requests other than GET we just use the first value
649 * that was sent. See below where we limit to one binding
650 * per SNMP request when other than GET.
651 */
652
653 if (pdu->nbindings == 0) {
654 log_warn ("received response from the server without any values");
655 return;
656 }
657
658 if (pdu->nbindings > 1)
659 log_warn ("received response from the server with extra values");
660
661 /* Shouldn't have sent more than one binding */
662 ASSERT (req->pdu.nbindings == 1);
663
664 if (req->callbacks[0].func)
665 (req->callbacks[0].func) (MAKE_REQUEST_ID (req->snmp_id, 0), SNMP_ERR_NOERROR,
666 &(pdu->bindings[0]), req->callbacks[0].arg);
667
668 log_debug ("request #%d is complete", snmp_id);
669
670 /*
671 * Request could have been freed by the callback, by calling the cancel
672 * function, check and bail if so.
673 */
674 if (hsh_get (snmp_processing, &req->snmp_id, sizeof (req->snmp_id)) != req)
675 return;
676
677 val = hsh_rem (snmp_processing, &req->snmp_id, sizeof (req->snmp_id));
678 ASSERT (val == req);
679 request_release (req);
680 }
681
682 static void
request_response(int fd,int type,void * arg)683 request_response (int fd, int type, void* arg)
684 {
685 struct sockaddr_storage from;
686 char hostname[MAXPATHLEN];
687 struct snmp_pdu pdu;
688 struct asn_buf b;
689 struct request *req;
690 const char *msg;
691 socklen_t from_len;
692 int len, ret;
693 int ip, id;
694
695 /* Read in the packet */
696 from_len = sizeof (from);
697 len = recvfrom (fd, snmp_buffer, sizeof (snmp_buffer), 0,
698 (struct sockaddr*)&from, &from_len);
699 if(len < 0) {
700 if(errno != EAGAIN && errno != EWOULDBLOCK)
701 log_error ("error receiving snmp packet from network");
702 return;
703 }
704
705 if (getnameinfo ((struct sockaddr*)&from, from_len,
706 hostname, sizeof (hostname), NULL, 0,
707 NI_NUMERICHOST) != 0)
708 strcpy (hostname, "[UNKNOWN]");
709
710 /* Now parse the packet */
711
712 b.asn_ptr = snmp_buffer;
713 b.asn_len = len;
714
715 ret = snmp_pdu_decode(&b, &pdu, &ip);
716 if (ret != SNMP_CODE_OK) {
717 log_warnx ("invalid snmp packet received from: %s", hostname);
718 return;
719 }
720
721 /* It needs to match something we're waiting for */
722 id = pdu.request_id;
723 req = hsh_get (snmp_processing, &id, sizeof (id));
724 if(!req) {
725 log_debug ("received extra, cancelled or delayed packet from: %s", hostname);
726 snmp_pdu_clear (&pdu);
727 return;
728 }
729
730 if(pdu.version != req->pdu.version)
731 log_warnx ("wrong version snmp packet from: %s", hostname);
732
733
734 /* Log any errors */
735 if(pdu.error_status == SNMP_ERR_NOERROR) {
736 log_debug ("response to request #%d from: %s", req->snmp_id, hostname);
737
738 if (req->pdu.type == SNMP_PDU_GET)
739 request_get_dispatch (req, &pdu);
740 else
741 request_other_dispatch (req, &pdu);
742
743 } else {
744 msg = snmp_get_errmsg (pdu.error_status);
745 if(msg)
746 log_debug ("failure for request #%d from: %s: %s", req->snmp_id, hostname, msg);
747 else
748 log_debug ("failure for request #%d from: %s: %d", req->snmp_id, hostname,
749 pdu.error_status);
750 request_failure (req, pdu.error_status);
751 }
752
753 snmp_pdu_clear (&pdu);
754 }
755
756 static void
request_process_all(mstime when)757 request_process_all (mstime when)
758 {
759 struct request *req;
760 hsh_index_t *i;
761
762 /* Go through all processing packets */
763 for (i = hsh_first (snmp_processing); i; ) {
764
765 req = hsh_this (i, NULL, NULL);
766 ASSERT (req);
767
768 /* Move to the next, as we may delete below */
769 i = hsh_next (i);
770
771 if (when >= req->when_timeout)
772 request_failure (req, -1);
773 else if (req->next_send && when >= req->next_send)
774 request_send (req, when);
775 }
776 }
777
778 static int
request_resend_timer(mstime when,void * arg)779 request_resend_timer (mstime when, void* arg)
780 {
781 request_process_all (when);
782 return 1;
783 }
784
785 static void
request_flush(struct request * req,mstime when)786 request_flush (struct request *req, mstime when)
787 {
788 void *val;
789
790 ASSERT (req->host->prepared == req);
791
792 val = hsh_rem (snmp_preparing, &req->snmp_id, sizeof (req->snmp_id));
793 ASSERT (val == req);
794
795 /* Don't let us add more onto this request via the host */
796 ASSERT (req->host->prepared == req);
797 req->host->prepared = NULL;
798
799 /* Mark this packet to be sent now */
800 req->next_send = when;
801
802 if (!hsh_set (snmp_processing, &req->snmp_id, sizeof (req->snmp_id), req)) {
803 log_errorx ("out of memory, discarding packets");
804 request_release (req);
805 }
806 }
807
808 static void
request_flush_all(mstime when)809 request_flush_all (mstime when)
810 {
811 struct request *req;
812 hsh_index_t *i;
813
814 /* Transfer everything to the processing table */
815 for (i = hsh_first (snmp_preparing); i; ) {
816 req = hsh_this (i, NULL, NULL);
817
818 /* Do this here, because below removes from table */
819 i = hsh_next (i);
820
821 request_flush (req, when);
822 }
823
824 /* Clear the preparing table */
825 hsh_clear (snmp_preparing);
826
827 /* Process all packets in processing */
828 request_process_all (when);
829 }
830
831
832
833 static int
request_flush_cb(mstime when,void * arg)834 request_flush_cb (mstime when, void *arg)
835 {
836 snmp_flush_pending = 0;
837 request_flush_all (when);
838 return 0; // unrepeated
839 }
840
841 static struct request*
request_prep_instance(struct host * host,mstime interval,mstime timeout,int reqtype,struct asn_oid * oid,int * is_duplicate)842 request_prep_instance (struct host *host, mstime interval, mstime timeout,
843 int reqtype, struct asn_oid *oid, int *is_duplicate)
844 {
845 struct request *req;
846 struct snmp_value *rvalue;
847 int i;
848
849 *is_duplicate = 0;
850
851 /* See if we have one we can piggy back onto */
852 req = host->prepared;
853 if (req) {
854 ASSERT (hsh_get (snmp_preparing, &req->snmp_id, sizeof (req->snmp_id)));
855
856 if (req->pdu.type == SNMP_PDU_GET) {
857 /*
858 * Check whether oid is already waiting in the pdu so we can avoid
859 * asking for it twice in the same request - we know request_get_dispatch
860 * will find the first copy for each callback anyway
861 */
862 for (i = 0; i < req->pdu.nbindings; ++i) {
863 rvalue = &(req->pdu.bindings[i]);
864 if (asn_compare_oid (&(rvalue->var), oid) == 0) {
865 *is_duplicate = 1;
866 return req;
867 }
868 }
869 }
870
871 /* We have one we can piggy back another request onto */
872 if (req->pdu.nbindings < SNMP_MAX_BINDINGS && req->pdu.type == reqtype)
873 return req;
874
875 /* It's too full, so send it off */
876 request_flush (req, server_get_time ());
877 req = NULL;
878 }
879
880 ASSERT (host->prepared == NULL);
881
882 /* Create a new request */
883 req = calloc (1, sizeof (struct request));
884 if (!req) {
885 log_error ("out of memory");
886 return NULL;
887 }
888
889 /* Assign the unique id */
890 req->snmp_id = snmp_request_id;
891 ++snmp_request_id;
892
893 /*
894 * Roll around after a decent amount of ids. Since we're using
895 * signed integers, and these are used in strange ways, we can't
896 * go above 0x800000 or so.
897 */
898 if (snmp_request_id > MAX_SNMP_REQUEST_ID)
899 snmp_request_id = 1;
900
901 /* Mark it down as something we want to prepare */
902 if (!hsh_set (snmp_preparing, &req->snmp_id, sizeof (req->snmp_id), req)) {
903 log_error ("out of memory");
904 free (req);
905 return NULL;
906 }
907
908 /* Setup the packet */
909 strlcpy (req->pdu.community, host->community, sizeof (req->pdu.community));
910 req->pdu.request_id = req->snmp_id;
911 req->pdu.version = host->version;
912 req->pdu.type = reqtype;
913 req->pdu.error_status = 0;
914 req->pdu.error_index = 0;
915 req->pdu.nbindings = 0;
916
917 /* Send interval is 200 ms when poll interval is below 2 seconds */
918 req->retry_interval = (interval <= 2000) ? 200L : 600L;
919
920 /* Timeout is for the last packet sent, not first */
921 req->when_timeout = server_get_time () + (req->retry_interval * ((mstime)snmp_retries)) + timeout;
922 req->num_sent = 0;
923
924 /* Add it to the host */
925 req->host = host;
926 ASSERT (host->prepared == NULL);
927 host->prepared = req;
928
929 log_debug ("preparing request #%d for: %s@%s", req->snmp_id,
930 req->host->community, req->host->hostname);
931
932 return req;
933 }
934
935 int
snmp_engine_request(const char * hostname,const char * port,const char * community,int version,mstime interval,mstime timeout,int reqtype,struct asn_oid * oid,snmp_response func,void * arg)936 snmp_engine_request (const char *hostname, const char *port,
937 const char *community, int version,
938 mstime interval, mstime timeout, int reqtype,
939 struct asn_oid *oid, snmp_response func, void *arg)
940 {
941 struct host *host;
942 struct request *req;
943 int is_duplicate;
944 int callback_id;
945
946 ASSERT (func);
947
948 /* Lookup host for request */
949 host = host_instance (hostname, port, community, version, interval);
950 if (!host)
951 return 0;
952
953 /* Get a request with space or a new request for that host */
954 req = request_prep_instance (host, interval, timeout, reqtype, oid, &is_duplicate);
955 if (!req)
956 return 0;
957
958 if (is_duplicate)
959 ++req->duplicates;
960 ASSERT (req->pdu.nbindings - req->duplicates < SNMP_MAX_BINDINGS);
961
962 /* Add the oid to that request */
963 callback_id = req->pdu.nbindings;
964 req->pdu.bindings[callback_id].var = *oid;
965 req->pdu.bindings[callback_id].syntax = SNMP_SYNTAX_NULL;
966 req->callbacks[callback_id].func = func;
967 req->callbacks[callback_id].arg = arg;
968 req->is_duplicate[callback_id] = is_duplicate;
969 req->pdu.nbindings++;
970
971 /* All other than GET, only get one binding */
972 if (reqtype != SNMP_PDU_GET) {
973 ASSERT (req->pdu.nbindings == 1);
974 request_flush (req, server_get_time ());
975 }
976
977 /* Otherwise flush on the idle callback */
978 else if (!snmp_flush_pending) {
979 server_timer (0, request_flush_cb, NULL);
980 snmp_flush_pending = 1;
981 }
982
983 return MAKE_REQUEST_ID (req->snmp_id, callback_id);
984 }
985
986 void
snmp_engine_remove(int id,const char * during)987 snmp_engine_remove (int id, const char *during)
988 {
989 struct request *req;
990 int snmp_id, callback_id, i;
991
992 ASSERT (id);
993
994 snmp_id = REQUEST_ID_SNMP (id);
995 callback_id = REQUEST_ID_CB (id);
996
997 ASSERT (snmp_id > 0 && snmp_id < MAX_SNMP_REQUEST_ID);
998 ASSERT (callback_id >= 0 && callback_id < SNMP_MAX_BINDINGS);
999
1000 /* Is it being processed or prepared? */
1001 req = hsh_get (snmp_processing, &snmp_id, sizeof (snmp_id));
1002 if (!req) {
1003 req = hsh_get (snmp_preparing, &snmp_id, sizeof (snmp_id));
1004 }
1005
1006 if (!req)
1007 return;
1008
1009 /* Remove this callback from the request */
1010 req->callbacks[callback_id].func = NULL;
1011 req->callbacks[callback_id].arg = NULL;
1012
1013 /* See if any other callbacks exist in the request */
1014 for (i = 0; i < req->pdu.nbindings; ++i) {
1015 if (req->callbacks[i].func)
1016 return;
1017 }
1018
1019 if (during)
1020 log_debug ("cancelling request #%d during %s", snmp_id, during);
1021
1022 hsh_rem (snmp_processing, &snmp_id, sizeof (snmp_id));
1023 hsh_rem (snmp_preparing, &snmp_id, sizeof (snmp_id));
1024
1025 /* If not, free the request */
1026 if (req->host->prepared == req)
1027 req->host->prepared = NULL;
1028 request_release (req);
1029 }
1030
1031 void
snmp_engine_clear(int id)1032 snmp_engine_clear (int id)
1033 {
1034 snmp_engine_remove (id, NULL);
1035 }
1036
1037 void
snmp_engine_cancel(int id)1038 snmp_engine_cancel (int id)
1039 {
1040 struct request *req;
1041 int snmp_id, callback_id;
1042 const char *during;
1043
1044 ASSERT (id);
1045
1046 snmp_id = REQUEST_ID_SNMP (id);
1047 callback_id = REQUEST_ID_CB (id);
1048
1049 ASSERT (snmp_id > 0 && snmp_id < MAX_SNMP_REQUEST_ID);
1050 ASSERT (callback_id >= 0 && callback_id < SNMP_MAX_BINDINGS);
1051
1052 /* Is it being processed? */
1053 req = hsh_get (snmp_processing, &snmp_id, sizeof (snmp_id));
1054 if (req) {
1055 during = "processing";
1056
1057 /* Is it being prepared? */
1058 } else {
1059 req = hsh_get (snmp_preparing, &snmp_id, sizeof (snmp_id));
1060 if (req) {
1061 during = "prep";
1062 ASSERT (req->host->prepared == req);
1063 } else {
1064 during = NULL;
1065 }
1066 }
1067
1068 snmp_engine_remove (id, during);
1069 }
1070
1071 void
snmp_engine_flush(void)1072 snmp_engine_flush (void)
1073 {
1074 request_flush_all (server_get_time ());
1075 }
1076
1077 /* -------------------------------------------------------------------------------
1078 * SYNC REQUESTS
1079 */
1080
1081 struct sync_data {
1082 int valid;
1083 int code;
1084 int id;
1085 struct snmp_value *dest;
1086 };
1087
1088 static void
sync_response(int req,int code,struct snmp_value * value,void * data)1089 sync_response (int req, int code, struct snmp_value *value, void *data)
1090 {
1091 struct sync_data *sync = data;
1092
1093 ASSERT (req == sync->id);
1094
1095 sync->valid = 1;
1096 sync->code = code;
1097 if (value)
1098 snmp_value_copy (sync->dest, value);
1099
1100 server_stop ();
1101 }
1102
1103 int
snmp_engine_sync(const char * host,const char * port,const char * community,int version,uint64_t interval,uint64_t timeout,int reqtype,struct snmp_value * value)1104 snmp_engine_sync (const char* host, const char *port, const char* community,
1105 int version, uint64_t interval, uint64_t timeout, int reqtype,
1106 struct snmp_value *value)
1107 {
1108 struct sync_data sync;
1109
1110 /* Can't run a sync request with the server running */
1111 ASSERT (server_stopped());
1112
1113 sync.valid = 0;
1114 sync.code = 0;
1115 sync.dest = value;
1116
1117 sync.id = snmp_engine_request (host, port, community, version, interval, timeout,
1118 reqtype, &value->var, sync_response, &sync);
1119
1120 if (!sync.id)
1121 return -1;
1122
1123 snmp_engine_flush ();
1124 server_run ();
1125
1126 ASSERT (sync.valid);
1127 return sync.code;
1128 }
1129
1130 /* -----------------------------------------------------------------------------
1131 * INIT
1132 */
1133
1134 void
snmp_engine_init(const char ** bindaddrs,int retries)1135 snmp_engine_init (const char **bindaddrs, int retries)
1136 {
1137 struct addrinfo hints, *ai;
1138 struct socket *sock;
1139 const char **p, *bindaddr;
1140 int fd, r;
1141
1142 ASSERT (bindaddrs);
1143
1144 snmp_retries = retries;
1145
1146 snmp_processing = hsh_create ();
1147 if (!snmp_processing)
1148 err (1, "out of memory");
1149
1150 snmp_preparing = hsh_create ();
1151 if (!snmp_preparing)
1152 err (1, "out of memory");
1153
1154 ASSERT (snmp_sockets == NULL);
1155
1156 for (p = bindaddrs; p && *p; ++p) {
1157 bindaddr = *p;
1158
1159 memset (&hints, 0, sizeof (hints));
1160 hints.ai_flags = AI_PASSIVE;
1161 hints.ai_family = PF_UNSPEC;
1162 hints.ai_socktype = SOCK_DGRAM;
1163 hints.ai_flags = AI_NUMERICSERV;
1164 r = getaddrinfo (bindaddr, "0", &hints, &ai);
1165 if (r != 0)
1166 errx (1, "couldn't resolve bind address '%s': %s", bindaddr, gai_strerror (r));
1167
1168 fd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1169 if (fd < 0) {
1170 if (errno == EPROTONOSUPPORT ||
1171 errno == ENOPROTOOPT ||
1172 errno == ESOCKTNOSUPPORT) {
1173 warn ("couldn't create snmp socket for '%s'", bindaddr);
1174 } else {
1175 err (1, "couldn't open snmp socket");
1176 }
1177 freeaddrinfo (ai);
1178 continue;
1179 }
1180
1181 if (bind (fd, ai->ai_addr, ai->ai_addrlen) < 0)
1182 err (1, "couldn't listen on port '%s'", bindaddr);
1183
1184 if (server_watch (fd, SERVER_READ, request_response, NULL) == -1)
1185 err (1, "couldn't watch port");
1186
1187 /* Stash this socket info */
1188 sock = xcalloc (sizeof (struct socket));
1189 sock->fd = fd;
1190
1191 if (ai->ai_addrlen > sizeof (sock->addr))
1192 errx (1, "resolve address is too big");
1193 memcpy (&sock->addr, ai->ai_addr, ai->ai_addrlen);
1194
1195 /* Push onto the linked list */
1196 sock->next = snmp_sockets;
1197 snmp_sockets = sock;
1198
1199 freeaddrinfo (ai);
1200 }
1201
1202 if (snmp_sockets == NULL)
1203 errx (1, "no local addresses to listen on");
1204
1205 /* We fire off the resend timer every 1/5 second */
1206 if (server_timer (200, request_resend_timer, NULL) == -1)
1207 err(1, "couldn't setup timer");
1208
1209 host_initialize ();
1210 }
1211
1212 void
snmp_engine_stop(void)1213 snmp_engine_stop (void)
1214 {
1215 struct socket *sock;
1216
1217 while (snmp_sockets != NULL) {
1218 /* Pop off the list */
1219 sock = snmp_sockets;
1220 snmp_sockets = sock->next;
1221
1222 /* And destroy */
1223 server_unwatch (sock->fd);
1224 close (sock->fd);
1225 free (sock);
1226 }
1227
1228 /*
1229 * Release all requests before freeing the either hash, request_release,
1230 * and thus request_release_all, expect both hashes to still exist.
1231 */
1232 if (snmp_preparing)
1233 request_release_all (snmp_preparing);
1234 if (snmp_processing)
1235 request_release_all (snmp_processing);
1236
1237 /* Now we can safely free both hashes. */
1238 if (snmp_preparing)
1239 hsh_free (snmp_preparing);
1240 snmp_preparing = NULL;
1241
1242 if (snmp_processing)
1243 hsh_free (snmp_processing);
1244 snmp_processing = NULL;
1245
1246 host_cleanup ();
1247 }
1248
1249 int
snmp_engine_match(const struct snmp_value * value,const char * text)1250 snmp_engine_match (const struct snmp_value *value, const char *text)
1251 {
1252 char *end;
1253
1254 ASSERT (value);
1255 ASSERT (text);
1256
1257 switch (value->syntax) {
1258
1259 /* Empty string */
1260 case SNMP_SYNTAX_NULL:
1261 case SNMP_SYNTAX_NOSUCHOBJECT:
1262 case SNMP_SYNTAX_NOSUCHINSTANCE:
1263 case SNMP_SYNTAX_ENDOFMIBVIEW:
1264 return *text == '\0';
1265
1266 /* Integer value */
1267 case SNMP_SYNTAX_INTEGER:
1268 {
1269 int num = strtoll (text, &end, 0);
1270 if (*end != '\0')
1271 return 0;
1272 return num == value->v.integer;
1273 }
1274
1275 /* String of bytes */
1276 case SNMP_SYNTAX_OCTETSTRING:
1277 {
1278 int len = strlen (text);
1279 if (value->v.octetstring.len != len)
1280 return 0;
1281 return memcmp (value->v.octetstring.octets, text, len) == 0;
1282 }
1283
1284
1285 case SNMP_SYNTAX_OID:
1286 {
1287 struct asn_oid oid;
1288 if (mib_parse (text, &oid) < 0)
1289 return 0;
1290 return asn_compare_oid (&oid, &value->v.oid) == 0;
1291 }
1292
1293 case SNMP_SYNTAX_IPADDRESS:
1294 {
1295 struct in_addr addr;
1296 if (!inet_aton (text, &addr))
1297 return 0;
1298 return memcmp (&addr, value->v.ipaddress, 4) == 0;
1299 }
1300
1301 case SNMP_SYNTAX_COUNTER:
1302 case SNMP_SYNTAX_GAUGE:
1303 case SNMP_SYNTAX_TIMETICKS:
1304 {
1305 uint64_t sub = strtoull (text, &end, 0);
1306 if (*end != '\0' || sub > 0xffffffff)
1307 return 0;
1308 return sub == value->v.uint32;
1309 }
1310
1311 case SNMP_SYNTAX_COUNTER64:
1312 {
1313 uint64_t sub = strtoull (text, &end, 0);
1314 if (*end != '\0' || sub > 0xffffffff)
1315 return 0;
1316 return sub == value->v.counter64;
1317 }
1318
1319 default:
1320 return 0;
1321 };
1322 }
1323