diff options
-rw-r--r-- | usr.sbin/hostated/check_http.c | 216 | ||||
-rw-r--r-- | usr.sbin/hostated/check_icmp.c | 377 | ||||
-rw-r--r-- | usr.sbin/hostated/check_tcp.c | 140 | ||||
-rw-r--r-- | usr.sbin/hostated/hce.c | 115 | ||||
-rw-r--r-- | usr.sbin/hostated/hostated.conf.5 | 16 | ||||
-rw-r--r-- | usr.sbin/hostated/hostated.h | 43 | ||||
-rw-r--r-- | usr.sbin/hostated/parse.y | 6 | ||||
-rw-r--r-- | usr.sbin/hoststated/check_http.c | 216 | ||||
-rw-r--r-- | usr.sbin/hoststated/check_icmp.c | 377 | ||||
-rw-r--r-- | usr.sbin/hoststated/check_tcp.c | 140 | ||||
-rw-r--r-- | usr.sbin/hoststated/hce.c | 115 | ||||
-rw-r--r-- | usr.sbin/hoststated/hoststated.conf.5 | 16 | ||||
-rw-r--r-- | usr.sbin/hoststated/hoststated.h | 43 | ||||
-rw-r--r-- | usr.sbin/hoststated/parse.y | 6 | ||||
-rw-r--r-- | usr.sbin/relayd/check_icmp.c | 377 | ||||
-rw-r--r-- | usr.sbin/relayd/check_tcp.c | 140 | ||||
-rw-r--r-- | usr.sbin/relayd/hce.c | 115 | ||||
-rw-r--r-- | usr.sbin/relayd/parse.y | 6 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.conf.5 | 16 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.h | 43 |
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); |