From 0af1b01b5ca61422c779b40a9c99cd0b29b40049 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Sat, 24 Oct 2009 14:10:41 +0000 Subject: Support for multiple local sockets (ie: INET + INET6) * Allows us to make ipv6 queries. --- common/compat.c | 12 ++++++ common/compat.h | 4 ++ common/snmp-engine.c | 109 ++++++++++++++++++++++++++++++++++++--------------- common/snmp-engine.h | 2 +- daemon/rrdbotd.c | 24 ++++++++++-- tools/rrdbot-get.c | 23 ++++++++++- 6 files changed, 136 insertions(+), 38 deletions(-) diff --git a/common/compat.c b/common/compat.c index eda00d0..1d94f1e 100644 --- a/common/compat.c +++ b/common/compat.c @@ -235,6 +235,18 @@ atexitv(voidfunc func, void* data) #endif /* HAVE_ATEXITV */ +#ifndef HAVE_XREALLOC + +void* +xrealloc(void *p, size_t size) +{ + register void* value = realloc(p, size); + if(value == NULL && size) + errx(1, "out of memory"); + return value; +} + +#endif /* HAVE_XREALLOC */ #ifndef HAVE_XCALLOC diff --git a/common/compat.h b/common/compat.h index 8eda20f..e19d1b1 100644 --- a/common/compat.h +++ b/common/compat.h @@ -73,6 +73,10 @@ int strtob(const char* str); void atexitv(void (*func)(void*), void* data); #endif +#ifndef HAVE_XREALLOC +void* xrealloc(void *p, size_t size); +#endif + #ifndef HAVE_XCALLOC void* xcalloc(size_t size); #endif diff --git a/common/snmp-engine.c b/common/snmp-engine.c index 945418e..baf0f0d 100644 --- a/common/snmp-engine.c +++ b/common/snmp-engine.c @@ -378,14 +378,22 @@ struct request struct snmp_pdu pdu; }; +struct socket +{ + int fd; /* The SNMP socket we're communicating on */ + struct sockaddr_storage addr; /* Local address of this socket */ + socklen_t addr_len; /* Length of addr */ + struct socket *next; /* Linked list of socket structures */ +}; + /* The number of SNMP packet retries */ static int snmp_retries = 3; /* The last request id */ static uint snmp_request_id = 1; -/* The SNMP socket we're communicating on */ -static int snmp_socket = -1; +/* The sockets we communicate on */ +static struct socket *snmp_sockets = NULL; /* Since we only deal with one packet at a time, global buffer */ static unsigned char snmp_buffer[0x1000]; @@ -413,10 +421,11 @@ request_release (struct request *req) static void request_send (struct request* req, mstime when) { + struct socket* sock; struct asn_buf b; ssize_t ret; - ASSERT (snmp_socket != -1); + assert (snmp_sockets != NULL); /* Update our bookkeeping */ req->num_sent++; @@ -433,13 +442,25 @@ request_send (struct request* req, mstime when) return; } + /* Select a good socket to use, based on address family */ + for (sock = snmp_sockets; sock; sock = sock->next) { + if (sock->addr.ss_family == req->host->address.ss_family) + break; + } + + if (sock == NULL) { + log_warnx ("couldn't send snmp packet to: %s: %s", + req->host->hostname, "no local address of relevant protocol family"); + return; + } + b.asn_ptr = snmp_buffer; b.asn_len = sizeof (snmp_buffer); if (snmp_pdu_encode (&req->pdu, &b)) { log_error("couldn't encode snmp buffer"); } else { - ret = sendto (snmp_socket, snmp_buffer, b.asn_ptr - snmp_buffer, 0, + ret = sendto (sock->fd, snmp_buffer, b.asn_ptr - snmp_buffer, 0, (struct sockaddr*)&req->host->address, req->host->address_len); if (ret == -1) log_error ("couldn't send snmp packet to: %s", req->host->hostname); @@ -605,11 +626,9 @@ request_response (int fd, int type, void* arg) int len, ret; int ip, id; - ASSERT (snmp_socket == fd); - /* Read in the packet */ from_len = sizeof (from); - len = recvfrom (snmp_socket, snmp_buffer, sizeof (snmp_buffer), 0, + len = recvfrom (fd, snmp_buffer, sizeof (snmp_buffer), 0, (struct sockaddr*)&from, &from_len); if(len < 0) { if(errno != EAGAIN && errno != EWOULDBLOCK) @@ -985,10 +1004,14 @@ snmp_engine_sync (const char* host, const char *port, const char* community, */ void -snmp_engine_init (const char *bindaddr, int retries) +snmp_engine_init (const char **bindaddrs, int retries) { struct addrinfo hints, *ai; - int r; + struct socket *sock; + const char **p, *bindaddr; + int fd, r; + + assert (bindaddrs); snmp_retries = retries; @@ -1000,29 +1023,44 @@ snmp_engine_init (const char *bindaddr, int retries) if (!snmp_preparing) err (1, "out of memory"); - if (!bindaddr) - bindaddr = "0.0.0.0"; - memset (&hints, 0, sizeof (hints)); - hints.ai_flags = AI_PASSIVE; - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_flags = AI_NUMERICSERV; - r = getaddrinfo (bindaddr, "0", &hints, &ai); - if (r != 0) - errx (1, "couldn't resolve bind address: %s: %s", bindaddr, gai_strerror (r)); + assert (snmp_sockets == NULL); + + for (p = bindaddrs; p && *p; ++p) { + bindaddr = *p; + + memset (&hints, 0, sizeof (hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_NUMERICSERV; + r = getaddrinfo (bindaddr, "0", &hints, &ai); + if (r != 0) + errx (1, "couldn't resolve bind address: %s: %s", bindaddr, gai_strerror (r)); - ASSERT (snmp_socket == -1); - snmp_socket = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (snmp_socket < 0) - err (1, "couldn't open snmp socket"); + fd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd < 0) + err (1, "couldn't open snmp socket"); - if (bind (snmp_socket, ai->ai_addr, ai->ai_addrlen) < 0) - err (1, "couldn't listen on port"); + if (bind (fd, ai->ai_addr, ai->ai_addrlen) < 0) + err (1, "couldn't listen on port"); - freeaddrinfo (ai); + if (server_watch (fd, SERVER_READ, request_response, NULL) == -1) + err (1, "couldn't listen on socket"); - if (server_watch (snmp_socket, SERVER_READ, request_response, NULL) == -1) - err (1, "couldn't listen on socket"); + /* Stash this socket info */ + sock = xcalloc (sizeof (struct socket)); + sock->fd = fd; + + if (ai->ai_addrlen > sizeof (sock->addr)) + errx (1, "resolve address is too big"); + memcpy (&sock->addr, ai->ai_addr, ai->ai_addrlen); + + /* Push onto the linked list */ + sock->next = snmp_sockets; + snmp_sockets = sock; + + freeaddrinfo (ai); + } /* We fire off the resend timer every 1/5 second */ if (server_timer (200, request_resend_timer, NULL) == -1) @@ -1034,10 +1072,17 @@ snmp_engine_init (const char *bindaddr, int retries) void snmp_engine_stop (void) { - if (snmp_socket != -1) { - server_unwatch (snmp_socket); - close (snmp_socket); - snmp_socket = -1; + struct socket *sock; + + while (snmp_sockets != NULL) { + /* Pop off the list */ + sock = snmp_sockets; + snmp_sockets = sock->next; + + /* And destroy */ + server_unwatch (sock->fd); + close (sock->fd); + free (sock); } host_cleanup (); diff --git a/common/snmp-engine.h b/common/snmp-engine.h index 59c733d..d3d353c 100644 --- a/common/snmp-engine.h +++ b/common/snmp-engine.h @@ -6,7 +6,7 @@ typedef void (*snmp_response) (int request, int code, struct snmp_value *value, void *data); -void snmp_engine_init (const char *bind_address, int retries); +void snmp_engine_init (const char **bind_addresses, int retries); int snmp_engine_request (const char* host, const char *port, const char* community, int version, uint64_t interval, uint64_t timeout, int reqtype, diff --git a/daemon/rrdbotd.c b/daemon/rrdbotd.c index 573eb5f..f2ecd52 100644 --- a/daemon/rrdbotd.c +++ b/daemon/rrdbotd.c @@ -204,8 +204,9 @@ removepid(const char* pidfile) int main(int argc, char* argv[]) { + const char** local = NULL; + int n_local = 0; const char* pidfile = NULL; - const char *bind_address = NULL; int daemonize = 1; char ch; char* t; @@ -231,7 +232,9 @@ main(int argc, char* argv[]) /* Bind address */ case 'b': - bind_address = optarg; + local = xrealloc (local, sizeof (char*) * (n_local + 2)); + local[n_local] = optarg; + local[++n_local] = NULL; break; /* Config directory */ @@ -301,6 +304,17 @@ main(int argc, char* argv[]) if(argc != 0) usage(); + /* No bind addresses specified, use defaults... */ + if (local == NULL) { + local = xrealloc (local, sizeof (char*) * 3); + local[0] = "0.0.0.0"; + local[1] = NULL; +#ifdef HAVE_INET6 + local[1] = "::"; + local[2] = NULL; +#endif + } + /* The mainloop server */ server_init(); @@ -311,9 +325,13 @@ main(int argc, char* argv[]) mib_uninit(); /* Rev up the main engine */ - snmp_engine_init(bind_address, 3); + snmp_engine_init (local, 3); rb_poll_engine_init(); + free (local); + n_local = 0; + local = NULL; + if(daemonize) { /* Fork a daemon nicely */ diff --git a/tools/rrdbot-get.c b/tools/rrdbot-get.c index 46219d9..27c8b75 100644 --- a/tools/rrdbot-get.c +++ b/tools/rrdbot-get.c @@ -426,6 +426,8 @@ int main (int argc, char* argv[]) { char *bind_address = NULL; + const char **local = NULL; + int n_local = 0; char ch; char* t; @@ -460,7 +462,9 @@ main (int argc, char* argv[]) /* local source address */ case 's': - bind_address = optarg; + local = xrealloc (local, sizeof (char*) * (n_local + 2)); + local[n_local] = optarg; + local[++n_local] = NULL; break; /* The timeout */ @@ -495,8 +499,23 @@ main (int argc, char* argv[]) if(argc != 1) usage (); + /* No bind addresses specified, use defaults... */ + if (local == NULL) { + local = xrealloc (local, sizeof (char*) * 3); + local[0] = "0.0.0.0"; + local[1] = NULL; +#ifdef HAVE_INET6 + local[1] = "::"; + local[2] = NULL; +#endif + } + server_init (); - snmp_engine_init (bind_address, MAX_RETRIES); + snmp_engine_init (local, MAX_RETRIES); + + free (local); + n_local = 0; + local = NULL; parse_argument (argv[0]); -- cgit v1.2.3