summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/hostated/check_http.c216
-rw-r--r--usr.sbin/hostated/check_icmp.c377
-rw-r--r--usr.sbin/hostated/check_tcp.c140
-rw-r--r--usr.sbin/hostated/hce.c115
-rw-r--r--usr.sbin/hostated/hostated.conf.516
-rw-r--r--usr.sbin/hostated/hostated.h43
-rw-r--r--usr.sbin/hostated/parse.y6
-rw-r--r--usr.sbin/hoststated/check_http.c216
-rw-r--r--usr.sbin/hoststated/check_icmp.c377
-rw-r--r--usr.sbin/hoststated/check_tcp.c140
-rw-r--r--usr.sbin/hoststated/hce.c115
-rw-r--r--usr.sbin/hoststated/hoststated.conf.516
-rw-r--r--usr.sbin/hoststated/hoststated.h43
-rw-r--r--usr.sbin/hoststated/parse.y6
-rw-r--r--usr.sbin/relayd/check_icmp.c377
-rw-r--r--usr.sbin/relayd/check_tcp.c140
-rw-r--r--usr.sbin/relayd/hce.c115
-rw-r--r--usr.sbin/relayd/parse.y6
-rw-r--r--usr.sbin/relayd/relayd.conf.516
-rw-r--r--usr.sbin/relayd/relayd.h43
20 files changed, 1697 insertions, 826 deletions
diff --git a/usr.sbin/hostated/check_http.c b/usr.sbin/hostated/check_http.c
index 5dc759e4c42..3529764375b 100644
--- a/usr.sbin/hostated/check_http.c
+++ b/usr.sbin/hostated/check_http.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: check_http.c,v 1.2 2006/12/16 12:42:14 reyk Exp $ */
+/* $OpenBSD: check_http.c,v 1.3 2006/12/25 18:12:14 reyk Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
*
@@ -31,114 +31,164 @@
#include "hostated.h"
-struct buf *http_request(struct host *, struct table *, int, const char *);
+void check_http_code(struct ctl_tcp_event *);
+void check_http_digest(struct ctl_tcp_event *);
+void http_read(int, short, void *);
-struct buf *
-http_request(struct host *host, struct table *table, int s, const char *req)
+void
+check_http_code(struct ctl_tcp_event *cte)
{
- int fl;
- ssize_t sz;
- char rbuf[1024];
- struct buf *buf;
-
- if ((fl = fcntl(s, F_GETFL, 0)) == -1)
- fatal("http_request: cannot get flags for socket");
- if (fcntl(s, F_SETFL, fl & ~(O_NONBLOCK)) == -1)
- fatal("http_request: cannot set blocking socket");
- if ((buf = buf_dynamic(sizeof(rbuf), UINT_MAX)) == NULL)
- fatalx("http_request: cannot create dynamic buffer");
-
- if (write(s, req, strlen(req)) != (ssize_t) strlen(req)) {
- close(s);
- return (NULL);
- }
- for (; (sz = read(s, rbuf, sizeof(rbuf))) != 0; ) {
- if (sz == -1)
- fatal("http_request: read");
- if (buf_add(buf, rbuf, sz) == -1)
- fatal("http_request: buf_add");
- }
- return (buf);
-}
-
-int
-check_http_code(struct host *host, struct table *table)
-{
- int s;
- int code;
- char scode[4];
- char *req;
char *head;
+ char scode[4];
const char *estr;
- struct buf *buf;
-
- if ((s = tcp_connect(host, table)) <= 0)
- return (s);
-
- asprintf(&req, "HEAD %s HTTP/1.0\r\n\r\n", table->path);
- if ((buf = http_request(host, table, s, req)) == NULL)
- return (HOST_UNKNOWN);
- free(req);
+ int code;
- head = buf->buf;
+ head = cte->buf->buf;
if (strncmp(head, "HTTP/1.1 ", strlen("HTTP/1.1 ")) &&
strncmp(head, "HTTP/1.0 ", strlen("HTTP/1.0 "))) {
log_debug("check_http_code: cannot parse HTTP version");
- close(s);
- return (HOST_DOWN);
+ cte->host->up = HOST_DOWN;
+ return;
}
head += strlen("HTTP/1.1 ");
- if (strlen(head) < 5) /* code + \r\n */
- return (HOST_DOWN);
+ if (strlen(head) < 5) /* code + \r\n */ {
+ cte->host->up = HOST_DOWN;
+ return;
+ }
strlcpy(scode, head, sizeof(scode));
code = strtonum(scode, 100, 999, &estr);
if (estr != NULL) {
log_debug("check_http_code: cannot parse HTTP code");
- close(s);
- return (HOST_DOWN);
+ cte->host->up = HOST_DOWN;
+ return;
}
- if (code != table->retcode) {
+ if (code != cte->table->retcode) {
log_debug("check_http_code: invalid HTTP code returned");
- close(s);
- return (HOST_DOWN);
- }
- close(s);
- return (HOST_UP);
+ cte->host->up = HOST_DOWN;
+ } else
+ cte->host->up = HOST_UP;
}
-int
-check_http_digest(struct host *host, struct table *table)
+void
+check_http_digest(struct ctl_tcp_event *cte)
{
- int s;
- char *head;
- char *req;
- struct buf *buf;
- char digest[(SHA1_DIGEST_LENGTH*2)+1];
-
- if ((s = tcp_connect(host, table)) <= 0)
- return (s);
+ char *head;
+ char digest[(SHA1_DIGEST_LENGTH*2)+1];
- asprintf(&req, "GET %s HTTP/1.0\r\n\r\n", table->path);
- if ((buf = http_request(host, table, s, req)) == NULL)
- return (HOST_UNKNOWN);
- free(req);
-
- head = buf->buf;
+ head = cte->buf->buf;
if ((head = strstr(head, "\r\n\r\n")) == NULL) {
log_debug("check_http_digest: host %u no end of headers",
- host->id);
- close(s);
- return (HOST_DOWN);
+ cte->host->id);
+ cte->host->up = HOST_DOWN;
+ return;
}
head += strlen("\r\n\r\n");
SHA1Data(head, strlen(head), digest);
- close(s);
- buf_free(buf);
- if (strcmp(table->digest, digest)) {
+ if (strcmp(cte->table->digest, digest)) {
log_warnx("check_http_digest: wrong digest for host %u",
- host->id);
- return (HOST_DOWN);
+ cte->host->id);
+ cte->host->up = HOST_DOWN;
+ } else
+ cte->host->up = HOST_UP;
+}
+
+void
+http_read(int s, short event, void *arg)
+{
+ ssize_t br;
+ char rbuf[SMALL_READ_BUF_SIZE];
+ struct timeval tv;
+ struct timeval tv_now;
+ struct ctl_tcp_event *cte = arg;
+
+ if (event == EV_TIMEOUT) {
+ cte->host->up = HOST_DOWN;
+ buf_free(cte->buf);
+ hce_notify_done(cte->host, "http_read: timeout");
+ return;
+ }
+ br = read(s, rbuf, sizeof(rbuf));
+ if (br == 0) {
+ cte->host->up = HOST_DOWN;
+ switch (cte->table->check) {
+ case CHECK_HTTP_CODE:
+ check_http_code(cte);
+ break;
+ case CHECK_HTTP_DIGEST:
+ check_http_digest(cte);
+ break;
+ default:
+ fatalx("http_read: unhandled check type");
+ }
+ buf_free(cte->buf);
+ hce_notify_done(cte->host, "http_read: connection closed");
+ } else if (br == -1) {
+ cte->host->up = HOST_DOWN;
+ buf_free(cte->buf);
+ hce_notify_done(cte->host, "http_read: read failed");
+ } else {
+ buf_add(cte->buf, rbuf, br);
+ tv.tv_sec = cte->table->timeout / 1000;
+ tv.tv_usec = cte->table->timeout % 1000;
+ if (gettimeofday(&tv_now, NULL))
+ fatal("send_http_request: gettimeofday");
+ timersub(&tv_now, &cte->tv_start, &tv_now);
+ timersub(&tv, &tv_now, &tv);
+ event_once(s, EV_READ|EV_TIMEOUT, http_read, cte, &tv);
}
- return (HOST_UP);
+}
+
+void
+send_http_request(struct ctl_tcp_event *cte)
+{
+ int bs;
+ int pos;
+ int len;
+ char *req;
+ struct timeval tv;
+ struct timeval tv_now;
+
+ switch (cte->table->check) {
+ case CHECK_HTTP_CODE:
+ asprintf(&req, "HEAD %s HTTP/1.0\r\n\r\n",
+ cte->table->path);
+ break;
+ case CHECK_HTTP_DIGEST:
+ asprintf(&req, "GET %s HTTP/1.0\r\n\r\n",
+ cte->table->path);
+ break;
+ default:
+ fatalx("send_http_request: unhandled check type");
+ }
+ if (req == NULL)
+ fatal("out of memory");
+ pos = 0;
+ len = strlen(req);
+ /*
+ * write all at once for now.
+ */
+ do {
+ bs = write(cte->s, req + pos, len);
+ if (bs <= 0) {
+ log_warnx("send_http_request: cannot send request");
+ cte->host->up = HOST_DOWN;
+ hce_notify_done(cte->host, "send_http_request: write");
+ free(req);
+ return;
+ }
+ pos += bs;
+ len -= bs;
+ } while (len > 0);
+ free(req);
+ if ((cte->buf = buf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL)
+ fatalx("send_http_request: cannot create dynamic buffer");
+
+ tv.tv_sec = cte->table->timeout / 1000;
+ tv.tv_usec = cte->table->timeout % 1000;
+ if (gettimeofday(&tv_now, NULL))
+ fatal("send_http_request: gettimeofday");
+ timersub(&tv_now, &cte->tv_start, &tv_now);
+ timersub(&tv, &tv_now, &tv);
+ event_once(cte->s, EV_READ|EV_TIMEOUT, http_read, cte, &tv);
}
diff --git a/usr.sbin/hostated/check_icmp.c b/usr.sbin/hostated/check_icmp.c
index 4d93a652345..ad562e771c4 100644
--- a/usr.sbin/hostated/check_icmp.c
+++ b/usr.sbin/hostated/check_icmp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: check_icmp.c,v 1.2 2006/12/16 11:59:12 reyk Exp $ */
+/* $OpenBSD: check_icmp.c,v 1.3 2006/12/25 18:12:14 reyk Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -32,151 +32,306 @@
#include <errno.h>
#include <unistd.h>
#include <string.h>
+#include <stdlib.h>
#include "hostated.h"
-int check_icmp6(struct host *, int, int);
-int check_icmp4(struct host *, int, int);
-int in_cksum(u_short *, int);
+int icmp6_checks_done(struct ctl_icmp_event *);
+int icmp4_checks_done(struct ctl_icmp_event *);
+void send_icmp6(struct ctl_icmp_event *, struct host *);
+void send_icmp4(struct ctl_icmp_event *, struct host *);
+void recv_icmp6(int, short, void *);
+void recv_icmp4(int, short, void *);
+int in_cksum(u_short *, int);
-int check_icmp(struct host *host, int s, int s6, int timeout)
+void
+schedule_icmp(struct ctl_icmp_event *cie, struct table *table)
{
- if (host->ss.ss_family == AF_INET)
- return (check_icmp4(host, s, timeout));
- else
- return (check_icmp6(host, s6, timeout));
+ struct host *host;
+
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (host->flags & F_DISABLE)
+ continue;
+ host->last_up = host->up;
+ host->flags &= ~F_CHECK_DONE;
+ if (((struct sockaddr *)&host->ss)->sa_family == AF_INET) {
+ send_icmp4(cie, host);
+ } else {
+ send_icmp6(cie, host);
+ }
+ }
+}
+
+void
+check_icmp(struct ctl_icmp_event *cie)
+{
+ struct timeval tv;
+
+ if (gettimeofday(&cie->tv_start, NULL))
+ fatal("check_icmp: gettimeofday");
+ if (cie->has_icmp4) {
+ tv.tv_sec = cie->env->timeout / 1000;
+ tv.tv_usec = cie->env->timeout % 1000;
+ event_once(cie->icmp_sock, EV_READ|EV_TIMEOUT,
+ recv_icmp4, cie, &tv);
+ }
+ if (cie->has_icmp6) {
+ tv.tv_sec = cie->env->timeout / 1000;
+ tv.tv_usec = cie->env->timeout % 1000;
+ event_once(cie->icmp6_sock, EV_READ|EV_TIMEOUT,
+ recv_icmp6, cie, &tv);
+ }
}
-int check_icmp6(struct host *host, int s, int timeout)
+int
+icmp6_checks_done(struct ctl_icmp_event *cie)
+{
+ struct table *table;
+ struct host *host;
+
+ TAILQ_FOREACH(table, &cie->env->tables, entry) {
+ if (table->flags & F_DISABLE || table->check != CHECK_ICMP)
+ continue;
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (((struct sockaddr *)&host->ss)->sa_family !=
+ AF_INET6)
+ continue;
+ if (!(host->flags & F_CHECK_DONE))
+ return (0);
+ }
+ }
+ return (1);
+}
+
+int
+icmp4_checks_done(struct ctl_icmp_event *cie)
+{
+ struct table *table;
+ struct host *host;
+
+ TAILQ_FOREACH(table, &cie->env->tables, entry) {
+ if (table->flags & F_DISABLE || table->check != CHECK_ICMP)
+ continue;
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (((struct sockaddr *)&host->ss)->sa_family !=
+ AF_INET)
+ continue;
+ if (!(host->flags & F_CHECK_DONE)) {
+ return (0);
+ }
+ }
+ }
+ return (1);
+}
+
+void
+send_icmp6(struct ctl_icmp_event *cie, struct host *host)
{
struct sockaddr *to;
struct icmp6_hdr *icp;
- int ident;
ssize_t i;
- int cc;
int datalen = (64 - 8);
u_char packet[datalen];
- fd_set fdset;
- socklen_t len;
- struct timeval tv;
+ cie->has_icmp6 = 1;
to = (struct sockaddr *)&host->ss;
- ident = getpid() & 0xFFFF;
- len = sizeof(struct sockaddr_in6);
-
bzero(&packet, sizeof(packet));
icp = (struct icmp6_hdr *)packet;
icp->icmp6_type = ICMP6_ECHO_REQUEST;
icp->icmp6_code = 0;
icp->icmp6_seq = 1;
- icp->icmp6_id = ident;
-
- memset((packet + sizeof(*icp)), 'X', datalen);
- cc = datalen + 8;
-
- i = sendto(s, packet, cc, 0, to, len);
-
- if (i < 0 || i != cc) {
- log_warn("check_icmp6: cannot send ping");
- return (HOST_UNKNOWN);
- }
-
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = timeout % 1000;
- FD_ZERO(&fdset);
- FD_SET(s, &fdset);
- switch (select(s + 1, &fdset, NULL, NULL, &tv)) {
- case -1:
- if (errno == EINTR) {
- log_warnx("check_icmp6: interrupted");
- return (HOST_UNKNOWN);
- } else
- fatal("check_icmp6: select");
- case 0:
- log_debug("check_icmp6: timeout");
- return (HOST_DOWN);
- default:
- bzero(&packet, sizeof(packet));
- i = recvfrom(s, packet, cc, 0, to, &len);
- if (i < 0 || i != cc) {
- log_warn("check_icmp6: did not receive valid ping");
- return (HOST_DOWN);
- }
- icp = (struct icmp6_hdr *)(packet);
- if (icp->icmp6_id != ident) {
- log_warnx("check_icmp6: did not receive valid ident");
- return (HOST_DOWN);
- }
- break;
+ icp->icmp6_id = getpid() & 0xffff;
+
+ memcpy((packet + sizeof(*icp)), &host->id, sizeof(host->id));
+
+ i = sendto(cie->icmp6_sock, packet, datalen + 8, 0, to,
+ sizeof(struct sockaddr_in6));
+ if (i < 0 || i != datalen + 8) {
+ host->up = HOST_DOWN;
+ hce_notify_done(host, "send_icmp6: cannot send");
+ return;
}
- return (HOST_UP);
}
-int check_icmp4(struct host *host, int s, int timeout)
+void
+send_icmp4(struct ctl_icmp_event *cie, struct host *host)
{
- struct sockaddr *to;
- struct icmp *icp;
- int ident;
- ssize_t i;
- int cc;
- int datalen = (64 - 8);
- u_char packet[datalen];
- fd_set fdset;
- socklen_t len;
- struct timeval tv;
+ struct sockaddr *to;
+ struct icmp *icp;
+ ssize_t i;
+ int datalen = (64 - 8);
+ u_char packet[datalen];
+ cie->has_icmp4 = 1;
to = (struct sockaddr *)&host->ss;
- ident = getpid() & 0xFFFF;
- len = sizeof(struct sockaddr_in);
-
bzero(&packet, sizeof(packet));
icp = (struct icmp *)packet;
- icp->icmp_type = htons(ICMP_ECHO);
+ icp->icmp_type = ICMP_ECHO;
icp->icmp_code = 0;
icp->icmp_seq = htons(1);
- icp->icmp_id = htons(ident);
+ icp->icmp_id = htons(getpid() & 0xffff);
icp->icmp_cksum = 0;
- memset(icp->icmp_data, 'X', datalen);
- cc = datalen + 8;
- icp->icmp_cksum = in_cksum((u_short *)icp, cc);
-
- i = sendto(s, packet, cc, 0, to, len);
-
- if (i < 0 || i != cc) {
- log_warn("check_icmp4: cannot send ping");
- return (HOST_UNKNOWN);
- }
-
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = timeout % 1000;
- FD_ZERO(&fdset);
- FD_SET(s, &fdset);
- switch (select(s + 1, &fdset, NULL, NULL, &tv)) {
- case -1:
- if (errno == EINTR) {
- log_warnx("check_icmp4: ping interrupted");
- return (HOST_UNKNOWN);
- } else
- fatal("check_icmp4: select");
- case 0:
- log_debug("check_icmp4: timeout");
- return (HOST_DOWN);
- default:
- bzero(&packet, sizeof(packet));
- i = recvfrom(s, packet, cc, 0, to, &len);
- if (i < 0 || i != cc) {
- log_warn("check_icmp4: did not receive valid ping");
- return (HOST_DOWN);
+ memcpy(icp->icmp_data, &host->id, sizeof(host->id));
+ icp->icmp_cksum = in_cksum((u_short *)icp, datalen + 8);
+
+ i = sendto(cie->icmp_sock, packet, datalen + 8, 0, to,
+ sizeof(struct sockaddr_in));
+ if (i < 0 || i != datalen + 8) {
+ host->up = HOST_DOWN;
+ hce_notify_done(host, "send_icmp4: cannot send");
+ }
+}
+
+void
+recv_icmp6(int s, short event, void *arg)
+{
+ struct ctl_icmp_event *cie = arg;
+ int datalen = (64 - 8);
+ u_char packet[datalen];
+ socklen_t len;
+ struct sockaddr_storage ss;
+ struct icmp6_hdr *icp;
+ struct host *host;
+ struct table *table;
+ ssize_t i;
+ objid_t id;
+ struct timeval tv;
+ struct timeval tv_now;
+
+ if (event == EV_TIMEOUT) {
+ /*
+ * mark all hosts which have not responded yet as down.
+ */
+ TAILQ_FOREACH(table, &cie->env->tables, entry) {
+ if (table->check != CHECK_ICMP ||
+ table->flags & F_DISABLE)
+ continue;
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (host->flags & F_DISABLE)
+ continue;
+ if (((struct sockaddr *)&host->ss)->sa_family
+ != AF_INET6)
+ continue;
+ if (!(host->flags & F_CHECK_DONE)) {
+ host->up = HOST_DOWN;
+ }
+ }
}
- icp = (struct icmp *)(packet + sizeof(struct ip));
- if (ntohs(icp->icmp_id) != ident) {
- log_warnx("check_icmp4: did not receive valid ident");
- return (HOST_DOWN);
+ return;
+ }
+ bzero(&packet, sizeof(packet));
+ bzero(&ss, sizeof(ss));
+ len = sizeof(struct sockaddr_in6);
+ i = recvfrom(s, packet, datalen + 8, 0, (struct sockaddr *)&ss, &len);
+ if (i < 0 || i != datalen + 8) {
+ log_warn("recv_icmp6: did not receive valid ping");
+ return;
+ }
+ icp = (struct icmp6_hdr *)(packet);
+ memcpy(&id, (packet + sizeof(*icp)), sizeof(id));
+ host = host_find(cie->env, id);
+ if (host == NULL)
+ log_warn("recv_icmp6: ping for unknown host received");
+ if (bcmp(&ss, &host->ss, len)) {
+ log_warnx("recv_icmp6: forged icmp packet ?");
+ return;
+ }
+ if (icp->icmp6_id != (getpid() & 0xffff)) {
+ log_warnx("recv_icmp6: did not receive valid ident");
+ host->up = HOST_DOWN;
+ } else
+ host->up = HOST_UP;
+ hce_notify_done(host, "recv_icmp6: final");
+ if (icmp6_checks_done(cie))
+ return;
+ if (gettimeofday(&tv_now, NULL))
+ fatal("recv_icmp6: gettimeofday");
+ tv.tv_sec = cie->env->timeout / 1000;
+ tv.tv_usec = cie->env->timeout % 1000;
+ timersub(&tv_now, &cie->tv_start, &tv_now);
+ timersub(&tv, &tv_now, &tv);
+ event_once(cie->icmp6_sock, EV_READ|EV_TIMEOUT, recv_icmp6, cie, &tv);
+}
+
+void
+recv_icmp4(int s, short event, void *arg)
+{
+ int datalen = (64 - 8);
+ socklen_t len;
+ struct icmp *icp;
+ struct ctl_icmp_event *cie = arg;
+ u_char packet[datalen];
+ struct host *host;
+ struct table *table;
+ ssize_t i;
+ objid_t id;
+ struct timeval tv;
+ struct timeval tv_now;
+ struct sockaddr_storage ss;
+
+ if (event == EV_TIMEOUT) {
+ /*
+ * mark all hosts which have not responded yet as down.
+ */
+ TAILQ_FOREACH(table, &cie->env->tables, entry) {
+ if (table->check != CHECK_ICMP ||
+ table->flags & F_DISABLE)
+ continue;
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (host->flags & F_DISABLE)
+ continue;
+ if (((struct sockaddr *)&host->ss)->sa_family
+ != AF_INET)
+ continue;
+ if (!(host->flags & F_CHECK_DONE)) {
+ host->up = HOST_DOWN;
+ }
+ }
}
- break;
+ return;
+ }
+
+ len = sizeof(struct sockaddr_in);
+ bzero(&packet, sizeof(packet));
+ bzero(&ss, sizeof(ss));
+ i = recvfrom(s, packet, datalen + 8, 0, (struct sockaddr *)&ss, &len);
+ if (i < 0 || i != (datalen + 8)) {
+ log_warn("recv_icmp4: did not receive valid ping");
+ return;
+ }
+
+ icp = (struct icmp *)(packet + sizeof(struct ip));
+ memcpy(&id, icp->icmp_data, sizeof(id));
+ host = host_find(cie->env, id);
+ if (host == NULL) {
+ log_warnx("recv_icmp4: received ping for unknown host");
+ return;
+ }
+ if (bcmp(&ss, &host->ss, len)) {
+ log_warnx("recv_icmp4: forged icmp packet ?");
+ return;
+ }
+ if (ntohs(icp->icmp_id) != (getpid() & 0xffff)) {
+ log_warnx("recv_icmp4: did not receive valid ident");
+ host->up = HOST_DOWN;
+ } else
+ host->up = HOST_UP;
+
+ host->flags |= F_CHECK_DONE;
+ if (icmp4_checks_done(cie)) {
+ hce_notify_done(host, "recv_icmp4: all done");
+ return;
}
- return (HOST_UP);
+ hce_notify_done(host, "recv_icmp4: host");
+
+ if (gettimeofday(&tv_now, NULL))
+ fatal("recv_icmp4: gettimeofday");
+ tv.tv_sec = cie->env->timeout / 1000;
+ tv.tv_usec = cie->env->timeout % 1000;
+ timersub(&tv_now, &cie->tv_start, &tv_now);
+ timersub(&tv, &tv_now, &tv);
+ event_once(cie->icmp_sock, EV_READ|EV_TIMEOUT, recv_icmp4, cie, &tv);
}
/* in_cksum from ping.c --
diff --git a/usr.sbin/hostated/check_tcp.c b/usr.sbin/hostated/check_tcp.c
index a0390326a7a..2a65abc4307 100644
--- a/usr.sbin/hostated/check_tcp.c
+++ b/usr.sbin/hostated/check_tcp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: check_tcp.c,v 1.2 2006/12/16 12:42:14 reyk Exp $ */
+/* $OpenBSD: check_tcp.c,v 1.3 2006/12/25 18:12:14 reyk Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -27,84 +27,108 @@
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
+#include <stdlib.h>
#include <errno.h>
#include "hostated.h"
-int
-check_tcp(struct host *host, struct table *table)
-{
- int sock;
-
- if ((sock = tcp_connect(host, table)) <= 0)
- return (sock);
- close(sock);
- return (HOST_UP);
-}
+void tcp_write(int, short, void *);
+void tcp_host_up(int s, struct ctl_tcp_event *);
-int
-tcp_connect(struct host *host, struct table *table)
+void
+check_tcp(struct ctl_tcp_event *cte)
{
- int s;
- socklen_t len;
- struct timeval tv;
- struct sockaddr sa;
- fd_set fdset;
+ int s;
+ int type;
+ socklen_t len;
+ struct timeval tv;
+ struct linger lng;
- switch (host->ss.ss_family) {
+ switch (cte->host->ss.ss_family) {
case AF_INET:
- ((struct sockaddr_in *)&host->ss)->sin_port =
- htons(table->port);
+ ((struct sockaddr_in *)&cte->host->ss)->sin_port =
+ htons(cte->table->port);
break;
case AF_INET6:
- ((struct sockaddr_in6 *)&host->ss)->sin6_port =
- htons(table->port);
+ ((struct sockaddr_in6 *)&cte->host->ss)->sin6_port =
+ htons(cte->table->port);
break;
}
- len = ((struct sockaddr *)&host->ss)->sa_len;
+ len = ((struct sockaddr *)&cte->host->ss)->sa_len;
+
+ if ((s = socket(cte->host->ss.ss_family, SOCK_STREAM, 0)) == -1)
+ goto bad;
- if ((s = socket(host->ss.ss_family, SOCK_STREAM, 0)) == -1)
- fatal("check_tcp: cannot create socket");
+ bzero(&lng, sizeof(lng));
+ if (setsockopt(s, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1)
+ goto bad;
+
+ type = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &type, sizeof(type)) == -1)
+ goto bad;
if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
- fatal("check_tcp: cannot set non blocking socket");
+ goto bad;
- if (connect(s, (struct sockaddr *)&host->ss, len) == -1) {
- if (errno != EINPROGRESS && errno != EWOULDBLOCK) {
- close(s);
- return (HOST_DOWN);
- }
- } else
- return (s);
+ if (connect(s, (struct sockaddr *)&cte->host->ss, len) == -1) {
+ if (errno != EINPROGRESS)
+ goto bad;
+ } else {
+ cte->host->up = HOST_UP;
+ tcp_host_up(s, cte);
+ return;
+ }
+ tv.tv_sec = cte->table->timeout / 1000;
+ tv.tv_usec = cte->table->timeout % 1000;
+ event_once(s, EV_TIMEOUT|EV_WRITE, tcp_write, cte, &tv);
+ return;
+bad:
+ close(s);
+ cte->host->up = HOST_DOWN;
+ hce_notify_done(cte->host, "check_tcp: cannot connect");
+}
- tv.tv_sec = table->timeout / 1000;
- tv.tv_usec = table->timeout % 1000;
- FD_ZERO(&fdset);
- FD_SET(s, &fdset);
+void
+tcp_write(int s, short event, void *arg)
+{
+ struct ctl_tcp_event *cte = arg;
+ int err;
+ socklen_t len;
- /* XXX This needs to be rewritten */
- switch (select(s + 1, NULL, &fdset, NULL, &tv)) {
- case -1:
- if (errno != EINTR)
- fatal("check_tcp: select");
+ if (event == EV_TIMEOUT) {
+ log_debug("tcp_write: connect timed out");
+ cte->host->up = HOST_DOWN;
+ } else {
+ len = sizeof(err);
+ if (getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len))
+ fatal("tcp_write: getsockopt");
+ if (err)
+ cte->host->up = HOST_DOWN;
else
- return (HOST_UNKNOWN);
- case 0:
+ cte->host->up = HOST_UP;
+ }
+ if (cte->host->up == HOST_UP)
+ tcp_host_up(s, cte);
+ else {
+ close(s);
+ hce_notify_done(cte->host, "connect failed");
+ }
+}
+
+void
+tcp_host_up(int s, struct ctl_tcp_event *cte)
+{
+ switch (cte->table->check) {
+ case CHECK_TCP:
close(s);
- return (HOST_DOWN);
+ hce_notify_done(cte->host, "tcp_write: success");
+ break;
+ case CHECK_HTTP_CODE:
+ case CHECK_HTTP_DIGEST:
+ send_http_request(cte);
+ break;
default:
- if (getpeername(s, &sa, &len) == -1) {
- if (errno == ENOTCONN) {
- close(s);
- return (HOST_DOWN);
- } else {
- log_debug("check_tcp: unknown peername");
- close(s);
- return (HOST_UNKNOWN);
- }
- } else
- return (s);
+ fatalx("tcp_write: unhandled check type");
}
- return (HOST_UNKNOWN);
}
diff --git a/usr.sbin/hostated/hce.c b/usr.sbin/hostated/hce.c
index c3d7c69eba0..6b291036e17 100644
--- a/usr.sbin/hostated/hce.c
+++ b/usr.sbin/hostated/hce.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: hce.c,v 1.3 2006/12/16 17:48:27 deraadt Exp $ */
+/* $OpenBSD: hce.c,v 1.4 2006/12/25 18:12:14 reyk Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -43,8 +43,9 @@ void hce_shutdown(void);
void hce_dispatch_imsg(int, short, void *);
void hce_dispatch_parent(int, short, void *);
void hce_launch_checks(int, short, void *);
+int hce_checks_done(void);
-static struct hostated *env = NULL;
+static struct hostated *env = NULL;
struct imsgbuf *ibuf_pfe;
struct imsgbuf *ibuf_main;
@@ -103,6 +104,10 @@ hce(struct hostated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2],
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
fatal("hce: can't drop privileges");
+ env->cie.icmp_sock = env->icmp_sock;
+ env->cie.icmp6_sock = env->icmp6_sock;
+ env->cie.env = env;
+
event_init();
signal_set(&ev_sigint, SIGINT, hce_sig_handler, NULL);
@@ -148,51 +153,89 @@ hce(struct hostated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2],
void
hce_launch_checks(int fd, short event, void *arg)
{
- int previous_up;
struct host *host;
struct table *table;
- struct ctl_status st;
- struct timeval tv;
- tv.tv_sec = env->interval;
- tv.tv_usec = 0;
- evtimer_add(&env->ev, &tv);
- bzero(&st, sizeof(st));
+ TAILQ_FOREACH(table, &env->tables, entry) {
+ if (table->flags & F_DISABLE)
+ continue;
+ if (table->check == CHECK_NOCHECK)
+ fatalx("hce_launch_checks: unknown check type");
+ if (table->check == CHECK_ICMP) {
+ schedule_icmp(&env->cie, table);
+ continue;
+ }
+ /*
+ * tcp type checks follow
+ */
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (host->flags & F_DISABLE)
+ continue;
+ bzero(&host->cte, sizeof(host->cte));
+ host->last_up = host->up;
+ host->cte.host = host;
+ host->cte.table = table;
+ if (gettimeofday(&host->cte.tv_start, NULL))
+ fatal("hce_launch_checks: gettimeofday");
+ check_tcp(&host->cte);
+ }
+ }
+ check_icmp(&env->cie);
+}
+
+int
+hce_checks_done()
+{
+ struct table *table;
+ struct host *host;
+
TAILQ_FOREACH(table, &env->tables, entry) {
if (table->flags & F_DISABLE)
continue;
TAILQ_FOREACH(host, &table->hosts, entry) {
if (host->flags & F_DISABLE)
continue;
- previous_up = host->up;
- switch (table->check) {
- case CHECK_ICMP:
- host->up = check_icmp(host, env->icmp_sock,
- env->icmp6_sock, table->timeout);
- break;
- case CHECK_TCP:
- host->up = check_tcp(host, table);
- break;
- case CHECK_HTTP_CODE:
- host->up = check_http_code(host, table);
- break;
- case CHECK_HTTP_DIGEST:
- host->up = check_http_digest(host, table);
- break;
- default:
- fatalx("hce_launch_checks: unknown check type");
- break;
- }
- if (host->up != previous_up) {
- st.id = host->id;
- st.up = host->up;
- imsg_compose(ibuf_pfe, IMSG_HOST_STATUS, 0, 0,
- &st, sizeof(st));
- }
+ if (!(host->flags & F_CHECK_DONE))
+ return (0);
+ }
+ }
+ return (1);
+}
+
+void
+hce_notify_done(struct host *host, const char *msg)
+{
+ struct ctl_status st;
+ struct timeval tv;
+ struct table *table;
+
+ st.id = host->id;
+ st.up = host->up;
+ host->flags |= F_CHECK_DONE;
+ if (msg)
+ log_debug("hce_notify_done: %s", msg);
+ if (host->up != host->last_up) {
+ imsg_compose(ibuf_pfe, IMSG_HOST_STATUS, 0, 0, &st, sizeof(st));
+ host->last_up = host->up;
+ }
+ /*
+ * check if everything is done, I see no other way than going
+ * through the tree for every host that calls this function.
+ */
+ if (hce_checks_done()) {
+ /*
+ * notify pfe checks are done and schedule next check
+ */
+ imsg_compose(ibuf_pfe, IMSG_SYNC, 0, 0, NULL, 0);
+ TAILQ_FOREACH(table, &env->tables, entry) {
+ TAILQ_FOREACH(host, &table->hosts, entry)
+ host->flags &= ~F_CHECK_DONE;
}
+ tv.tv_sec = env->interval;
+ tv.tv_usec = 0;
+ evtimer_add(&env->ev, &tv);
+ bzero(&st, sizeof(st));
}
- /* tell pfe we're finished */
- imsg_compose(ibuf_pfe, IMSG_SYNC, 0, 0, NULL, 0);
}
void
diff --git a/usr.sbin/hostated/hostated.conf.5 b/usr.sbin/hostated/hostated.conf.5
index 54fe3dda883..136671bc76e 100644
--- a/usr.sbin/hostated/hostated.conf.5
+++ b/usr.sbin/hostated/hostated.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: hostated.conf.5,v 1.5 2006/12/19 14:39:30 jmc Exp $
+.\" $OpenBSD: hostated.conf.5,v 1.6 2006/12/25 18:12:14 reyk Exp $
.\"
.\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
.\"
@@ -68,10 +68,17 @@ table webhosts {
}
.Ed
.Sh GLOBAL CONFIGURATION
-Only one global setting can be set.
+Here are the settings that can be set globally:
.Pp
.Bl -tag -width Ds -compact
.It Xo
+.Ic timeout Ar number
+.Xc
+Set the global timeout for checks.
+This can be overriden by the timeout value in the table definitions
+and is 200 milliseconds by default.
+.Pp
+.It Xo
.Ic interval Ar number
.Xc
Set the interval in seconds at which the hosts will be checked.
@@ -125,8 +132,9 @@ to hosts.
This parameter is mandatory.
Main and backup tables need to have the same real port.
.It Ic timeout Ar number
-Set the timeout in milliseconds for each host that is checked.
-The default timeout is 200 milliseconds.
+Set the timeout in milliseconds for each host that is checked using
+TCP as the transport.
+This will override the global timeout, which is 200 milliseconds by default.
.El
.Sh SERVICES
Services represent a
diff --git a/usr.sbin/hostated/hostated.h b/usr.sbin/hostated/hostated.h
index 2187d838a3f..4ca1519bae2 100644
--- a/usr.sbin/hostated/hostated.h
+++ b/usr.sbin/hostated/hostated.h
@@ -20,7 +20,7 @@
#define PF_SOCKET "/dev/pf"
#define HOSTATED_USER "_hostated"
#define HOSTATED_ANCHOR "hostated"
-#define CONNECT_TIMEOUT 200
+#define CHECK_TIMEOUT 200
#define CHECK_INTERVAL 10
#define EMPTY_TABLE UINT_MAX
#define EMPTY_ID UINT_MAX
@@ -30,7 +30,8 @@
#define MAX_NAME_SIZE 64
#define SRV_MAX_VIRTS 16
-#define READ_BUF_SIZE 65535
+#define SMALL_READ_BUF_SIZE 1024
+#define READ_BUF_SIZE 65535
/* buffer */
struct buf {
@@ -120,6 +121,25 @@ struct ctl_id {
char name[MAX_NAME_SIZE];
};
+struct ctl_icmp_event {
+ struct hostated *env;
+ int icmp_sock;
+ int icmp6_sock;
+ int has_icmp4;
+ int has_icmp6;
+ int last_up;
+ struct event ev;
+ struct timeval tv_start;
+};
+
+struct ctl_tcp_event {
+ int s;
+ struct buf *buf;
+ struct host *host;
+ struct table *table;
+ struct timeval tv_start;
+};
+
struct address {
struct sockaddr_storage ss;
in_port_t port;
@@ -130,8 +150,9 @@ TAILQ_HEAD(addresslist, address);
#define F_DISABLE 0x01
#define F_BACKUP 0x02
+#define F_CHECK_DONE 0x02 /* reused for host */
#define F_USED 0x04
-#define F_ACTIVE_RULESET 0x04
+#define F_ACTIVE_RULESET 0x04 /* reused for service */
#define F_DOWN 0x08
#define F_ADD 0x10
#define F_DEL 0x20
@@ -144,7 +165,9 @@ struct host {
char *tablename;
char name[MAXHOSTNAMELEN];
int up;
+ int last_up;
struct sockaddr_storage ss;
+ struct ctl_tcp_event cte;
TAILQ_ENTRY(host) entry;
};
TAILQ_HEAD(hostlist, host);
@@ -203,10 +226,12 @@ struct hostated {
int icmp6_sock;
int tablecount;
int servicecount;
+ int timeout;
struct table empty_table;
struct event ev;
struct tablelist tables;
struct servicelist services;
+ struct ctl_icmp_event cie;
};
#define HOSTATED_OPT_VERBOSE 0x01
@@ -299,17 +324,17 @@ void flush_rulesets(struct hostated *);
/* hce.c */
pid_t hce(struct hostated *, int [2], int [2], int [2]);
+void hce_notify_done(struct host *, const char *);
/* check_icmp.c */
-int check_icmp(struct host *, int, int, int);
+void schedule_icmp(struct ctl_icmp_event *, struct table *);
+void check_icmp(struct ctl_icmp_event *);
/* check_tcp.c */
-int check_tcp(struct host *, struct table *);
-int tcp_connect(struct host *, struct table *);
+void check_tcp(struct ctl_tcp_event *);
-/* check_tcp.c */
-int check_http_code(struct host *, struct table *);
-int check_http_digest(struct host *, struct table *);
+/* check_http.c */
+void send_http_request(struct ctl_tcp_event *);
/* hostated.c */
struct host *host_find(struct hostated *, objid_t);
diff --git a/usr.sbin/hostated/parse.y b/usr.sbin/hostated/parse.y
index b6ce1f815bb..0c6008365b8 100644
--- a/usr.sbin/hostated/parse.y
+++ b/usr.sbin/hostated/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.4 2006/12/16 18:05:35 martin Exp $ */
+/* $OpenBSD: parse.y,v 1.5 2006/12/25 18:12:14 reyk Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -140,6 +140,7 @@ varset : STRING '=' STRING {
;
main : INTERVAL number { conf->interval = $2; }
+ | TIMEOUT number { conf->timeout = $2; }
;
service : SERVICE STRING {
@@ -291,7 +292,7 @@ table : TABLE STRING {
YYERROR;
}
tb->id = last_table_id++;
- tb->timeout = CONNECT_TIMEOUT;
+ tb->timeout = conf->timeout;
if (last_table_id == UINT_MAX) {
yyerror("too many tables defined");
YYERROR;
@@ -685,6 +686,7 @@ parse_config(struct hostated *x_conf, const char *filename, int opts)
(void)strlcpy(conf->empty_table.name, "empty",
sizeof(conf->empty_table.name));
+ conf->timeout = CHECK_TIMEOUT;
conf->interval = CHECK_INTERVAL;
conf->opts = opts;
diff --git a/usr.sbin/hoststated/check_http.c b/usr.sbin/hoststated/check_http.c
index 5dc759e4c42..3529764375b 100644
--- a/usr.sbin/hoststated/check_http.c
+++ b/usr.sbin/hoststated/check_http.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: check_http.c,v 1.2 2006/12/16 12:42:14 reyk Exp $ */
+/* $OpenBSD: check_http.c,v 1.3 2006/12/25 18:12:14 reyk Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
*
@@ -31,114 +31,164 @@
#include "hostated.h"
-struct buf *http_request(struct host *, struct table *, int, const char *);
+void check_http_code(struct ctl_tcp_event *);
+void check_http_digest(struct ctl_tcp_event *);
+void http_read(int, short, void *);
-struct buf *
-http_request(struct host *host, struct table *table, int s, const char *req)
+void
+check_http_code(struct ctl_tcp_event *cte)
{
- int fl;
- ssize_t sz;
- char rbuf[1024];
- struct buf *buf;
-
- if ((fl = fcntl(s, F_GETFL, 0)) == -1)
- fatal("http_request: cannot get flags for socket");
- if (fcntl(s, F_SETFL, fl & ~(O_NONBLOCK)) == -1)
- fatal("http_request: cannot set blocking socket");
- if ((buf = buf_dynamic(sizeof(rbuf), UINT_MAX)) == NULL)
- fatalx("http_request: cannot create dynamic buffer");
-
- if (write(s, req, strlen(req)) != (ssize_t) strlen(req)) {
- close(s);
- return (NULL);
- }
- for (; (sz = read(s, rbuf, sizeof(rbuf))) != 0; ) {
- if (sz == -1)
- fatal("http_request: read");
- if (buf_add(buf, rbuf, sz) == -1)
- fatal("http_request: buf_add");
- }
- return (buf);
-}
-
-int
-check_http_code(struct host *host, struct table *table)
-{
- int s;
- int code;
- char scode[4];
- char *req;
char *head;
+ char scode[4];
const char *estr;
- struct buf *buf;
-
- if ((s = tcp_connect(host, table)) <= 0)
- return (s);
-
- asprintf(&req, "HEAD %s HTTP/1.0\r\n\r\n", table->path);
- if ((buf = http_request(host, table, s, req)) == NULL)
- return (HOST_UNKNOWN);
- free(req);
+ int code;
- head = buf->buf;
+ head = cte->buf->buf;
if (strncmp(head, "HTTP/1.1 ", strlen("HTTP/1.1 ")) &&
strncmp(head, "HTTP/1.0 ", strlen("HTTP/1.0 "))) {
log_debug("check_http_code: cannot parse HTTP version");
- close(s);
- return (HOST_DOWN);
+ cte->host->up = HOST_DOWN;
+ return;
}
head += strlen("HTTP/1.1 ");
- if (strlen(head) < 5) /* code + \r\n */
- return (HOST_DOWN);
+ if (strlen(head) < 5) /* code + \r\n */ {
+ cte->host->up = HOST_DOWN;
+ return;
+ }
strlcpy(scode, head, sizeof(scode));
code = strtonum(scode, 100, 999, &estr);
if (estr != NULL) {
log_debug("check_http_code: cannot parse HTTP code");
- close(s);
- return (HOST_DOWN);
+ cte->host->up = HOST_DOWN;
+ return;
}
- if (code != table->retcode) {
+ if (code != cte->table->retcode) {
log_debug("check_http_code: invalid HTTP code returned");
- close(s);
- return (HOST_DOWN);
- }
- close(s);
- return (HOST_UP);
+ cte->host->up = HOST_DOWN;
+ } else
+ cte->host->up = HOST_UP;
}
-int
-check_http_digest(struct host *host, struct table *table)
+void
+check_http_digest(struct ctl_tcp_event *cte)
{
- int s;
- char *head;
- char *req;
- struct buf *buf;
- char digest[(SHA1_DIGEST_LENGTH*2)+1];
-
- if ((s = tcp_connect(host, table)) <= 0)
- return (s);
+ char *head;
+ char digest[(SHA1_DIGEST_LENGTH*2)+1];
- asprintf(&req, "GET %s HTTP/1.0\r\n\r\n", table->path);
- if ((buf = http_request(host, table, s, req)) == NULL)
- return (HOST_UNKNOWN);
- free(req);
-
- head = buf->buf;
+ head = cte->buf->buf;
if ((head = strstr(head, "\r\n\r\n")) == NULL) {
log_debug("check_http_digest: host %u no end of headers",
- host->id);
- close(s);
- return (HOST_DOWN);
+ cte->host->id);
+ cte->host->up = HOST_DOWN;
+ return;
}
head += strlen("\r\n\r\n");
SHA1Data(head, strlen(head), digest);
- close(s);
- buf_free(buf);
- if (strcmp(table->digest, digest)) {
+ if (strcmp(cte->table->digest, digest)) {
log_warnx("check_http_digest: wrong digest for host %u",
- host->id);
- return (HOST_DOWN);
+ cte->host->id);
+ cte->host->up = HOST_DOWN;
+ } else
+ cte->host->up = HOST_UP;
+}
+
+void
+http_read(int s, short event, void *arg)
+{
+ ssize_t br;
+ char rbuf[SMALL_READ_BUF_SIZE];
+ struct timeval tv;
+ struct timeval tv_now;
+ struct ctl_tcp_event *cte = arg;
+
+ if (event == EV_TIMEOUT) {
+ cte->host->up = HOST_DOWN;
+ buf_free(cte->buf);
+ hce_notify_done(cte->host, "http_read: timeout");
+ return;
+ }
+ br = read(s, rbuf, sizeof(rbuf));
+ if (br == 0) {
+ cte->host->up = HOST_DOWN;
+ switch (cte->table->check) {
+ case CHECK_HTTP_CODE:
+ check_http_code(cte);
+ break;
+ case CHECK_HTTP_DIGEST:
+ check_http_digest(cte);
+ break;
+ default:
+ fatalx("http_read: unhandled check type");
+ }
+ buf_free(cte->buf);
+ hce_notify_done(cte->host, "http_read: connection closed");
+ } else if (br == -1) {
+ cte->host->up = HOST_DOWN;
+ buf_free(cte->buf);
+ hce_notify_done(cte->host, "http_read: read failed");
+ } else {
+ buf_add(cte->buf, rbuf, br);
+ tv.tv_sec = cte->table->timeout / 1000;
+ tv.tv_usec = cte->table->timeout % 1000;
+ if (gettimeofday(&tv_now, NULL))
+ fatal("send_http_request: gettimeofday");
+ timersub(&tv_now, &cte->tv_start, &tv_now);
+ timersub(&tv, &tv_now, &tv);
+ event_once(s, EV_READ|EV_TIMEOUT, http_read, cte, &tv);
}
- return (HOST_UP);
+}
+
+void
+send_http_request(struct ctl_tcp_event *cte)
+{
+ int bs;
+ int pos;
+ int len;
+ char *req;
+ struct timeval tv;
+ struct timeval tv_now;
+
+ switch (cte->table->check) {
+ case CHECK_HTTP_CODE:
+ asprintf(&req, "HEAD %s HTTP/1.0\r\n\r\n",
+ cte->table->path);
+ break;
+ case CHECK_HTTP_DIGEST:
+ asprintf(&req, "GET %s HTTP/1.0\r\n\r\n",
+ cte->table->path);
+ break;
+ default:
+ fatalx("send_http_request: unhandled check type");
+ }
+ if (req == NULL)
+ fatal("out of memory");
+ pos = 0;
+ len = strlen(req);
+ /*
+ * write all at once for now.
+ */
+ do {
+ bs = write(cte->s, req + pos, len);
+ if (bs <= 0) {
+ log_warnx("send_http_request: cannot send request");
+ cte->host->up = HOST_DOWN;
+ hce_notify_done(cte->host, "send_http_request: write");
+ free(req);
+ return;
+ }
+ pos += bs;
+ len -= bs;
+ } while (len > 0);
+ free(req);
+ if ((cte->buf = buf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL)
+ fatalx("send_http_request: cannot create dynamic buffer");
+
+ tv.tv_sec = cte->table->timeout / 1000;
+ tv.tv_usec = cte->table->timeout % 1000;
+ if (gettimeofday(&tv_now, NULL))
+ fatal("send_http_request: gettimeofday");
+ timersub(&tv_now, &cte->tv_start, &tv_now);
+ timersub(&tv, &tv_now, &tv);
+ event_once(cte->s, EV_READ|EV_TIMEOUT, http_read, cte, &tv);
}
diff --git a/usr.sbin/hoststated/check_icmp.c b/usr.sbin/hoststated/check_icmp.c
index 4d93a652345..ad562e771c4 100644
--- a/usr.sbin/hoststated/check_icmp.c
+++ b/usr.sbin/hoststated/check_icmp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: check_icmp.c,v 1.2 2006/12/16 11:59:12 reyk Exp $ */
+/* $OpenBSD: check_icmp.c,v 1.3 2006/12/25 18:12:14 reyk Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -32,151 +32,306 @@
#include <errno.h>
#include <unistd.h>
#include <string.h>
+#include <stdlib.h>
#include "hostated.h"
-int check_icmp6(struct host *, int, int);
-int check_icmp4(struct host *, int, int);
-int in_cksum(u_short *, int);
+int icmp6_checks_done(struct ctl_icmp_event *);
+int icmp4_checks_done(struct ctl_icmp_event *);
+void send_icmp6(struct ctl_icmp_event *, struct host *);
+void send_icmp4(struct ctl_icmp_event *, struct host *);
+void recv_icmp6(int, short, void *);
+void recv_icmp4(int, short, void *);
+int in_cksum(u_short *, int);
-int check_icmp(struct host *host, int s, int s6, int timeout)
+void
+schedule_icmp(struct ctl_icmp_event *cie, struct table *table)
{
- if (host->ss.ss_family == AF_INET)
- return (check_icmp4(host, s, timeout));
- else
- return (check_icmp6(host, s6, timeout));
+ struct host *host;
+
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (host->flags & F_DISABLE)
+ continue;
+ host->last_up = host->up;
+ host->flags &= ~F_CHECK_DONE;
+ if (((struct sockaddr *)&host->ss)->sa_family == AF_INET) {
+ send_icmp4(cie, host);
+ } else {
+ send_icmp6(cie, host);
+ }
+ }
+}
+
+void
+check_icmp(struct ctl_icmp_event *cie)
+{
+ struct timeval tv;
+
+ if (gettimeofday(&cie->tv_start, NULL))
+ fatal("check_icmp: gettimeofday");
+ if (cie->has_icmp4) {
+ tv.tv_sec = cie->env->timeout / 1000;
+ tv.tv_usec = cie->env->timeout % 1000;
+ event_once(cie->icmp_sock, EV_READ|EV_TIMEOUT,
+ recv_icmp4, cie, &tv);
+ }
+ if (cie->has_icmp6) {
+ tv.tv_sec = cie->env->timeout / 1000;
+ tv.tv_usec = cie->env->timeout % 1000;
+ event_once(cie->icmp6_sock, EV_READ|EV_TIMEOUT,
+ recv_icmp6, cie, &tv);
+ }
}
-int check_icmp6(struct host *host, int s, int timeout)
+int
+icmp6_checks_done(struct ctl_icmp_event *cie)
+{
+ struct table *table;
+ struct host *host;
+
+ TAILQ_FOREACH(table, &cie->env->tables, entry) {
+ if (table->flags & F_DISABLE || table->check != CHECK_ICMP)
+ continue;
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (((struct sockaddr *)&host->ss)->sa_family !=
+ AF_INET6)
+ continue;
+ if (!(host->flags & F_CHECK_DONE))
+ return (0);
+ }
+ }
+ return (1);
+}
+
+int
+icmp4_checks_done(struct ctl_icmp_event *cie)
+{
+ struct table *table;
+ struct host *host;
+
+ TAILQ_FOREACH(table, &cie->env->tables, entry) {
+ if (table->flags & F_DISABLE || table->check != CHECK_ICMP)
+ continue;
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (((struct sockaddr *)&host->ss)->sa_family !=
+ AF_INET)
+ continue;
+ if (!(host->flags & F_CHECK_DONE)) {
+ return (0);
+ }
+ }
+ }
+ return (1);
+}
+
+void
+send_icmp6(struct ctl_icmp_event *cie, struct host *host)
{
struct sockaddr *to;
struct icmp6_hdr *icp;
- int ident;
ssize_t i;
- int cc;
int datalen = (64 - 8);
u_char packet[datalen];
- fd_set fdset;
- socklen_t len;
- struct timeval tv;
+ cie->has_icmp6 = 1;
to = (struct sockaddr *)&host->ss;
- ident = getpid() & 0xFFFF;
- len = sizeof(struct sockaddr_in6);
-
bzero(&packet, sizeof(packet));
icp = (struct icmp6_hdr *)packet;
icp->icmp6_type = ICMP6_ECHO_REQUEST;
icp->icmp6_code = 0;
icp->icmp6_seq = 1;
- icp->icmp6_id = ident;
-
- memset((packet + sizeof(*icp)), 'X', datalen);
- cc = datalen + 8;
-
- i = sendto(s, packet, cc, 0, to, len);
-
- if (i < 0 || i != cc) {
- log_warn("check_icmp6: cannot send ping");
- return (HOST_UNKNOWN);
- }
-
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = timeout % 1000;
- FD_ZERO(&fdset);
- FD_SET(s, &fdset);
- switch (select(s + 1, &fdset, NULL, NULL, &tv)) {
- case -1:
- if (errno == EINTR) {
- log_warnx("check_icmp6: interrupted");
- return (HOST_UNKNOWN);
- } else
- fatal("check_icmp6: select");
- case 0:
- log_debug("check_icmp6: timeout");
- return (HOST_DOWN);
- default:
- bzero(&packet, sizeof(packet));
- i = recvfrom(s, packet, cc, 0, to, &len);
- if (i < 0 || i != cc) {
- log_warn("check_icmp6: did not receive valid ping");
- return (HOST_DOWN);
- }
- icp = (struct icmp6_hdr *)(packet);
- if (icp->icmp6_id != ident) {
- log_warnx("check_icmp6: did not receive valid ident");
- return (HOST_DOWN);
- }
- break;
+ icp->icmp6_id = getpid() & 0xffff;
+
+ memcpy((packet + sizeof(*icp)), &host->id, sizeof(host->id));
+
+ i = sendto(cie->icmp6_sock, packet, datalen + 8, 0, to,
+ sizeof(struct sockaddr_in6));
+ if (i < 0 || i != datalen + 8) {
+ host->up = HOST_DOWN;
+ hce_notify_done(host, "send_icmp6: cannot send");
+ return;
}
- return (HOST_UP);
}
-int check_icmp4(struct host *host, int s, int timeout)
+void
+send_icmp4(struct ctl_icmp_event *cie, struct host *host)
{
- struct sockaddr *to;
- struct icmp *icp;
- int ident;
- ssize_t i;
- int cc;
- int datalen = (64 - 8);
- u_char packet[datalen];
- fd_set fdset;
- socklen_t len;
- struct timeval tv;
+ struct sockaddr *to;
+ struct icmp *icp;
+ ssize_t i;
+ int datalen = (64 - 8);
+ u_char packet[datalen];
+ cie->has_icmp4 = 1;
to = (struct sockaddr *)&host->ss;
- ident = getpid() & 0xFFFF;
- len = sizeof(struct sockaddr_in);
-
bzero(&packet, sizeof(packet));
icp = (struct icmp *)packet;
- icp->icmp_type = htons(ICMP_ECHO);
+ icp->icmp_type = ICMP_ECHO;
icp->icmp_code = 0;
icp->icmp_seq = htons(1);
- icp->icmp_id = htons(ident);
+ icp->icmp_id = htons(getpid() & 0xffff);
icp->icmp_cksum = 0;
- memset(icp->icmp_data, 'X', datalen);
- cc = datalen + 8;
- icp->icmp_cksum = in_cksum((u_short *)icp, cc);
-
- i = sendto(s, packet, cc, 0, to, len);
-
- if (i < 0 || i != cc) {
- log_warn("check_icmp4: cannot send ping");
- return (HOST_UNKNOWN);
- }
-
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = timeout % 1000;
- FD_ZERO(&fdset);
- FD_SET(s, &fdset);
- switch (select(s + 1, &fdset, NULL, NULL, &tv)) {
- case -1:
- if (errno == EINTR) {
- log_warnx("check_icmp4: ping interrupted");
- return (HOST_UNKNOWN);
- } else
- fatal("check_icmp4: select");
- case 0:
- log_debug("check_icmp4: timeout");
- return (HOST_DOWN);
- default:
- bzero(&packet, sizeof(packet));
- i = recvfrom(s, packet, cc, 0, to, &len);
- if (i < 0 || i != cc) {
- log_warn("check_icmp4: did not receive valid ping");
- return (HOST_DOWN);
+ memcpy(icp->icmp_data, &host->id, sizeof(host->id));
+ icp->icmp_cksum = in_cksum((u_short *)icp, datalen + 8);
+
+ i = sendto(cie->icmp_sock, packet, datalen + 8, 0, to,
+ sizeof(struct sockaddr_in));
+ if (i < 0 || i != datalen + 8) {
+ host->up = HOST_DOWN;
+ hce_notify_done(host, "send_icmp4: cannot send");
+ }
+}
+
+void
+recv_icmp6(int s, short event, void *arg)
+{
+ struct ctl_icmp_event *cie = arg;
+ int datalen = (64 - 8);
+ u_char packet[datalen];
+ socklen_t len;
+ struct sockaddr_storage ss;
+ struct icmp6_hdr *icp;
+ struct host *host;
+ struct table *table;
+ ssize_t i;
+ objid_t id;
+ struct timeval tv;
+ struct timeval tv_now;
+
+ if (event == EV_TIMEOUT) {
+ /*
+ * mark all hosts which have not responded yet as down.
+ */
+ TAILQ_FOREACH(table, &cie->env->tables, entry) {
+ if (table->check != CHECK_ICMP ||
+ table->flags & F_DISABLE)
+ continue;
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (host->flags & F_DISABLE)
+ continue;
+ if (((struct sockaddr *)&host->ss)->sa_family
+ != AF_INET6)
+ continue;
+ if (!(host->flags & F_CHECK_DONE)) {
+ host->up = HOST_DOWN;
+ }
+ }
}
- icp = (struct icmp *)(packet + sizeof(struct ip));
- if (ntohs(icp->icmp_id) != ident) {
- log_warnx("check_icmp4: did not receive valid ident");
- return (HOST_DOWN);
+ return;
+ }
+ bzero(&packet, sizeof(packet));
+ bzero(&ss, sizeof(ss));
+ len = sizeof(struct sockaddr_in6);
+ i = recvfrom(s, packet, datalen + 8, 0, (struct sockaddr *)&ss, &len);
+ if (i < 0 || i != datalen + 8) {
+ log_warn("recv_icmp6: did not receive valid ping");
+ return;
+ }
+ icp = (struct icmp6_hdr *)(packet);
+ memcpy(&id, (packet + sizeof(*icp)), sizeof(id));
+ host = host_find(cie->env, id);
+ if (host == NULL)
+ log_warn("recv_icmp6: ping for unknown host received");
+ if (bcmp(&ss, &host->ss, len)) {
+ log_warnx("recv_icmp6: forged icmp packet ?");
+ return;
+ }
+ if (icp->icmp6_id != (getpid() & 0xffff)) {
+ log_warnx("recv_icmp6: did not receive valid ident");
+ host->up = HOST_DOWN;
+ } else
+ host->up = HOST_UP;
+ hce_notify_done(host, "recv_icmp6: final");
+ if (icmp6_checks_done(cie))
+ return;
+ if (gettimeofday(&tv_now, NULL))
+ fatal("recv_icmp6: gettimeofday");
+ tv.tv_sec = cie->env->timeout / 1000;
+ tv.tv_usec = cie->env->timeout % 1000;
+ timersub(&tv_now, &cie->tv_start, &tv_now);
+ timersub(&tv, &tv_now, &tv);
+ event_once(cie->icmp6_sock, EV_READ|EV_TIMEOUT, recv_icmp6, cie, &tv);
+}
+
+void
+recv_icmp4(int s, short event, void *arg)
+{
+ int datalen = (64 - 8);
+ socklen_t len;
+ struct icmp *icp;
+ struct ctl_icmp_event *cie = arg;
+ u_char packet[datalen];
+ struct host *host;
+ struct table *table;
+ ssize_t i;
+ objid_t id;
+ struct timeval tv;
+ struct timeval tv_now;
+ struct sockaddr_storage ss;
+
+ if (event == EV_TIMEOUT) {
+ /*
+ * mark all hosts which have not responded yet as down.
+ */
+ TAILQ_FOREACH(table, &cie->env->tables, entry) {
+ if (table->check != CHECK_ICMP ||
+ table->flags & F_DISABLE)
+ continue;
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (host->flags & F_DISABLE)
+ continue;
+ if (((struct sockaddr *)&host->ss)->sa_family
+ != AF_INET)
+ continue;
+ if (!(host->flags & F_CHECK_DONE)) {
+ host->up = HOST_DOWN;
+ }
+ }
}
- break;
+ return;
+ }
+
+ len = sizeof(struct sockaddr_in);
+ bzero(&packet, sizeof(packet));
+ bzero(&ss, sizeof(ss));
+ i = recvfrom(s, packet, datalen + 8, 0, (struct sockaddr *)&ss, &len);
+ if (i < 0 || i != (datalen + 8)) {
+ log_warn("recv_icmp4: did not receive valid ping");
+ return;
+ }
+
+ icp = (struct icmp *)(packet + sizeof(struct ip));
+ memcpy(&id, icp->icmp_data, sizeof(id));
+ host = host_find(cie->env, id);
+ if (host == NULL) {
+ log_warnx("recv_icmp4: received ping for unknown host");
+ return;
+ }
+ if (bcmp(&ss, &host->ss, len)) {
+ log_warnx("recv_icmp4: forged icmp packet ?");
+ return;
+ }
+ if (ntohs(icp->icmp_id) != (getpid() & 0xffff)) {
+ log_warnx("recv_icmp4: did not receive valid ident");
+ host->up = HOST_DOWN;
+ } else
+ host->up = HOST_UP;
+
+ host->flags |= F_CHECK_DONE;
+ if (icmp4_checks_done(cie)) {
+ hce_notify_done(host, "recv_icmp4: all done");
+ return;
}
- return (HOST_UP);
+ hce_notify_done(host, "recv_icmp4: host");
+
+ if (gettimeofday(&tv_now, NULL))
+ fatal("recv_icmp4: gettimeofday");
+ tv.tv_sec = cie->env->timeout / 1000;
+ tv.tv_usec = cie->env->timeout % 1000;
+ timersub(&tv_now, &cie->tv_start, &tv_now);
+ timersub(&tv, &tv_now, &tv);
+ event_once(cie->icmp_sock, EV_READ|EV_TIMEOUT, recv_icmp4, cie, &tv);
}
/* in_cksum from ping.c --
diff --git a/usr.sbin/hoststated/check_tcp.c b/usr.sbin/hoststated/check_tcp.c
index a0390326a7a..2a65abc4307 100644
--- a/usr.sbin/hoststated/check_tcp.c
+++ b/usr.sbin/hoststated/check_tcp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: check_tcp.c,v 1.2 2006/12/16 12:42:14 reyk Exp $ */
+/* $OpenBSD: check_tcp.c,v 1.3 2006/12/25 18:12:14 reyk Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -27,84 +27,108 @@
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
+#include <stdlib.h>
#include <errno.h>
#include "hostated.h"
-int
-check_tcp(struct host *host, struct table *table)
-{
- int sock;
-
- if ((sock = tcp_connect(host, table)) <= 0)
- return (sock);
- close(sock);
- return (HOST_UP);
-}
+void tcp_write(int, short, void *);
+void tcp_host_up(int s, struct ctl_tcp_event *);
-int
-tcp_connect(struct host *host, struct table *table)
+void
+check_tcp(struct ctl_tcp_event *cte)
{
- int s;
- socklen_t len;
- struct timeval tv;
- struct sockaddr sa;
- fd_set fdset;
+ int s;
+ int type;
+ socklen_t len;
+ struct timeval tv;
+ struct linger lng;
- switch (host->ss.ss_family) {
+ switch (cte->host->ss.ss_family) {
case AF_INET:
- ((struct sockaddr_in *)&host->ss)->sin_port =
- htons(table->port);
+ ((struct sockaddr_in *)&cte->host->ss)->sin_port =
+ htons(cte->table->port);
break;
case AF_INET6:
- ((struct sockaddr_in6 *)&host->ss)->sin6_port =
- htons(table->port);
+ ((struct sockaddr_in6 *)&cte->host->ss)->sin6_port =
+ htons(cte->table->port);
break;
}
- len = ((struct sockaddr *)&host->ss)->sa_len;
+ len = ((struct sockaddr *)&cte->host->ss)->sa_len;
+
+ if ((s = socket(cte->host->ss.ss_family, SOCK_STREAM, 0)) == -1)
+ goto bad;
- if ((s = socket(host->ss.ss_family, SOCK_STREAM, 0)) == -1)
- fatal("check_tcp: cannot create socket");
+ bzero(&lng, sizeof(lng));
+ if (setsockopt(s, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1)
+ goto bad;
+
+ type = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &type, sizeof(type)) == -1)
+ goto bad;
if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
- fatal("check_tcp: cannot set non blocking socket");
+ goto bad;
- if (connect(s, (struct sockaddr *)&host->ss, len) == -1) {
- if (errno != EINPROGRESS && errno != EWOULDBLOCK) {
- close(s);
- return (HOST_DOWN);
- }
- } else
- return (s);
+ if (connect(s, (struct sockaddr *)&cte->host->ss, len) == -1) {
+ if (errno != EINPROGRESS)
+ goto bad;
+ } else {
+ cte->host->up = HOST_UP;
+ tcp_host_up(s, cte);
+ return;
+ }
+ tv.tv_sec = cte->table->timeout / 1000;
+ tv.tv_usec = cte->table->timeout % 1000;
+ event_once(s, EV_TIMEOUT|EV_WRITE, tcp_write, cte, &tv);
+ return;
+bad:
+ close(s);
+ cte->host->up = HOST_DOWN;
+ hce_notify_done(cte->host, "check_tcp: cannot connect");
+}
- tv.tv_sec = table->timeout / 1000;
- tv.tv_usec = table->timeout % 1000;
- FD_ZERO(&fdset);
- FD_SET(s, &fdset);
+void
+tcp_write(int s, short event, void *arg)
+{
+ struct ctl_tcp_event *cte = arg;
+ int err;
+ socklen_t len;
- /* XXX This needs to be rewritten */
- switch (select(s + 1, NULL, &fdset, NULL, &tv)) {
- case -1:
- if (errno != EINTR)
- fatal("check_tcp: select");
+ if (event == EV_TIMEOUT) {
+ log_debug("tcp_write: connect timed out");
+ cte->host->up = HOST_DOWN;
+ } else {
+ len = sizeof(err);
+ if (getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len))
+ fatal("tcp_write: getsockopt");
+ if (err)
+ cte->host->up = HOST_DOWN;
else
- return (HOST_UNKNOWN);
- case 0:
+ cte->host->up = HOST_UP;
+ }
+ if (cte->host->up == HOST_UP)
+ tcp_host_up(s, cte);
+ else {
+ close(s);
+ hce_notify_done(cte->host, "connect failed");
+ }
+}
+
+void
+tcp_host_up(int s, struct ctl_tcp_event *cte)
+{
+ switch (cte->table->check) {
+ case CHECK_TCP:
close(s);
- return (HOST_DOWN);
+ hce_notify_done(cte->host, "tcp_write: success");
+ break;
+ case CHECK_HTTP_CODE:
+ case CHECK_HTTP_DIGEST:
+ send_http_request(cte);
+ break;
default:
- if (getpeername(s, &sa, &len) == -1) {
- if (errno == ENOTCONN) {
- close(s);
- return (HOST_DOWN);
- } else {
- log_debug("check_tcp: unknown peername");
- close(s);
- return (HOST_UNKNOWN);
- }
- } else
- return (s);
+ fatalx("tcp_write: unhandled check type");
}
- return (HOST_UNKNOWN);
}
diff --git a/usr.sbin/hoststated/hce.c b/usr.sbin/hoststated/hce.c
index c3d7c69eba0..6b291036e17 100644
--- a/usr.sbin/hoststated/hce.c
+++ b/usr.sbin/hoststated/hce.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: hce.c,v 1.3 2006/12/16 17:48:27 deraadt Exp $ */
+/* $OpenBSD: hce.c,v 1.4 2006/12/25 18:12:14 reyk Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -43,8 +43,9 @@ void hce_shutdown(void);
void hce_dispatch_imsg(int, short, void *);
void hce_dispatch_parent(int, short, void *);
void hce_launch_checks(int, short, void *);
+int hce_checks_done(void);
-static struct hostated *env = NULL;
+static struct hostated *env = NULL;
struct imsgbuf *ibuf_pfe;
struct imsgbuf *ibuf_main;
@@ -103,6 +104,10 @@ hce(struct hostated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2],
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
fatal("hce: can't drop privileges");
+ env->cie.icmp_sock = env->icmp_sock;
+ env->cie.icmp6_sock = env->icmp6_sock;
+ env->cie.env = env;
+
event_init();
signal_set(&ev_sigint, SIGINT, hce_sig_handler, NULL);
@@ -148,51 +153,89 @@ hce(struct hostated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2],
void
hce_launch_checks(int fd, short event, void *arg)
{
- int previous_up;
struct host *host;
struct table *table;
- struct ctl_status st;
- struct timeval tv;
- tv.tv_sec = env->interval;
- tv.tv_usec = 0;
- evtimer_add(&env->ev, &tv);
- bzero(&st, sizeof(st));
+ TAILQ_FOREACH(table, &env->tables, entry) {
+ if (table->flags & F_DISABLE)
+ continue;
+ if (table->check == CHECK_NOCHECK)
+ fatalx("hce_launch_checks: unknown check type");
+ if (table->check == CHECK_ICMP) {
+ schedule_icmp(&env->cie, table);
+ continue;
+ }
+ /*
+ * tcp type checks follow
+ */
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (host->flags & F_DISABLE)
+ continue;
+ bzero(&host->cte, sizeof(host->cte));
+ host->last_up = host->up;
+ host->cte.host = host;
+ host->cte.table = table;
+ if (gettimeofday(&host->cte.tv_start, NULL))
+ fatal("hce_launch_checks: gettimeofday");
+ check_tcp(&host->cte);
+ }
+ }
+ check_icmp(&env->cie);
+}
+
+int
+hce_checks_done()
+{
+ struct table *table;
+ struct host *host;
+
TAILQ_FOREACH(table, &env->tables, entry) {
if (table->flags & F_DISABLE)
continue;
TAILQ_FOREACH(host, &table->hosts, entry) {
if (host->flags & F_DISABLE)
continue;
- previous_up = host->up;
- switch (table->check) {
- case CHECK_ICMP:
- host->up = check_icmp(host, env->icmp_sock,
- env->icmp6_sock, table->timeout);
- break;
- case CHECK_TCP:
- host->up = check_tcp(host, table);
- break;
- case CHECK_HTTP_CODE:
- host->up = check_http_code(host, table);
- break;
- case CHECK_HTTP_DIGEST:
- host->up = check_http_digest(host, table);
- break;
- default:
- fatalx("hce_launch_checks: unknown check type");
- break;
- }
- if (host->up != previous_up) {
- st.id = host->id;
- st.up = host->up;
- imsg_compose(ibuf_pfe, IMSG_HOST_STATUS, 0, 0,
- &st, sizeof(st));
- }
+ if (!(host->flags & F_CHECK_DONE))
+ return (0);
+ }
+ }
+ return (1);
+}
+
+void
+hce_notify_done(struct host *host, const char *msg)
+{
+ struct ctl_status st;
+ struct timeval tv;
+ struct table *table;
+
+ st.id = host->id;
+ st.up = host->up;
+ host->flags |= F_CHECK_DONE;
+ if (msg)
+ log_debug("hce_notify_done: %s", msg);
+ if (host->up != host->last_up) {
+ imsg_compose(ibuf_pfe, IMSG_HOST_STATUS, 0, 0, &st, sizeof(st));
+ host->last_up = host->up;
+ }
+ /*
+ * check if everything is done, I see no other way than going
+ * through the tree for every host that calls this function.
+ */
+ if (hce_checks_done()) {
+ /*
+ * notify pfe checks are done and schedule next check
+ */
+ imsg_compose(ibuf_pfe, IMSG_SYNC, 0, 0, NULL, 0);
+ TAILQ_FOREACH(table, &env->tables, entry) {
+ TAILQ_FOREACH(host, &table->hosts, entry)
+ host->flags &= ~F_CHECK_DONE;
}
+ tv.tv_sec = env->interval;
+ tv.tv_usec = 0;
+ evtimer_add(&env->ev, &tv);
+ bzero(&st, sizeof(st));
}
- /* tell pfe we're finished */
- imsg_compose(ibuf_pfe, IMSG_SYNC, 0, 0, NULL, 0);
}
void
diff --git a/usr.sbin/hoststated/hoststated.conf.5 b/usr.sbin/hoststated/hoststated.conf.5
index 0ce39e23d5f..dc3cf6a8bf7 100644
--- a/usr.sbin/hoststated/hoststated.conf.5
+++ b/usr.sbin/hoststated/hoststated.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: hoststated.conf.5,v 1.5 2006/12/19 14:39:30 jmc Exp $
+.\" $OpenBSD: hoststated.conf.5,v 1.6 2006/12/25 18:12:14 reyk Exp $
.\"
.\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
.\"
@@ -68,10 +68,17 @@ table webhosts {
}
.Ed
.Sh GLOBAL CONFIGURATION
-Only one global setting can be set.
+Here are the settings that can be set globally:
.Pp
.Bl -tag -width Ds -compact
.It Xo
+.Ic timeout Ar number
+.Xc
+Set the global timeout for checks.
+This can be overriden by the timeout value in the table definitions
+and is 200 milliseconds by default.
+.Pp
+.It Xo
.Ic interval Ar number
.Xc
Set the interval in seconds at which the hosts will be checked.
@@ -125,8 +132,9 @@ to hosts.
This parameter is mandatory.
Main and backup tables need to have the same real port.
.It Ic timeout Ar number
-Set the timeout in milliseconds for each host that is checked.
-The default timeout is 200 milliseconds.
+Set the timeout in milliseconds for each host that is checked using
+TCP as the transport.
+This will override the global timeout, which is 200 milliseconds by default.
.El
.Sh SERVICES
Services represent a
diff --git a/usr.sbin/hoststated/hoststated.h b/usr.sbin/hoststated/hoststated.h
index 2187d838a3f..4ca1519bae2 100644
--- a/usr.sbin/hoststated/hoststated.h
+++ b/usr.sbin/hoststated/hoststated.h
@@ -20,7 +20,7 @@
#define PF_SOCKET "/dev/pf"
#define HOSTATED_USER "_hostated"
#define HOSTATED_ANCHOR "hostated"
-#define CONNECT_TIMEOUT 200
+#define CHECK_TIMEOUT 200
#define CHECK_INTERVAL 10
#define EMPTY_TABLE UINT_MAX
#define EMPTY_ID UINT_MAX
@@ -30,7 +30,8 @@
#define MAX_NAME_SIZE 64
#define SRV_MAX_VIRTS 16
-#define READ_BUF_SIZE 65535
+#define SMALL_READ_BUF_SIZE 1024
+#define READ_BUF_SIZE 65535
/* buffer */
struct buf {
@@ -120,6 +121,25 @@ struct ctl_id {
char name[MAX_NAME_SIZE];
};
+struct ctl_icmp_event {
+ struct hostated *env;
+ int icmp_sock;
+ int icmp6_sock;
+ int has_icmp4;
+ int has_icmp6;
+ int last_up;
+ struct event ev;
+ struct timeval tv_start;
+};
+
+struct ctl_tcp_event {
+ int s;
+ struct buf *buf;
+ struct host *host;
+ struct table *table;
+ struct timeval tv_start;
+};
+
struct address {
struct sockaddr_storage ss;
in_port_t port;
@@ -130,8 +150,9 @@ TAILQ_HEAD(addresslist, address);
#define F_DISABLE 0x01
#define F_BACKUP 0x02
+#define F_CHECK_DONE 0x02 /* reused for host */
#define F_USED 0x04
-#define F_ACTIVE_RULESET 0x04
+#define F_ACTIVE_RULESET 0x04 /* reused for service */
#define F_DOWN 0x08
#define F_ADD 0x10
#define F_DEL 0x20
@@ -144,7 +165,9 @@ struct host {
char *tablename;
char name[MAXHOSTNAMELEN];
int up;
+ int last_up;
struct sockaddr_storage ss;
+ struct ctl_tcp_event cte;
TAILQ_ENTRY(host) entry;
};
TAILQ_HEAD(hostlist, host);
@@ -203,10 +226,12 @@ struct hostated {
int icmp6_sock;
int tablecount;
int servicecount;
+ int timeout;
struct table empty_table;
struct event ev;
struct tablelist tables;
struct servicelist services;
+ struct ctl_icmp_event cie;
};
#define HOSTATED_OPT_VERBOSE 0x01
@@ -299,17 +324,17 @@ void flush_rulesets(struct hostated *);
/* hce.c */
pid_t hce(struct hostated *, int [2], int [2], int [2]);
+void hce_notify_done(struct host *, const char *);
/* check_icmp.c */
-int check_icmp(struct host *, int, int, int);
+void schedule_icmp(struct ctl_icmp_event *, struct table *);
+void check_icmp(struct ctl_icmp_event *);
/* check_tcp.c */
-int check_tcp(struct host *, struct table *);
-int tcp_connect(struct host *, struct table *);
+void check_tcp(struct ctl_tcp_event *);
-/* check_tcp.c */
-int check_http_code(struct host *, struct table *);
-int check_http_digest(struct host *, struct table *);
+/* check_http.c */
+void send_http_request(struct ctl_tcp_event *);
/* hostated.c */
struct host *host_find(struct hostated *, objid_t);
diff --git a/usr.sbin/hoststated/parse.y b/usr.sbin/hoststated/parse.y
index b6ce1f815bb..0c6008365b8 100644
--- a/usr.sbin/hoststated/parse.y
+++ b/usr.sbin/hoststated/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.4 2006/12/16 18:05:35 martin Exp $ */
+/* $OpenBSD: parse.y,v 1.5 2006/12/25 18:12:14 reyk Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -140,6 +140,7 @@ varset : STRING '=' STRING {
;
main : INTERVAL number { conf->interval = $2; }
+ | TIMEOUT number { conf->timeout = $2; }
;
service : SERVICE STRING {
@@ -291,7 +292,7 @@ table : TABLE STRING {
YYERROR;
}
tb->id = last_table_id++;
- tb->timeout = CONNECT_TIMEOUT;
+ tb->timeout = conf->timeout;
if (last_table_id == UINT_MAX) {
yyerror("too many tables defined");
YYERROR;
@@ -685,6 +686,7 @@ parse_config(struct hostated *x_conf, const char *filename, int opts)
(void)strlcpy(conf->empty_table.name, "empty",
sizeof(conf->empty_table.name));
+ conf->timeout = CHECK_TIMEOUT;
conf->interval = CHECK_INTERVAL;
conf->opts = opts;
diff --git a/usr.sbin/relayd/check_icmp.c b/usr.sbin/relayd/check_icmp.c
index 4d93a652345..ad562e771c4 100644
--- a/usr.sbin/relayd/check_icmp.c
+++ b/usr.sbin/relayd/check_icmp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: check_icmp.c,v 1.2 2006/12/16 11:59:12 reyk Exp $ */
+/* $OpenBSD: check_icmp.c,v 1.3 2006/12/25 18:12:14 reyk Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -32,151 +32,306 @@
#include <errno.h>
#include <unistd.h>
#include <string.h>
+#include <stdlib.h>
#include "hostated.h"
-int check_icmp6(struct host *, int, int);
-int check_icmp4(struct host *, int, int);
-int in_cksum(u_short *, int);
+int icmp6_checks_done(struct ctl_icmp_event *);
+int icmp4_checks_done(struct ctl_icmp_event *);
+void send_icmp6(struct ctl_icmp_event *, struct host *);
+void send_icmp4(struct ctl_icmp_event *, struct host *);
+void recv_icmp6(int, short, void *);
+void recv_icmp4(int, short, void *);
+int in_cksum(u_short *, int);
-int check_icmp(struct host *host, int s, int s6, int timeout)
+void
+schedule_icmp(struct ctl_icmp_event *cie, struct table *table)
{
- if (host->ss.ss_family == AF_INET)
- return (check_icmp4(host, s, timeout));
- else
- return (check_icmp6(host, s6, timeout));
+ struct host *host;
+
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (host->flags & F_DISABLE)
+ continue;
+ host->last_up = host->up;
+ host->flags &= ~F_CHECK_DONE;
+ if (((struct sockaddr *)&host->ss)->sa_family == AF_INET) {
+ send_icmp4(cie, host);
+ } else {
+ send_icmp6(cie, host);
+ }
+ }
+}
+
+void
+check_icmp(struct ctl_icmp_event *cie)
+{
+ struct timeval tv;
+
+ if (gettimeofday(&cie->tv_start, NULL))
+ fatal("check_icmp: gettimeofday");
+ if (cie->has_icmp4) {
+ tv.tv_sec = cie->env->timeout / 1000;
+ tv.tv_usec = cie->env->timeout % 1000;
+ event_once(cie->icmp_sock, EV_READ|EV_TIMEOUT,
+ recv_icmp4, cie, &tv);
+ }
+ if (cie->has_icmp6) {
+ tv.tv_sec = cie->env->timeout / 1000;
+ tv.tv_usec = cie->env->timeout % 1000;
+ event_once(cie->icmp6_sock, EV_READ|EV_TIMEOUT,
+ recv_icmp6, cie, &tv);
+ }
}
-int check_icmp6(struct host *host, int s, int timeout)
+int
+icmp6_checks_done(struct ctl_icmp_event *cie)
+{
+ struct table *table;
+ struct host *host;
+
+ TAILQ_FOREACH(table, &cie->env->tables, entry) {
+ if (table->flags & F_DISABLE || table->check != CHECK_ICMP)
+ continue;
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (((struct sockaddr *)&host->ss)->sa_family !=
+ AF_INET6)
+ continue;
+ if (!(host->flags & F_CHECK_DONE))
+ return (0);
+ }
+ }
+ return (1);
+}
+
+int
+icmp4_checks_done(struct ctl_icmp_event *cie)
+{
+ struct table *table;
+ struct host *host;
+
+ TAILQ_FOREACH(table, &cie->env->tables, entry) {
+ if (table->flags & F_DISABLE || table->check != CHECK_ICMP)
+ continue;
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (((struct sockaddr *)&host->ss)->sa_family !=
+ AF_INET)
+ continue;
+ if (!(host->flags & F_CHECK_DONE)) {
+ return (0);
+ }
+ }
+ }
+ return (1);
+}
+
+void
+send_icmp6(struct ctl_icmp_event *cie, struct host *host)
{
struct sockaddr *to;
struct icmp6_hdr *icp;
- int ident;
ssize_t i;
- int cc;
int datalen = (64 - 8);
u_char packet[datalen];
- fd_set fdset;
- socklen_t len;
- struct timeval tv;
+ cie->has_icmp6 = 1;
to = (struct sockaddr *)&host->ss;
- ident = getpid() & 0xFFFF;
- len = sizeof(struct sockaddr_in6);
-
bzero(&packet, sizeof(packet));
icp = (struct icmp6_hdr *)packet;
icp->icmp6_type = ICMP6_ECHO_REQUEST;
icp->icmp6_code = 0;
icp->icmp6_seq = 1;
- icp->icmp6_id = ident;
-
- memset((packet + sizeof(*icp)), 'X', datalen);
- cc = datalen + 8;
-
- i = sendto(s, packet, cc, 0, to, len);
-
- if (i < 0 || i != cc) {
- log_warn("check_icmp6: cannot send ping");
- return (HOST_UNKNOWN);
- }
-
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = timeout % 1000;
- FD_ZERO(&fdset);
- FD_SET(s, &fdset);
- switch (select(s + 1, &fdset, NULL, NULL, &tv)) {
- case -1:
- if (errno == EINTR) {
- log_warnx("check_icmp6: interrupted");
- return (HOST_UNKNOWN);
- } else
- fatal("check_icmp6: select");
- case 0:
- log_debug("check_icmp6: timeout");
- return (HOST_DOWN);
- default:
- bzero(&packet, sizeof(packet));
- i = recvfrom(s, packet, cc, 0, to, &len);
- if (i < 0 || i != cc) {
- log_warn("check_icmp6: did not receive valid ping");
- return (HOST_DOWN);
- }
- icp = (struct icmp6_hdr *)(packet);
- if (icp->icmp6_id != ident) {
- log_warnx("check_icmp6: did not receive valid ident");
- return (HOST_DOWN);
- }
- break;
+ icp->icmp6_id = getpid() & 0xffff;
+
+ memcpy((packet + sizeof(*icp)), &host->id, sizeof(host->id));
+
+ i = sendto(cie->icmp6_sock, packet, datalen + 8, 0, to,
+ sizeof(struct sockaddr_in6));
+ if (i < 0 || i != datalen + 8) {
+ host->up = HOST_DOWN;
+ hce_notify_done(host, "send_icmp6: cannot send");
+ return;
}
- return (HOST_UP);
}
-int check_icmp4(struct host *host, int s, int timeout)
+void
+send_icmp4(struct ctl_icmp_event *cie, struct host *host)
{
- struct sockaddr *to;
- struct icmp *icp;
- int ident;
- ssize_t i;
- int cc;
- int datalen = (64 - 8);
- u_char packet[datalen];
- fd_set fdset;
- socklen_t len;
- struct timeval tv;
+ struct sockaddr *to;
+ struct icmp *icp;
+ ssize_t i;
+ int datalen = (64 - 8);
+ u_char packet[datalen];
+ cie->has_icmp4 = 1;
to = (struct sockaddr *)&host->ss;
- ident = getpid() & 0xFFFF;
- len = sizeof(struct sockaddr_in);
-
bzero(&packet, sizeof(packet));
icp = (struct icmp *)packet;
- icp->icmp_type = htons(ICMP_ECHO);
+ icp->icmp_type = ICMP_ECHO;
icp->icmp_code = 0;
icp->icmp_seq = htons(1);
- icp->icmp_id = htons(ident);
+ icp->icmp_id = htons(getpid() & 0xffff);
icp->icmp_cksum = 0;
- memset(icp->icmp_data, 'X', datalen);
- cc = datalen + 8;
- icp->icmp_cksum = in_cksum((u_short *)icp, cc);
-
- i = sendto(s, packet, cc, 0, to, len);
-
- if (i < 0 || i != cc) {
- log_warn("check_icmp4: cannot send ping");
- return (HOST_UNKNOWN);
- }
-
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = timeout % 1000;
- FD_ZERO(&fdset);
- FD_SET(s, &fdset);
- switch (select(s + 1, &fdset, NULL, NULL, &tv)) {
- case -1:
- if (errno == EINTR) {
- log_warnx("check_icmp4: ping interrupted");
- return (HOST_UNKNOWN);
- } else
- fatal("check_icmp4: select");
- case 0:
- log_debug("check_icmp4: timeout");
- return (HOST_DOWN);
- default:
- bzero(&packet, sizeof(packet));
- i = recvfrom(s, packet, cc, 0, to, &len);
- if (i < 0 || i != cc) {
- log_warn("check_icmp4: did not receive valid ping");
- return (HOST_DOWN);
+ memcpy(icp->icmp_data, &host->id, sizeof(host->id));
+ icp->icmp_cksum = in_cksum((u_short *)icp, datalen + 8);
+
+ i = sendto(cie->icmp_sock, packet, datalen + 8, 0, to,
+ sizeof(struct sockaddr_in));
+ if (i < 0 || i != datalen + 8) {
+ host->up = HOST_DOWN;
+ hce_notify_done(host, "send_icmp4: cannot send");
+ }
+}
+
+void
+recv_icmp6(int s, short event, void *arg)
+{
+ struct ctl_icmp_event *cie = arg;
+ int datalen = (64 - 8);
+ u_char packet[datalen];
+ socklen_t len;
+ struct sockaddr_storage ss;
+ struct icmp6_hdr *icp;
+ struct host *host;
+ struct table *table;
+ ssize_t i;
+ objid_t id;
+ struct timeval tv;
+ struct timeval tv_now;
+
+ if (event == EV_TIMEOUT) {
+ /*
+ * mark all hosts which have not responded yet as down.
+ */
+ TAILQ_FOREACH(table, &cie->env->tables, entry) {
+ if (table->check != CHECK_ICMP ||
+ table->flags & F_DISABLE)
+ continue;
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (host->flags & F_DISABLE)
+ continue;
+ if (((struct sockaddr *)&host->ss)->sa_family
+ != AF_INET6)
+ continue;
+ if (!(host->flags & F_CHECK_DONE)) {
+ host->up = HOST_DOWN;
+ }
+ }
}
- icp = (struct icmp *)(packet + sizeof(struct ip));
- if (ntohs(icp->icmp_id) != ident) {
- log_warnx("check_icmp4: did not receive valid ident");
- return (HOST_DOWN);
+ return;
+ }
+ bzero(&packet, sizeof(packet));
+ bzero(&ss, sizeof(ss));
+ len = sizeof(struct sockaddr_in6);
+ i = recvfrom(s, packet, datalen + 8, 0, (struct sockaddr *)&ss, &len);
+ if (i < 0 || i != datalen + 8) {
+ log_warn("recv_icmp6: did not receive valid ping");
+ return;
+ }
+ icp = (struct icmp6_hdr *)(packet);
+ memcpy(&id, (packet + sizeof(*icp)), sizeof(id));
+ host = host_find(cie->env, id);
+ if (host == NULL)
+ log_warn("recv_icmp6: ping for unknown host received");
+ if (bcmp(&ss, &host->ss, len)) {
+ log_warnx("recv_icmp6: forged icmp packet ?");
+ return;
+ }
+ if (icp->icmp6_id != (getpid() & 0xffff)) {
+ log_warnx("recv_icmp6: did not receive valid ident");
+ host->up = HOST_DOWN;
+ } else
+ host->up = HOST_UP;
+ hce_notify_done(host, "recv_icmp6: final");
+ if (icmp6_checks_done(cie))
+ return;
+ if (gettimeofday(&tv_now, NULL))
+ fatal("recv_icmp6: gettimeofday");
+ tv.tv_sec = cie->env->timeout / 1000;
+ tv.tv_usec = cie->env->timeout % 1000;
+ timersub(&tv_now, &cie->tv_start, &tv_now);
+ timersub(&tv, &tv_now, &tv);
+ event_once(cie->icmp6_sock, EV_READ|EV_TIMEOUT, recv_icmp6, cie, &tv);
+}
+
+void
+recv_icmp4(int s, short event, void *arg)
+{
+ int datalen = (64 - 8);
+ socklen_t len;
+ struct icmp *icp;
+ struct ctl_icmp_event *cie = arg;
+ u_char packet[datalen];
+ struct host *host;
+ struct table *table;
+ ssize_t i;
+ objid_t id;
+ struct timeval tv;
+ struct timeval tv_now;
+ struct sockaddr_storage ss;
+
+ if (event == EV_TIMEOUT) {
+ /*
+ * mark all hosts which have not responded yet as down.
+ */
+ TAILQ_FOREACH(table, &cie->env->tables, entry) {
+ if (table->check != CHECK_ICMP ||
+ table->flags & F_DISABLE)
+ continue;
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (host->flags & F_DISABLE)
+ continue;
+ if (((struct sockaddr *)&host->ss)->sa_family
+ != AF_INET)
+ continue;
+ if (!(host->flags & F_CHECK_DONE)) {
+ host->up = HOST_DOWN;
+ }
+ }
}
- break;
+ return;
+ }
+
+ len = sizeof(struct sockaddr_in);
+ bzero(&packet, sizeof(packet));
+ bzero(&ss, sizeof(ss));
+ i = recvfrom(s, packet, datalen + 8, 0, (struct sockaddr *)&ss, &len);
+ if (i < 0 || i != (datalen + 8)) {
+ log_warn("recv_icmp4: did not receive valid ping");
+ return;
+ }
+
+ icp = (struct icmp *)(packet + sizeof(struct ip));
+ memcpy(&id, icp->icmp_data, sizeof(id));
+ host = host_find(cie->env, id);
+ if (host == NULL) {
+ log_warnx("recv_icmp4: received ping for unknown host");
+ return;
+ }
+ if (bcmp(&ss, &host->ss, len)) {
+ log_warnx("recv_icmp4: forged icmp packet ?");
+ return;
+ }
+ if (ntohs(icp->icmp_id) != (getpid() & 0xffff)) {
+ log_warnx("recv_icmp4: did not receive valid ident");
+ host->up = HOST_DOWN;
+ } else
+ host->up = HOST_UP;
+
+ host->flags |= F_CHECK_DONE;
+ if (icmp4_checks_done(cie)) {
+ hce_notify_done(host, "recv_icmp4: all done");
+ return;
}
- return (HOST_UP);
+ hce_notify_done(host, "recv_icmp4: host");
+
+ if (gettimeofday(&tv_now, NULL))
+ fatal("recv_icmp4: gettimeofday");
+ tv.tv_sec = cie->env->timeout / 1000;
+ tv.tv_usec = cie->env->timeout % 1000;
+ timersub(&tv_now, &cie->tv_start, &tv_now);
+ timersub(&tv, &tv_now, &tv);
+ event_once(cie->icmp_sock, EV_READ|EV_TIMEOUT, recv_icmp4, cie, &tv);
}
/* in_cksum from ping.c --
diff --git a/usr.sbin/relayd/check_tcp.c b/usr.sbin/relayd/check_tcp.c
index a0390326a7a..2a65abc4307 100644
--- a/usr.sbin/relayd/check_tcp.c
+++ b/usr.sbin/relayd/check_tcp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: check_tcp.c,v 1.2 2006/12/16 12:42:14 reyk Exp $ */
+/* $OpenBSD: check_tcp.c,v 1.3 2006/12/25 18:12:14 reyk Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -27,84 +27,108 @@
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
+#include <stdlib.h>
#include <errno.h>
#include "hostated.h"
-int
-check_tcp(struct host *host, struct table *table)
-{
- int sock;
-
- if ((sock = tcp_connect(host, table)) <= 0)
- return (sock);
- close(sock);
- return (HOST_UP);
-}
+void tcp_write(int, short, void *);
+void tcp_host_up(int s, struct ctl_tcp_event *);
-int
-tcp_connect(struct host *host, struct table *table)
+void
+check_tcp(struct ctl_tcp_event *cte)
{
- int s;
- socklen_t len;
- struct timeval tv;
- struct sockaddr sa;
- fd_set fdset;
+ int s;
+ int type;
+ socklen_t len;
+ struct timeval tv;
+ struct linger lng;
- switch (host->ss.ss_family) {
+ switch (cte->host->ss.ss_family) {
case AF_INET:
- ((struct sockaddr_in *)&host->ss)->sin_port =
- htons(table->port);
+ ((struct sockaddr_in *)&cte->host->ss)->sin_port =
+ htons(cte->table->port);
break;
case AF_INET6:
- ((struct sockaddr_in6 *)&host->ss)->sin6_port =
- htons(table->port);
+ ((struct sockaddr_in6 *)&cte->host->ss)->sin6_port =
+ htons(cte->table->port);
break;
}
- len = ((struct sockaddr *)&host->ss)->sa_len;
+ len = ((struct sockaddr *)&cte->host->ss)->sa_len;
+
+ if ((s = socket(cte->host->ss.ss_family, SOCK_STREAM, 0)) == -1)
+ goto bad;
- if ((s = socket(host->ss.ss_family, SOCK_STREAM, 0)) == -1)
- fatal("check_tcp: cannot create socket");
+ bzero(&lng, sizeof(lng));
+ if (setsockopt(s, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1)
+ goto bad;
+
+ type = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &type, sizeof(type)) == -1)
+ goto bad;
if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
- fatal("check_tcp: cannot set non blocking socket");
+ goto bad;
- if (connect(s, (struct sockaddr *)&host->ss, len) == -1) {
- if (errno != EINPROGRESS && errno != EWOULDBLOCK) {
- close(s);
- return (HOST_DOWN);
- }
- } else
- return (s);
+ if (connect(s, (struct sockaddr *)&cte->host->ss, len) == -1) {
+ if (errno != EINPROGRESS)
+ goto bad;
+ } else {
+ cte->host->up = HOST_UP;
+ tcp_host_up(s, cte);
+ return;
+ }
+ tv.tv_sec = cte->table->timeout / 1000;
+ tv.tv_usec = cte->table->timeout % 1000;
+ event_once(s, EV_TIMEOUT|EV_WRITE, tcp_write, cte, &tv);
+ return;
+bad:
+ close(s);
+ cte->host->up = HOST_DOWN;
+ hce_notify_done(cte->host, "check_tcp: cannot connect");
+}
- tv.tv_sec = table->timeout / 1000;
- tv.tv_usec = table->timeout % 1000;
- FD_ZERO(&fdset);
- FD_SET(s, &fdset);
+void
+tcp_write(int s, short event, void *arg)
+{
+ struct ctl_tcp_event *cte = arg;
+ int err;
+ socklen_t len;
- /* XXX This needs to be rewritten */
- switch (select(s + 1, NULL, &fdset, NULL, &tv)) {
- case -1:
- if (errno != EINTR)
- fatal("check_tcp: select");
+ if (event == EV_TIMEOUT) {
+ log_debug("tcp_write: connect timed out");
+ cte->host->up = HOST_DOWN;
+ } else {
+ len = sizeof(err);
+ if (getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len))
+ fatal("tcp_write: getsockopt");
+ if (err)
+ cte->host->up = HOST_DOWN;
else
- return (HOST_UNKNOWN);
- case 0:
+ cte->host->up = HOST_UP;
+ }
+ if (cte->host->up == HOST_UP)
+ tcp_host_up(s, cte);
+ else {
+ close(s);
+ hce_notify_done(cte->host, "connect failed");
+ }
+}
+
+void
+tcp_host_up(int s, struct ctl_tcp_event *cte)
+{
+ switch (cte->table->check) {
+ case CHECK_TCP:
close(s);
- return (HOST_DOWN);
+ hce_notify_done(cte->host, "tcp_write: success");
+ break;
+ case CHECK_HTTP_CODE:
+ case CHECK_HTTP_DIGEST:
+ send_http_request(cte);
+ break;
default:
- if (getpeername(s, &sa, &len) == -1) {
- if (errno == ENOTCONN) {
- close(s);
- return (HOST_DOWN);
- } else {
- log_debug("check_tcp: unknown peername");
- close(s);
- return (HOST_UNKNOWN);
- }
- } else
- return (s);
+ fatalx("tcp_write: unhandled check type");
}
- return (HOST_UNKNOWN);
}
diff --git a/usr.sbin/relayd/hce.c b/usr.sbin/relayd/hce.c
index c3d7c69eba0..6b291036e17 100644
--- a/usr.sbin/relayd/hce.c
+++ b/usr.sbin/relayd/hce.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: hce.c,v 1.3 2006/12/16 17:48:27 deraadt Exp $ */
+/* $OpenBSD: hce.c,v 1.4 2006/12/25 18:12:14 reyk Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -43,8 +43,9 @@ void hce_shutdown(void);
void hce_dispatch_imsg(int, short, void *);
void hce_dispatch_parent(int, short, void *);
void hce_launch_checks(int, short, void *);
+int hce_checks_done(void);
-static struct hostated *env = NULL;
+static struct hostated *env = NULL;
struct imsgbuf *ibuf_pfe;
struct imsgbuf *ibuf_main;
@@ -103,6 +104,10 @@ hce(struct hostated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2],
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
fatal("hce: can't drop privileges");
+ env->cie.icmp_sock = env->icmp_sock;
+ env->cie.icmp6_sock = env->icmp6_sock;
+ env->cie.env = env;
+
event_init();
signal_set(&ev_sigint, SIGINT, hce_sig_handler, NULL);
@@ -148,51 +153,89 @@ hce(struct hostated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2],
void
hce_launch_checks(int fd, short event, void *arg)
{
- int previous_up;
struct host *host;
struct table *table;
- struct ctl_status st;
- struct timeval tv;
- tv.tv_sec = env->interval;
- tv.tv_usec = 0;
- evtimer_add(&env->ev, &tv);
- bzero(&st, sizeof(st));
+ TAILQ_FOREACH(table, &env->tables, entry) {
+ if (table->flags & F_DISABLE)
+ continue;
+ if (table->check == CHECK_NOCHECK)
+ fatalx("hce_launch_checks: unknown check type");
+ if (table->check == CHECK_ICMP) {
+ schedule_icmp(&env->cie, table);
+ continue;
+ }
+ /*
+ * tcp type checks follow
+ */
+ TAILQ_FOREACH(host, &table->hosts, entry) {
+ if (host->flags & F_DISABLE)
+ continue;
+ bzero(&host->cte, sizeof(host->cte));
+ host->last_up = host->up;
+ host->cte.host = host;
+ host->cte.table = table;
+ if (gettimeofday(&host->cte.tv_start, NULL))
+ fatal("hce_launch_checks: gettimeofday");
+ check_tcp(&host->cte);
+ }
+ }
+ check_icmp(&env->cie);
+}
+
+int
+hce_checks_done()
+{
+ struct table *table;
+ struct host *host;
+
TAILQ_FOREACH(table, &env->tables, entry) {
if (table->flags & F_DISABLE)
continue;
TAILQ_FOREACH(host, &table->hosts, entry) {
if (host->flags & F_DISABLE)
continue;
- previous_up = host->up;
- switch (table->check) {
- case CHECK_ICMP:
- host->up = check_icmp(host, env->icmp_sock,
- env->icmp6_sock, table->timeout);
- break;
- case CHECK_TCP:
- host->up = check_tcp(host, table);
- break;
- case CHECK_HTTP_CODE:
- host->up = check_http_code(host, table);
- break;
- case CHECK_HTTP_DIGEST:
- host->up = check_http_digest(host, table);
- break;
- default:
- fatalx("hce_launch_checks: unknown check type");
- break;
- }
- if (host->up != previous_up) {
- st.id = host->id;
- st.up = host->up;
- imsg_compose(ibuf_pfe, IMSG_HOST_STATUS, 0, 0,
- &st, sizeof(st));
- }
+ if (!(host->flags & F_CHECK_DONE))
+ return (0);
+ }
+ }
+ return (1);
+}
+
+void
+hce_notify_done(struct host *host, const char *msg)
+{
+ struct ctl_status st;
+ struct timeval tv;
+ struct table *table;
+
+ st.id = host->id;
+ st.up = host->up;
+ host->flags |= F_CHECK_DONE;
+ if (msg)
+ log_debug("hce_notify_done: %s", msg);
+ if (host->up != host->last_up) {
+ imsg_compose(ibuf_pfe, IMSG_HOST_STATUS, 0, 0, &st, sizeof(st));
+ host->last_up = host->up;
+ }
+ /*
+ * check if everything is done, I see no other way than going
+ * through the tree for every host that calls this function.
+ */
+ if (hce_checks_done()) {
+ /*
+ * notify pfe checks are done and schedule next check
+ */
+ imsg_compose(ibuf_pfe, IMSG_SYNC, 0, 0, NULL, 0);
+ TAILQ_FOREACH(table, &env->tables, entry) {
+ TAILQ_FOREACH(host, &table->hosts, entry)
+ host->flags &= ~F_CHECK_DONE;
}
+ tv.tv_sec = env->interval;
+ tv.tv_usec = 0;
+ evtimer_add(&env->ev, &tv);
+ bzero(&st, sizeof(st));
}
- /* tell pfe we're finished */
- imsg_compose(ibuf_pfe, IMSG_SYNC, 0, 0, NULL, 0);
}
void
diff --git a/usr.sbin/relayd/parse.y b/usr.sbin/relayd/parse.y
index b6ce1f815bb..0c6008365b8 100644
--- a/usr.sbin/relayd/parse.y
+++ b/usr.sbin/relayd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.4 2006/12/16 18:05:35 martin Exp $ */
+/* $OpenBSD: parse.y,v 1.5 2006/12/25 18:12:14 reyk Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -140,6 +140,7 @@ varset : STRING '=' STRING {
;
main : INTERVAL number { conf->interval = $2; }
+ | TIMEOUT number { conf->timeout = $2; }
;
service : SERVICE STRING {
@@ -291,7 +292,7 @@ table : TABLE STRING {
YYERROR;
}
tb->id = last_table_id++;
- tb->timeout = CONNECT_TIMEOUT;
+ tb->timeout = conf->timeout;
if (last_table_id == UINT_MAX) {
yyerror("too many tables defined");
YYERROR;
@@ -685,6 +686,7 @@ parse_config(struct hostated *x_conf, const char *filename, int opts)
(void)strlcpy(conf->empty_table.name, "empty",
sizeof(conf->empty_table.name));
+ conf->timeout = CHECK_TIMEOUT;
conf->interval = CHECK_INTERVAL;
conf->opts = opts;
diff --git a/usr.sbin/relayd/relayd.conf.5 b/usr.sbin/relayd/relayd.conf.5
index ea958d0d965..79e7746358e 100644
--- a/usr.sbin/relayd/relayd.conf.5
+++ b/usr.sbin/relayd/relayd.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: relayd.conf.5,v 1.5 2006/12/19 14:39:30 jmc Exp $
+.\" $OpenBSD: relayd.conf.5,v 1.6 2006/12/25 18:12:14 reyk Exp $
.\"
.\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
.\"
@@ -68,10 +68,17 @@ table webhosts {
}
.Ed
.Sh GLOBAL CONFIGURATION
-Only one global setting can be set.
+Here are the settings that can be set globally:
.Pp
.Bl -tag -width Ds -compact
.It Xo
+.Ic timeout Ar number
+.Xc
+Set the global timeout for checks.
+This can be overriden by the timeout value in the table definitions
+and is 200 milliseconds by default.
+.Pp
+.It Xo
.Ic interval Ar number
.Xc
Set the interval in seconds at which the hosts will be checked.
@@ -125,8 +132,9 @@ to hosts.
This parameter is mandatory.
Main and backup tables need to have the same real port.
.It Ic timeout Ar number
-Set the timeout in milliseconds for each host that is checked.
-The default timeout is 200 milliseconds.
+Set the timeout in milliseconds for each host that is checked using
+TCP as the transport.
+This will override the global timeout, which is 200 milliseconds by default.
.El
.Sh SERVICES
Services represent a
diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h
index 2187d838a3f..4ca1519bae2 100644
--- a/usr.sbin/relayd/relayd.h
+++ b/usr.sbin/relayd/relayd.h
@@ -20,7 +20,7 @@
#define PF_SOCKET "/dev/pf"
#define HOSTATED_USER "_hostated"
#define HOSTATED_ANCHOR "hostated"
-#define CONNECT_TIMEOUT 200
+#define CHECK_TIMEOUT 200
#define CHECK_INTERVAL 10
#define EMPTY_TABLE UINT_MAX
#define EMPTY_ID UINT_MAX
@@ -30,7 +30,8 @@
#define MAX_NAME_SIZE 64
#define SRV_MAX_VIRTS 16
-#define READ_BUF_SIZE 65535
+#define SMALL_READ_BUF_SIZE 1024
+#define READ_BUF_SIZE 65535
/* buffer */
struct buf {
@@ -120,6 +121,25 @@ struct ctl_id {
char name[MAX_NAME_SIZE];
};
+struct ctl_icmp_event {
+ struct hostated *env;
+ int icmp_sock;
+ int icmp6_sock;
+ int has_icmp4;
+ int has_icmp6;
+ int last_up;
+ struct event ev;
+ struct timeval tv_start;
+};
+
+struct ctl_tcp_event {
+ int s;
+ struct buf *buf;
+ struct host *host;
+ struct table *table;
+ struct timeval tv_start;
+};
+
struct address {
struct sockaddr_storage ss;
in_port_t port;
@@ -130,8 +150,9 @@ TAILQ_HEAD(addresslist, address);
#define F_DISABLE 0x01
#define F_BACKUP 0x02
+#define F_CHECK_DONE 0x02 /* reused for host */
#define F_USED 0x04
-#define F_ACTIVE_RULESET 0x04
+#define F_ACTIVE_RULESET 0x04 /* reused for service */
#define F_DOWN 0x08
#define F_ADD 0x10
#define F_DEL 0x20
@@ -144,7 +165,9 @@ struct host {
char *tablename;
char name[MAXHOSTNAMELEN];
int up;
+ int last_up;
struct sockaddr_storage ss;
+ struct ctl_tcp_event cte;
TAILQ_ENTRY(host) entry;
};
TAILQ_HEAD(hostlist, host);
@@ -203,10 +226,12 @@ struct hostated {
int icmp6_sock;
int tablecount;
int servicecount;
+ int timeout;
struct table empty_table;
struct event ev;
struct tablelist tables;
struct servicelist services;
+ struct ctl_icmp_event cie;
};
#define HOSTATED_OPT_VERBOSE 0x01
@@ -299,17 +324,17 @@ void flush_rulesets(struct hostated *);
/* hce.c */
pid_t hce(struct hostated *, int [2], int [2], int [2]);
+void hce_notify_done(struct host *, const char *);
/* check_icmp.c */
-int check_icmp(struct host *, int, int, int);
+void schedule_icmp(struct ctl_icmp_event *, struct table *);
+void check_icmp(struct ctl_icmp_event *);
/* check_tcp.c */
-int check_tcp(struct host *, struct table *);
-int tcp_connect(struct host *, struct table *);
+void check_tcp(struct ctl_tcp_event *);
-/* check_tcp.c */
-int check_http_code(struct host *, struct table *);
-int check_http_digest(struct host *, struct table *);
+/* check_http.c */
+void send_http_request(struct ctl_tcp_event *);
/* hostated.c */
struct host *host_find(struct hostated *, objid_t);