diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2007-01-11 18:05:09 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2007-01-11 18:05:09 +0000 |
commit | 211260efec719d8d4c5d28f6a07d22cc6a993504 (patch) | |
tree | 6eaad27e6675d1a5e413a11ea7b7ee32e5a44c17 /usr.sbin | |
parent | d85c0dc058186c85cfed44b898af95f7d7ec453d (diff) |
use real async events for checks and improve the non-blocking socket
usage. also modify the check_icmp code to use non-blocking raw sockets
and merge the icmp4 and icmp6 functions. some other minor changes
while i'm here.
as discussed with pyr@ claudio@ deraadt@
ok pyr@
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/hoststated/check_http.c | 64 | ||||
-rw-r--r-- | usr.sbin/hoststated/check_icmp.c | 376 | ||||
-rw-r--r-- | usr.sbin/hoststated/check_send_expect.c | 77 | ||||
-rw-r--r-- | usr.sbin/hoststated/check_tcp.c | 25 | ||||
-rw-r--r-- | usr.sbin/hoststated/hce.c | 83 | ||||
-rw-r--r-- | usr.sbin/hoststated/hoststated.c | 23 | ||||
-rw-r--r-- | usr.sbin/hoststated/hoststated.h | 43 | ||||
-rw-r--r-- | usr.sbin/relayd/check_icmp.c | 376 | ||||
-rw-r--r-- | usr.sbin/relayd/check_tcp.c | 25 | ||||
-rw-r--r-- | usr.sbin/relayd/hce.c | 83 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.c | 23 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.h | 43 |
12 files changed, 616 insertions, 625 deletions
diff --git a/usr.sbin/hoststated/check_http.c b/usr.sbin/hoststated/check_http.c index 4e56f321ca9..d53f5b0b3fa 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.8 2007/01/09 14:11:23 reyk Exp $ */ +/* $OpenBSD: check_http.c,v 1.9 2007/01/11 18:05:08 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> * @@ -19,6 +19,7 @@ #include <sys/queue.h> #include <sys/socket.h> #include <sys/param.h> + #include <net/if.h> #include <sha1.h> #include <limits.h> @@ -28,6 +29,7 @@ #include <stdlib.h> #include <unistd.h> #include <fcntl.h> +#include <errno.h> #include "hoststated.h" @@ -98,8 +100,6 @@ 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) { @@ -109,7 +109,14 @@ http_read(int s, short event, void *arg) return; } br = read(s, rbuf, sizeof(rbuf)); - if (br == 0) { + if (br == -1) { + if (errno == EAGAIN || errno == EINTR) + goto retry; + cte->host->up = HOST_DOWN; + buf_free(cte->buf); + hce_notify_done(cte->host, "http_read: read failed"); + return; + } else if (br == 0) { cte->host->up = HOST_DOWN; switch (cte->table->check) { case CHECK_HTTP_CODE: @@ -123,30 +130,24 @@ http_read(int s, short event, void *arg) } 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); - bcopy(&cte->table->timeout, &tv, sizeof(tv)); - if (gettimeofday(&tv_now, NULL)) - fatal("http_read: 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; } + + buf_add(cte->buf, rbuf, br); + + retry: + event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, http_read, + &cte->tv_start, &cte->table->timeout, cte); } void -send_http_request(struct ctl_tcp_event *cte) +send_http_request(int s, short event, void *arg) { - int bs; - int pos; - int len; - char *req; - struct timeval tv; - struct timeval tv_now; + struct ctl_tcp_event *cte = (struct ctl_tcp_event *)arg; + int bs; + int pos; + int len; + char *req; switch (cte->table->check) { case CHECK_HTTP_CODE: @@ -169,7 +170,9 @@ send_http_request(struct ctl_tcp_event *cte) */ do { bs = write(cte->s, req + pos, len); - if (bs <= 0) { + if (bs == -1) { + if (errno == EAGAIN || errno == EINTR) + goto retry; log_warnx("send_http_request: cannot send request"); cte->host->up = HOST_DOWN; hce_notify_done(cte->host, "send_http_request: write"); @@ -183,10 +186,11 @@ send_http_request(struct ctl_tcp_event *cte) if ((cte->buf = buf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) fatalx("send_http_request: cannot create dynamic buffer"); - bcopy(&cte->table->timeout, &tv, sizeof(tv)); - 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); + event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, http_read, + &cte->tv_start, &cte->table->timeout, cte); + return; + + retry: + event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, send_http_request, + &cte->tv_start, &cte->table->timeout, cte); } diff --git a/usr.sbin/hoststated/check_icmp.c b/usr.sbin/hoststated/check_icmp.c index fb7692eac5e..e44c70c2c1b 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.9 2007/01/09 03:32:56 reyk Exp $ */ +/* $OpenBSD: check_icmp.c,v 1.10 2007/01/11 18:05:08 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -33,53 +33,86 @@ #include <unistd.h> #include <string.h> #include <stdlib.h> +#include <err.h> #include "hoststated.h" -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 *); +void icmp_setup(struct hoststated *, struct ctl_icmp_event *, int); +void check_icmp_add(struct ctl_icmp_event *, int, struct timeval *, + void (*)(int, short, void *)); +int icmp_checks_done(struct ctl_icmp_event *); +void icmp_checks_timeout(struct ctl_icmp_event *, const char *); +void send_icmp(int, short, void *); +void recv_icmp(int, short, void *); int in_cksum(u_short *, int); void -schedule_icmp(struct ctl_icmp_event *cie, struct table *table) +icmp_setup(struct hoststated *env, struct ctl_icmp_event *cie, int af) { - struct host *host; + int proto = IPPROTO_ICMP; + + if (af == AF_INET6) + proto = IPPROTO_ICMPV6; + if ((cie->s = socket(af, SOCK_RAW, proto)) < 0) + err(1, "icmp_init: socket"); + session_socket_blockmode(cie->s, BM_NONBLOCK); + cie->env = env; + cie->af = af; +} - 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 +icmp_init(struct hoststated *env) +{ + icmp_setup(env, &env->icmp_send, AF_INET); + icmp_setup(env, &env->icmp_recv, AF_INET); + icmp_setup(env, &env->icmp6_send, AF_INET6); + icmp_setup(env, &env->icmp6_recv, AF_INET6); + env->id = getpid() & 0xffff; } void -check_icmp(struct ctl_icmp_event *cie) +schedule_icmp(struct hoststated *env, struct host *host) { - struct timeval tv; + host->last_up = host->up; + host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE); - if (gettimeofday(&cie->tv_start, NULL)) - fatal("check_icmp: gettimeofday"); + if (((struct sockaddr *)&host->ss)->sa_family == AF_INET) + env->has_icmp = 1; + else + env->has_icmp6 = 1; +} + +void +check_icmp_add(struct ctl_icmp_event *cie, int flags, struct timeval *start, + void (*fn)(int, short, void *)) +{ + struct timeval tv; + + if (start != NULL) + bcopy(start, &cie->tv_start, sizeof(cie->tv_start)); bcopy(&cie->env->timeout, &tv, sizeof(tv)); - if (cie->has_icmp4) - event_once(cie->icmp_sock, EV_READ|EV_TIMEOUT, - recv_icmp4, cie, &tv); - if (cie->has_icmp6) - event_once(cie->icmp6_sock, EV_READ|EV_TIMEOUT, - recv_icmp6, cie, &tv); + if (gettimeofday(&cie->tv_start, NULL)) + fatal("check_icmp_add: gettimeofday"); + event_del(&cie->ev); + event_set(&cie->ev, cie->s, EV_TIMEOUT|flags, fn, cie); + event_add(&cie->ev, &tv); +} + +void +check_icmp(struct hoststated *env, struct timeval *tv) +{ + if (env->has_icmp) { + check_icmp_add(&env->icmp_recv, EV_READ, tv, recv_icmp); + check_icmp_add(&env->icmp_send, EV_WRITE, tv, send_icmp); + } + if (env->has_icmp6) { + check_icmp_add(&env->icmp6_recv, EV_READ, tv, recv_icmp); + check_icmp_add(&env->icmp6_send, EV_WRITE, tv, send_icmp); + } } int -icmp6_checks_done(struct ctl_icmp_event *cie) +icmp_checks_done(struct ctl_icmp_event *cie) { struct table *table; struct host *host; @@ -88,8 +121,7 @@ icmp6_checks_done(struct ctl_icmp_event *cie) 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) + if (((struct sockaddr *)&host->ss)->sa_family != cie->af) continue; if (!(host->flags & F_CHECK_DONE)) return (0); @@ -98,8 +130,8 @@ icmp6_checks_done(struct ctl_icmp_event *cie) return (1); } -int -icmp4_checks_done(struct ctl_icmp_event *cie) +void +icmp_checks_timeout(struct ctl_icmp_event *cie, const char *msg) { struct table *table; struct host *host; @@ -108,220 +140,156 @@ icmp4_checks_done(struct ctl_icmp_event *cie) 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) + if (((struct sockaddr *)&host->ss)->sa_family != cie->af) continue; - if (!(host->flags & F_CHECK_DONE)) { - return (0); - } + if (!(host->flags & F_CHECK_DONE)) + host->up = HOST_DOWN; + hce_notify_done(host, msg); } } - return (1); } void -send_icmp6(struct ctl_icmp_event *cie, struct host *host) +send_icmp(int s, short event, void *arg) { + struct ctl_icmp_event *cie = (struct ctl_icmp_event *)arg; + struct table *table; + struct host *host; struct sockaddr *to; - struct icmp6_hdr *icp; - ssize_t i; + struct icmp *icp; + struct icmp6_hdr *icp6; + ssize_t r; u_char packet[ICMP_BUF_SIZE]; + socklen_t slen; + int i = 0; - cie->has_icmp6 = 1; - to = (struct sockaddr *)&host->ss; - 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 = getpid() & 0xffff; - - memcpy((packet + sizeof(*icp)), &host->id, sizeof(host->id)); - - i = sendto(cie->icmp6_sock, packet, sizeof(packet), 0, to, - sizeof(struct sockaddr_in6)); - if (i < 0 || i != sizeof(packet)) { - host->up = HOST_DOWN; - hce_notify_done(host, "send_icmp6: cannot send"); + if (event == EV_TIMEOUT) { + icmp_checks_timeout(cie, "send_icmp: timeout"); return; } -} - -void -send_icmp4(struct ctl_icmp_event *cie, struct host *host) -{ - struct sockaddr *to; - struct icmp *icp; - ssize_t i; - u_char packet[ICMP_BUF_SIZE]; - cie->has_icmp4 = 1; - to = (struct sockaddr *)&host->ss; bzero(&packet, sizeof(packet)); icp = (struct icmp *)packet; - icp->icmp_type = ICMP_ECHO; - icp->icmp_code = 0; - icp->icmp_seq = htons(1); - icp->icmp_id = htons(getpid() & 0xffff); - icp->icmp_cksum = 0; - - memcpy(icp->icmp_data, &host->id, sizeof(host->id)); - icp->icmp_cksum = in_cksum((u_short *)icp, sizeof(packet)); - - i = sendto(cie->icmp_sock, packet, sizeof(packet), 0, to, - sizeof(struct sockaddr_in)); - if (i < 0 || i != sizeof(packet)) { - host->up = HOST_DOWN; - hce_notify_done(host, "send_icmp4: cannot send"); + icp6 = (struct icmp6_hdr *)packet; + if (cie->af == AF_INET) { + icp->icmp_type = ICMP_ECHO; + icp->icmp_code = 0; + icp->icmp_id = htons(cie->env->id); + icp->icmp_cksum = 0; + slen = sizeof(struct sockaddr_in); + } else { + icp6->icmp6_type = ICMP6_ECHO_REQUEST; + icp6->icmp6_code = 0; + icp6->icmp6_cksum = 0; + icp6->icmp6_id = htons(cie->env->id); + slen = sizeof(struct sockaddr_in6); } -} -void -recv_icmp6(int s, short event, void *arg) -{ - struct ctl_icmp_event *cie = arg; - u_char packet[ICMP_BUF_SIZE]; - 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) + 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 | F_CHECK_SENT)) 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; - } + if (((struct sockaddr *)&host->ss)->sa_family != cie->af) + continue; + i++; + to = (struct sockaddr *)&host->ss; + if (cie->af == AF_INET) { + icp->icmp_seq = htons(i); + icp->icmp_cksum = 0; + memcpy(icp->icmp_data, &host->id, + sizeof(host->id)); + icp->icmp_cksum = in_cksum((u_short *)icp, + sizeof(packet)); + } else { + icp6->icmp6_seq = htons(i); + icp6->icmp6_cksum = 0; + memcpy(packet + sizeof(*icp6), &host->id, + sizeof(host->id)); + icp6->icmp6_cksum = in_cksum((u_short *)icp6, + sizeof(packet)); } + + r = sendto(s, packet, sizeof(packet), 0, to, slen); + if (r == -1) { + if (errno == EAGAIN && errno == EINTR) + goto retry; + host->flags |= F_CHECK_SENT|F_CHECK_DONE; + host->up = HOST_DOWN; + } else if (r != sizeof(packet)) + goto retry; + host->flags |= F_CHECK_SENT; } - return; - } - bzero(&packet, sizeof(packet)); - bzero(&ss, sizeof(ss)); - len = sizeof(struct sockaddr_in6); - i = recvfrom(s, packet, sizeof(packet), 0, (struct sockaddr *)&ss, &len); - if (i < 0 || i != sizeof(packet)) { - 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"); - bcopy(&cie->env->timeout, &tv, sizeof(tv)); - 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); + + return; + + retry: + event_again(&cie->ev, s, EV_TIMEOUT|EV_WRITE, send_icmp, + &cie->tv_start, &cie->env->timeout, cie); } void -recv_icmp4(int s, short event, void *arg) +recv_icmp(int s, short event, void *arg) { - socklen_t len; - struct icmp *icp; - struct ctl_icmp_event *cie = arg; + struct ctl_icmp_event *cie = (struct ctl_icmp_event *)arg; u_char packet[ICMP_BUF_SIZE]; + socklen_t slen; + struct sockaddr_storage ss; + struct icmp *icp; + struct icmp6_hdr *icp6; + u_int16_t icpid; struct host *host; - struct table *table; - ssize_t i; + ssize_t r; 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; - } - } - } + icmp_checks_timeout(cie, NULL); return; } - len = sizeof(struct sockaddr_in); bzero(&packet, sizeof(packet)); bzero(&ss, sizeof(ss)); - i = recvfrom(s, packet, sizeof(packet), 0, (struct sockaddr *)&ss, &len); - if (i < 0 || i != sizeof(packet)) { - log_warn("recv_icmp4: did not receive valid ping"); - return; + + r = recvfrom(s, packet, sizeof(packet), 0, (struct sockaddr *)&ss, &slen); + if (r == -1 || r != ICMP_BUF_SIZE) { + if (r == -1 && errno != EAGAIN && errno != EINTR) + log_debug("recv_icmp: receive error"); + goto retry; } - icp = (struct icmp *)(packet + sizeof(struct ip)); - memcpy(&id, icp->icmp_data, sizeof(id)); + if (cie->af == AF_INET) { + icp = (struct icmp *)(packet + sizeof(struct ip)); + icpid = ntohs(icp->icmp_id); + memcpy(&id, icp->icmp_data, sizeof(id)); + } else { + icp6 = (struct icmp6_hdr *)packet; + icpid = ntohs(icp6->icmp6_id); + memcpy(&id, packet + sizeof(*icp6), sizeof(id)); + } + if (icpid != cie->env->id) + goto retry; host = host_find(cie->env, id); if (host == NULL) { - log_warnx("recv_icmp4: received ping for unknown host"); - return; + log_warn("recv_icmp: ping for unknown host received"); + goto retry; } - if (bcmp(&ss, &host->ss, len)) { - log_warnx("recv_icmp4: forged icmp packet ?"); - return; + if (bcmp(&ss, &host->ss, slen)) { + log_warnx("recv_icmp: forged icmp packet?"); + goto retry; } - 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->up = HOST_UP; host->flags |= F_CHECK_DONE; - if (icmp4_checks_done(cie)) { - hce_notify_done(host, "recv_icmp4: all done"); - return; - } - hce_notify_done(host, "recv_icmp4: host"); + hce_notify_done(host, "recv_icmp: done"); - if (gettimeofday(&tv_now, NULL)) - fatal("recv_icmp4: gettimeofday"); + if (icmp_checks_done(cie)) + return; - bcopy(&cie->env->timeout, &tv, sizeof(tv)); - 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); + retry: + event_again(&cie->ev, s, EV_TIMEOUT|EV_READ, recv_icmp, + &cie->tv_start, &cie->env->timeout, cie); } /* in_cksum from ping.c -- diff --git a/usr.sbin/hoststated/check_send_expect.c b/usr.sbin/hoststated/check_send_expect.c index 528dd8b1fc1..81c9519b567 100644 --- a/usr.sbin/hoststated/check_send_expect.c +++ b/usr.sbin/hoststated/check_send_expect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: check_send_expect.c,v 1.3 2007/01/09 00:45:32 deraadt Exp $ */ +/* $OpenBSD: check_send_expect.c,v 1.4 2007/01/11 18:05:08 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> * @@ -19,6 +19,7 @@ #include <sys/queue.h> #include <sys/socket.h> #include <sys/param.h> + #include <net/if.h> #include <limits.h> #include <event.h> @@ -28,6 +29,7 @@ #include <unistd.h> #include <fcntl.h> #include <fnmatch.h> +#include <errno.h> #include "hoststated.h" @@ -49,7 +51,7 @@ se_validate(struct ctl_tcp_event *cte) if (fnmatch(cte->table->exbuf, cte->buf->buf, 0) == 0) cte->host->up = HOST_UP; else - cte->host->up = HOST_DOWN; + cte->host->up = HOST_UNKNOWN; /* * go back to original position. @@ -62,8 +64,6 @@ se_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) { @@ -72,42 +72,44 @@ se_read(int s, short event, void *arg) hce_notify_done(cte->host, "se_read: timeout"); return; } + br = read(s, rbuf, sizeof(rbuf)); - log_debug("se_read: %d bytes read", br); - if (br == 0) { + if (br == -1) { + if (errno == EAGAIN || errno == EINTR) + goto retry; + cte->host->up = HOST_DOWN; + buf_free(cte->buf); + hce_notify_done(cte->host, "se_read: read failed"); + return; + } else if (br == 0) { cte->host->up = HOST_DOWN; se_validate(cte); buf_free(cte->buf); hce_notify_done(cte->host, "se_read: connection closed"); - } else if (br == -1) { - cte->host->up = HOST_DOWN; + return; + } + + buf_add(cte->buf, rbuf, br); + se_validate(cte); + if (cte->host->up == HOST_UP) { buf_free(cte->buf); - hce_notify_done(cte->host, "se_read: read failed"); - } else { - buf_add(cte->buf, rbuf, br); - bcopy(&cte->table->timeout, &tv, sizeof(tv)); - if (gettimeofday(&tv_now, NULL)) - fatal("se_read: gettimeofday"); - timersub(&tv_now, &cte->tv_start, &tv_now); - timersub(&tv, &tv_now, &tv); - se_validate(cte); - if (cte->host->up == HOST_UP) { - buf_free(cte->buf); - hce_notify_done(cte->host, NULL); - } else - event_once(s, EV_READ|EV_TIMEOUT, se_read, cte, &tv); + hce_notify_done(cte->host, "se_read: done"); + return; } + + retry: + event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, se_read, + &cte->tv_start, &cte->table->timeout, cte); } void -start_send_expect(struct ctl_tcp_event *cte) +start_send_expect(int s, short event, void *arg) { - int bs; - int pos; - int len; - char *req; - struct timeval tv; - struct timeval tv_now; + struct ctl_tcp_event *cte = (struct ctl_tcp_event *)arg; + int bs; + int pos; + int len; + char *req; req = cte->table->sendbuf; pos = 0; @@ -115,7 +117,9 @@ start_send_expect(struct ctl_tcp_event *cte) if (len) { do { bs = write(cte->s, req + pos, len); - if (bs <= 0) { + if (bs == -1) { + if (errno == EAGAIN || errno == EINTR) + goto retry; log_warnx("send_se_data: cannot send"); cte->host->up = HOST_DOWN; hce_notify_done(cte->host, @@ -130,12 +134,11 @@ start_send_expect(struct ctl_tcp_event *cte) if ((cte->buf = buf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) fatalx("send_se_data: cannot create dynamic buffer"); - log_debug("start_send_expect: reading"); + event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, se_read, + &cte->tv_start, &cte->table->timeout, cte); + return; - bcopy(&cte->table->timeout, &tv, sizeof(tv)); - if (gettimeofday(&tv_now, NULL)) - fatal("start_send_expect: gettimeofday"); - timersub(&tv_now, &cte->tv_start, &tv_now); - timersub(&tv, &tv_now, &tv); - event_once(cte->s, EV_READ|EV_TIMEOUT, se_read, cte, &tv); + retry: + event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, start_send_expect, + &cte->tv_start, &cte->table->timeout, cte); } diff --git a/usr.sbin/hoststated/check_tcp.c b/usr.sbin/hoststated/check_tcp.c index 86b8244093f..fc365c18d9d 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.8 2007/01/09 00:45:32 deraadt Exp $ */ +/* $OpenBSD: check_tcp.c,v 1.9 2007/01/11 18:05:08 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -71,17 +71,16 @@ check_tcp(struct ctl_tcp_event *cte) if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) goto bad; + bcopy(&cte->table->timeout, &tv, sizeof(tv)); if (connect(s, (struct sockaddr *)&cte->host->ss, len) == -1) { if (errno != EINPROGRESS) goto bad; - } else { + } else cte->host->up = HOST_UP; - tcp_host_up(s, cte); - return; - } - bcopy(&cte->table->timeout, &tv, sizeof(tv)); - event_once(s, EV_TIMEOUT|EV_WRITE, tcp_write, cte, &tv); + event_set(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_write, cte); + event_add(&cte->ev, &tv); return; + bad: close(s); cte->host->up = HOST_DOWN; @@ -102,7 +101,7 @@ tcp_write(int s, short event, void *arg) len = sizeof(err); if (getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len)) fatal("tcp_write: getsockopt"); - if (err) + if (err != 0) cte->host->up = HOST_DOWN; else cte->host->up = HOST_UP; @@ -123,16 +122,18 @@ tcp_host_up(int s, struct ctl_tcp_event *cte) switch (cte->table->check) { case CHECK_TCP: close(s); - hce_notify_done(cte->host, "tcp_write: success"); + hce_notify_done(cte->host, "tcp_host_up: success"); break; case CHECK_HTTP_CODE: case CHECK_HTTP_DIGEST: - send_http_request(cte); + event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, send_http_request, + &cte->tv_start, &cte->table->timeout, cte); break; case CHECK_SEND_EXPECT: - start_send_expect(cte); + event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, start_send_expect, + &cte->tv_start, &cte->table->timeout, cte); break; default: - fatalx("tcp_write: unhandled check type"); + fatalx("tcp_host_up: unhandled check type"); } } diff --git a/usr.sbin/hoststated/hce.c b/usr.sbin/hoststated/hce.c index c951b5ba8bb..012a5dbf63f 100644 --- a/usr.sbin/hoststated/hce.c +++ b/usr.sbin/hoststated/hce.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hce.c,v 1.9 2007/01/09 13:50:11 pyr Exp $ */ +/* $OpenBSD: hce.c,v 1.10 2007/01/11 18:05:08 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -82,12 +82,6 @@ hce(struct hoststated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2], env = x_env; - /* this is needed for icmp tests */ - if ((env->icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) - err(1, "socket"); - if ((env->icmp6_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) - err(1, "socket"); - if ((pw = getpwnam(HOSTSTATED_USER)) == NULL) fatal("hce: getpwnam"); @@ -99,15 +93,14 @@ hce(struct hoststated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2], setproctitle("host check engine"); hoststated_process = PROC_HCE; + /* this is needed for icmp tests */ + icmp_init(env); + if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 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); @@ -137,11 +130,10 @@ hce(struct hoststated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2], ibuf_main->handler, ibuf_main); event_add(&ibuf_main->ev, NULL); - evtimer_set(&env->ev, hce_launch_checks, NULL); - bcopy(&env->interval, &tv, sizeof(tv)); + evtimer_set(&env->ev, hce_launch_checks, env); + bzero(&tv, sizeof(tv)); evtimer_add(&env->ev, &tv); - hce_launch_checks(0, 0, NULL); event_dispatch(); hce_shutdown(); @@ -154,32 +146,52 @@ hce_launch_checks(int fd, short event, void *arg) { struct host *host; struct table *table; + struct timeval tv; + + log_debug("hce_launch_checks: scheduled"); + + /* + * 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_SENT|F_CHECK_DONE); + event_del(&host->cte.ev); + } + } + + if (gettimeofday(&tv, NULL)) + fatal("hce_launch_checks: gettimeofday"); 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; + if (table->check == CHECK_ICMP) { + schedule_icmp(env, host); + continue; + } + + /* Any other TCP-style checks */ 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"); + bcopy(&tv, &host->cte.tv_start, + sizeof(host->cte.tv_start)); check_tcp(&host->cte); } } - check_icmp(&env->cie); + check_icmp(env, &tv); + + bcopy(&env->interval, &tv, sizeof(tv)); + evtimer_add(&env->ev, &tv); } int @@ -205,35 +217,16 @@ 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; + host->flags |= (F_CHECK_SENT|F_CHECK_DONE); if (msg) - log_debug("hce_notify_done: %s", msg); + log_debug("hce_notify_done: %s (%s)", host->name, 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; - } - bcopy(&env->interval, &tv, sizeof(tv)); - evtimer_add(&env->ev, &tv); - bzero(&st, sizeof(st)); - } } void diff --git a/usr.sbin/hoststated/hoststated.c b/usr.sbin/hoststated/hoststated.c index cbb72231380..771f8c7ddc6 100644 --- a/usr.sbin/hoststated/hoststated.c +++ b/usr.sbin/hoststated/hoststated.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hoststated.c,v 1.9 2007/01/09 13:50:11 pyr Exp $ */ +/* $OpenBSD: hoststated.c,v 1.10 2007/01/11 18:05:08 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -410,3 +410,24 @@ service_findbyname(struct hoststated *env, const char *name) return (service); return (NULL); } + +void +event_again(struct event *ev, int fd, short event, void (*fn)(int, short, void *), + struct timeval *start, struct timeval *end, void *arg) +{ + struct timeval tv_next, tv_now, tv; + + if (gettimeofday(&tv_now, NULL)) + fatal("event_again: gettimeofday"); + + bcopy(end, &tv_next, sizeof(tv_next)); + timersub(&tv_now, start, &tv_now); + timersub(&tv_next, &tv_now, &tv_next); + + bzero(&tv, sizeof(tv)); + if (timercmp(&tv_next, &tv, >)) + bcopy(&tv_next, &tv, sizeof(tv)); + + event_set(ev, fd, event, fn, arg); + event_add(ev, &tv); +} diff --git a/usr.sbin/hoststated/hoststated.h b/usr.sbin/hoststated/hoststated.h index 0102c4a1566..f629ddf266d 100644 --- a/usr.sbin/hoststated/hoststated.h +++ b/usr.sbin/hoststated/hoststated.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hoststated.h,v 1.13 2007/01/09 13:50:11 pyr Exp $ */ +/* $OpenBSD: hoststated.h,v 1.14 2007/01/11 18:05:08 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -126,21 +126,20 @@ struct ctl_id { struct ctl_icmp_event { struct hoststated *env; - int icmp_sock; - int icmp6_sock; - int has_icmp4; - int has_icmp6; + int s; + int af; 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; + int s; + struct buf *buf; + struct host *host; + struct table *table; + struct timeval tv_start; + struct event ev; }; struct address { @@ -156,6 +155,7 @@ TAILQ_HEAD(addresslist, address); #define F_CHECK_DONE 0x02 /* reused for host */ #define F_USED 0x04 #define F_ACTIVE_RULESET 0x04 /* reused for service */ +#define F_CHECK_SENT 0x04 /* reused for host */ #define F_DOWN 0x08 #define F_ADD 0x10 #define F_DEL 0x20 @@ -228,8 +228,6 @@ enum { struct hoststated { u_int8_t opts; struct pfdata *pf; - int icmp_sock; - int icmp6_sock; int tablecount; int servicecount; struct timeval interval; @@ -238,7 +236,14 @@ struct hoststated { struct event ev; struct tablelist tables; struct servicelist services; - struct ctl_icmp_event cie; + u_int16_t id; + + int has_icmp; + int has_icmp6; + struct ctl_icmp_event icmp_send; + struct ctl_icmp_event icmp_recv; + struct ctl_icmp_event icmp6_send; + struct ctl_icmp_event icmp6_recv; }; #define HOSTSTATED_OPT_VERBOSE 0x01 @@ -334,17 +339,18 @@ pid_t hce(struct hoststated *, int [2], int [2], int [2]); void hce_notify_done(struct host *, const char *); /* check_icmp.c */ -void schedule_icmp(struct ctl_icmp_event *, struct table *); -void check_icmp(struct ctl_icmp_event *); +void icmp_init(struct hoststated *); +void schedule_icmp(struct hoststated *, struct host *); +void check_icmp(struct hoststated *, struct timeval *); /* check_tcp.c */ void check_tcp(struct ctl_tcp_event *); /* check_http.c */ -void send_http_request(struct ctl_tcp_event *); +void send_http_request(int, short, void *); /* check_send_expect.c */ -void start_send_expect(struct ctl_tcp_event *); +void start_send_expect(int, short, void *); /* hoststated.c */ struct host *host_find(struct hoststated *, objid_t); @@ -353,3 +359,6 @@ struct service *service_find(struct hoststated *, objid_t); struct host *host_findbyname(struct hoststated *, const char *); struct table *table_findbyname(struct hoststated *, const char *); struct service *service_findbyname(struct hoststated *, const char *); +void event_again(struct event *, int, short, + void (*)(int, short, void *), + struct timeval *, struct timeval *, void *); diff --git a/usr.sbin/relayd/check_icmp.c b/usr.sbin/relayd/check_icmp.c index fb7692eac5e..e44c70c2c1b 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.9 2007/01/09 03:32:56 reyk Exp $ */ +/* $OpenBSD: check_icmp.c,v 1.10 2007/01/11 18:05:08 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -33,53 +33,86 @@ #include <unistd.h> #include <string.h> #include <stdlib.h> +#include <err.h> #include "hoststated.h" -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 *); +void icmp_setup(struct hoststated *, struct ctl_icmp_event *, int); +void check_icmp_add(struct ctl_icmp_event *, int, struct timeval *, + void (*)(int, short, void *)); +int icmp_checks_done(struct ctl_icmp_event *); +void icmp_checks_timeout(struct ctl_icmp_event *, const char *); +void send_icmp(int, short, void *); +void recv_icmp(int, short, void *); int in_cksum(u_short *, int); void -schedule_icmp(struct ctl_icmp_event *cie, struct table *table) +icmp_setup(struct hoststated *env, struct ctl_icmp_event *cie, int af) { - struct host *host; + int proto = IPPROTO_ICMP; + + if (af == AF_INET6) + proto = IPPROTO_ICMPV6; + if ((cie->s = socket(af, SOCK_RAW, proto)) < 0) + err(1, "icmp_init: socket"); + session_socket_blockmode(cie->s, BM_NONBLOCK); + cie->env = env; + cie->af = af; +} - 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 +icmp_init(struct hoststated *env) +{ + icmp_setup(env, &env->icmp_send, AF_INET); + icmp_setup(env, &env->icmp_recv, AF_INET); + icmp_setup(env, &env->icmp6_send, AF_INET6); + icmp_setup(env, &env->icmp6_recv, AF_INET6); + env->id = getpid() & 0xffff; } void -check_icmp(struct ctl_icmp_event *cie) +schedule_icmp(struct hoststated *env, struct host *host) { - struct timeval tv; + host->last_up = host->up; + host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE); - if (gettimeofday(&cie->tv_start, NULL)) - fatal("check_icmp: gettimeofday"); + if (((struct sockaddr *)&host->ss)->sa_family == AF_INET) + env->has_icmp = 1; + else + env->has_icmp6 = 1; +} + +void +check_icmp_add(struct ctl_icmp_event *cie, int flags, struct timeval *start, + void (*fn)(int, short, void *)) +{ + struct timeval tv; + + if (start != NULL) + bcopy(start, &cie->tv_start, sizeof(cie->tv_start)); bcopy(&cie->env->timeout, &tv, sizeof(tv)); - if (cie->has_icmp4) - event_once(cie->icmp_sock, EV_READ|EV_TIMEOUT, - recv_icmp4, cie, &tv); - if (cie->has_icmp6) - event_once(cie->icmp6_sock, EV_READ|EV_TIMEOUT, - recv_icmp6, cie, &tv); + if (gettimeofday(&cie->tv_start, NULL)) + fatal("check_icmp_add: gettimeofday"); + event_del(&cie->ev); + event_set(&cie->ev, cie->s, EV_TIMEOUT|flags, fn, cie); + event_add(&cie->ev, &tv); +} + +void +check_icmp(struct hoststated *env, struct timeval *tv) +{ + if (env->has_icmp) { + check_icmp_add(&env->icmp_recv, EV_READ, tv, recv_icmp); + check_icmp_add(&env->icmp_send, EV_WRITE, tv, send_icmp); + } + if (env->has_icmp6) { + check_icmp_add(&env->icmp6_recv, EV_READ, tv, recv_icmp); + check_icmp_add(&env->icmp6_send, EV_WRITE, tv, send_icmp); + } } int -icmp6_checks_done(struct ctl_icmp_event *cie) +icmp_checks_done(struct ctl_icmp_event *cie) { struct table *table; struct host *host; @@ -88,8 +121,7 @@ icmp6_checks_done(struct ctl_icmp_event *cie) 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) + if (((struct sockaddr *)&host->ss)->sa_family != cie->af) continue; if (!(host->flags & F_CHECK_DONE)) return (0); @@ -98,8 +130,8 @@ icmp6_checks_done(struct ctl_icmp_event *cie) return (1); } -int -icmp4_checks_done(struct ctl_icmp_event *cie) +void +icmp_checks_timeout(struct ctl_icmp_event *cie, const char *msg) { struct table *table; struct host *host; @@ -108,220 +140,156 @@ icmp4_checks_done(struct ctl_icmp_event *cie) 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) + if (((struct sockaddr *)&host->ss)->sa_family != cie->af) continue; - if (!(host->flags & F_CHECK_DONE)) { - return (0); - } + if (!(host->flags & F_CHECK_DONE)) + host->up = HOST_DOWN; + hce_notify_done(host, msg); } } - return (1); } void -send_icmp6(struct ctl_icmp_event *cie, struct host *host) +send_icmp(int s, short event, void *arg) { + struct ctl_icmp_event *cie = (struct ctl_icmp_event *)arg; + struct table *table; + struct host *host; struct sockaddr *to; - struct icmp6_hdr *icp; - ssize_t i; + struct icmp *icp; + struct icmp6_hdr *icp6; + ssize_t r; u_char packet[ICMP_BUF_SIZE]; + socklen_t slen; + int i = 0; - cie->has_icmp6 = 1; - to = (struct sockaddr *)&host->ss; - 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 = getpid() & 0xffff; - - memcpy((packet + sizeof(*icp)), &host->id, sizeof(host->id)); - - i = sendto(cie->icmp6_sock, packet, sizeof(packet), 0, to, - sizeof(struct sockaddr_in6)); - if (i < 0 || i != sizeof(packet)) { - host->up = HOST_DOWN; - hce_notify_done(host, "send_icmp6: cannot send"); + if (event == EV_TIMEOUT) { + icmp_checks_timeout(cie, "send_icmp: timeout"); return; } -} - -void -send_icmp4(struct ctl_icmp_event *cie, struct host *host) -{ - struct sockaddr *to; - struct icmp *icp; - ssize_t i; - u_char packet[ICMP_BUF_SIZE]; - cie->has_icmp4 = 1; - to = (struct sockaddr *)&host->ss; bzero(&packet, sizeof(packet)); icp = (struct icmp *)packet; - icp->icmp_type = ICMP_ECHO; - icp->icmp_code = 0; - icp->icmp_seq = htons(1); - icp->icmp_id = htons(getpid() & 0xffff); - icp->icmp_cksum = 0; - - memcpy(icp->icmp_data, &host->id, sizeof(host->id)); - icp->icmp_cksum = in_cksum((u_short *)icp, sizeof(packet)); - - i = sendto(cie->icmp_sock, packet, sizeof(packet), 0, to, - sizeof(struct sockaddr_in)); - if (i < 0 || i != sizeof(packet)) { - host->up = HOST_DOWN; - hce_notify_done(host, "send_icmp4: cannot send"); + icp6 = (struct icmp6_hdr *)packet; + if (cie->af == AF_INET) { + icp->icmp_type = ICMP_ECHO; + icp->icmp_code = 0; + icp->icmp_id = htons(cie->env->id); + icp->icmp_cksum = 0; + slen = sizeof(struct sockaddr_in); + } else { + icp6->icmp6_type = ICMP6_ECHO_REQUEST; + icp6->icmp6_code = 0; + icp6->icmp6_cksum = 0; + icp6->icmp6_id = htons(cie->env->id); + slen = sizeof(struct sockaddr_in6); } -} -void -recv_icmp6(int s, short event, void *arg) -{ - struct ctl_icmp_event *cie = arg; - u_char packet[ICMP_BUF_SIZE]; - 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) + 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 | F_CHECK_SENT)) 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; - } + if (((struct sockaddr *)&host->ss)->sa_family != cie->af) + continue; + i++; + to = (struct sockaddr *)&host->ss; + if (cie->af == AF_INET) { + icp->icmp_seq = htons(i); + icp->icmp_cksum = 0; + memcpy(icp->icmp_data, &host->id, + sizeof(host->id)); + icp->icmp_cksum = in_cksum((u_short *)icp, + sizeof(packet)); + } else { + icp6->icmp6_seq = htons(i); + icp6->icmp6_cksum = 0; + memcpy(packet + sizeof(*icp6), &host->id, + sizeof(host->id)); + icp6->icmp6_cksum = in_cksum((u_short *)icp6, + sizeof(packet)); } + + r = sendto(s, packet, sizeof(packet), 0, to, slen); + if (r == -1) { + if (errno == EAGAIN && errno == EINTR) + goto retry; + host->flags |= F_CHECK_SENT|F_CHECK_DONE; + host->up = HOST_DOWN; + } else if (r != sizeof(packet)) + goto retry; + host->flags |= F_CHECK_SENT; } - return; - } - bzero(&packet, sizeof(packet)); - bzero(&ss, sizeof(ss)); - len = sizeof(struct sockaddr_in6); - i = recvfrom(s, packet, sizeof(packet), 0, (struct sockaddr *)&ss, &len); - if (i < 0 || i != sizeof(packet)) { - 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"); - bcopy(&cie->env->timeout, &tv, sizeof(tv)); - 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); + + return; + + retry: + event_again(&cie->ev, s, EV_TIMEOUT|EV_WRITE, send_icmp, + &cie->tv_start, &cie->env->timeout, cie); } void -recv_icmp4(int s, short event, void *arg) +recv_icmp(int s, short event, void *arg) { - socklen_t len; - struct icmp *icp; - struct ctl_icmp_event *cie = arg; + struct ctl_icmp_event *cie = (struct ctl_icmp_event *)arg; u_char packet[ICMP_BUF_SIZE]; + socklen_t slen; + struct sockaddr_storage ss; + struct icmp *icp; + struct icmp6_hdr *icp6; + u_int16_t icpid; struct host *host; - struct table *table; - ssize_t i; + ssize_t r; 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; - } - } - } + icmp_checks_timeout(cie, NULL); return; } - len = sizeof(struct sockaddr_in); bzero(&packet, sizeof(packet)); bzero(&ss, sizeof(ss)); - i = recvfrom(s, packet, sizeof(packet), 0, (struct sockaddr *)&ss, &len); - if (i < 0 || i != sizeof(packet)) { - log_warn("recv_icmp4: did not receive valid ping"); - return; + + r = recvfrom(s, packet, sizeof(packet), 0, (struct sockaddr *)&ss, &slen); + if (r == -1 || r != ICMP_BUF_SIZE) { + if (r == -1 && errno != EAGAIN && errno != EINTR) + log_debug("recv_icmp: receive error"); + goto retry; } - icp = (struct icmp *)(packet + sizeof(struct ip)); - memcpy(&id, icp->icmp_data, sizeof(id)); + if (cie->af == AF_INET) { + icp = (struct icmp *)(packet + sizeof(struct ip)); + icpid = ntohs(icp->icmp_id); + memcpy(&id, icp->icmp_data, sizeof(id)); + } else { + icp6 = (struct icmp6_hdr *)packet; + icpid = ntohs(icp6->icmp6_id); + memcpy(&id, packet + sizeof(*icp6), sizeof(id)); + } + if (icpid != cie->env->id) + goto retry; host = host_find(cie->env, id); if (host == NULL) { - log_warnx("recv_icmp4: received ping for unknown host"); - return; + log_warn("recv_icmp: ping for unknown host received"); + goto retry; } - if (bcmp(&ss, &host->ss, len)) { - log_warnx("recv_icmp4: forged icmp packet ?"); - return; + if (bcmp(&ss, &host->ss, slen)) { + log_warnx("recv_icmp: forged icmp packet?"); + goto retry; } - 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->up = HOST_UP; host->flags |= F_CHECK_DONE; - if (icmp4_checks_done(cie)) { - hce_notify_done(host, "recv_icmp4: all done"); - return; - } - hce_notify_done(host, "recv_icmp4: host"); + hce_notify_done(host, "recv_icmp: done"); - if (gettimeofday(&tv_now, NULL)) - fatal("recv_icmp4: gettimeofday"); + if (icmp_checks_done(cie)) + return; - bcopy(&cie->env->timeout, &tv, sizeof(tv)); - 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); + retry: + event_again(&cie->ev, s, EV_TIMEOUT|EV_READ, recv_icmp, + &cie->tv_start, &cie->env->timeout, cie); } /* in_cksum from ping.c -- diff --git a/usr.sbin/relayd/check_tcp.c b/usr.sbin/relayd/check_tcp.c index 86b8244093f..fc365c18d9d 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.8 2007/01/09 00:45:32 deraadt Exp $ */ +/* $OpenBSD: check_tcp.c,v 1.9 2007/01/11 18:05:08 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -71,17 +71,16 @@ check_tcp(struct ctl_tcp_event *cte) if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) goto bad; + bcopy(&cte->table->timeout, &tv, sizeof(tv)); if (connect(s, (struct sockaddr *)&cte->host->ss, len) == -1) { if (errno != EINPROGRESS) goto bad; - } else { + } else cte->host->up = HOST_UP; - tcp_host_up(s, cte); - return; - } - bcopy(&cte->table->timeout, &tv, sizeof(tv)); - event_once(s, EV_TIMEOUT|EV_WRITE, tcp_write, cte, &tv); + event_set(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_write, cte); + event_add(&cte->ev, &tv); return; + bad: close(s); cte->host->up = HOST_DOWN; @@ -102,7 +101,7 @@ tcp_write(int s, short event, void *arg) len = sizeof(err); if (getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len)) fatal("tcp_write: getsockopt"); - if (err) + if (err != 0) cte->host->up = HOST_DOWN; else cte->host->up = HOST_UP; @@ -123,16 +122,18 @@ tcp_host_up(int s, struct ctl_tcp_event *cte) switch (cte->table->check) { case CHECK_TCP: close(s); - hce_notify_done(cte->host, "tcp_write: success"); + hce_notify_done(cte->host, "tcp_host_up: success"); break; case CHECK_HTTP_CODE: case CHECK_HTTP_DIGEST: - send_http_request(cte); + event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, send_http_request, + &cte->tv_start, &cte->table->timeout, cte); break; case CHECK_SEND_EXPECT: - start_send_expect(cte); + event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, start_send_expect, + &cte->tv_start, &cte->table->timeout, cte); break; default: - fatalx("tcp_write: unhandled check type"); + fatalx("tcp_host_up: unhandled check type"); } } diff --git a/usr.sbin/relayd/hce.c b/usr.sbin/relayd/hce.c index c951b5ba8bb..012a5dbf63f 100644 --- a/usr.sbin/relayd/hce.c +++ b/usr.sbin/relayd/hce.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hce.c,v 1.9 2007/01/09 13:50:11 pyr Exp $ */ +/* $OpenBSD: hce.c,v 1.10 2007/01/11 18:05:08 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -82,12 +82,6 @@ hce(struct hoststated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2], env = x_env; - /* this is needed for icmp tests */ - if ((env->icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) - err(1, "socket"); - if ((env->icmp6_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) - err(1, "socket"); - if ((pw = getpwnam(HOSTSTATED_USER)) == NULL) fatal("hce: getpwnam"); @@ -99,15 +93,14 @@ hce(struct hoststated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2], setproctitle("host check engine"); hoststated_process = PROC_HCE; + /* this is needed for icmp tests */ + icmp_init(env); + if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 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); @@ -137,11 +130,10 @@ hce(struct hoststated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2], ibuf_main->handler, ibuf_main); event_add(&ibuf_main->ev, NULL); - evtimer_set(&env->ev, hce_launch_checks, NULL); - bcopy(&env->interval, &tv, sizeof(tv)); + evtimer_set(&env->ev, hce_launch_checks, env); + bzero(&tv, sizeof(tv)); evtimer_add(&env->ev, &tv); - hce_launch_checks(0, 0, NULL); event_dispatch(); hce_shutdown(); @@ -154,32 +146,52 @@ hce_launch_checks(int fd, short event, void *arg) { struct host *host; struct table *table; + struct timeval tv; + + log_debug("hce_launch_checks: scheduled"); + + /* + * 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_SENT|F_CHECK_DONE); + event_del(&host->cte.ev); + } + } + + if (gettimeofday(&tv, NULL)) + fatal("hce_launch_checks: gettimeofday"); 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; + if (table->check == CHECK_ICMP) { + schedule_icmp(env, host); + continue; + } + + /* Any other TCP-style checks */ 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"); + bcopy(&tv, &host->cte.tv_start, + sizeof(host->cte.tv_start)); check_tcp(&host->cte); } } - check_icmp(&env->cie); + check_icmp(env, &tv); + + bcopy(&env->interval, &tv, sizeof(tv)); + evtimer_add(&env->ev, &tv); } int @@ -205,35 +217,16 @@ 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; + host->flags |= (F_CHECK_SENT|F_CHECK_DONE); if (msg) - log_debug("hce_notify_done: %s", msg); + log_debug("hce_notify_done: %s (%s)", host->name, 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; - } - bcopy(&env->interval, &tv, sizeof(tv)); - evtimer_add(&env->ev, &tv); - bzero(&st, sizeof(st)); - } } void diff --git a/usr.sbin/relayd/relayd.c b/usr.sbin/relayd/relayd.c index 1507f4d19fb..2062ad5e03b 100644 --- a/usr.sbin/relayd/relayd.c +++ b/usr.sbin/relayd/relayd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.c,v 1.9 2007/01/09 13:50:11 pyr Exp $ */ +/* $OpenBSD: relayd.c,v 1.10 2007/01/11 18:05:08 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -410,3 +410,24 @@ service_findbyname(struct hoststated *env, const char *name) return (service); return (NULL); } + +void +event_again(struct event *ev, int fd, short event, void (*fn)(int, short, void *), + struct timeval *start, struct timeval *end, void *arg) +{ + struct timeval tv_next, tv_now, tv; + + if (gettimeofday(&tv_now, NULL)) + fatal("event_again: gettimeofday"); + + bcopy(end, &tv_next, sizeof(tv_next)); + timersub(&tv_now, start, &tv_now); + timersub(&tv_next, &tv_now, &tv_next); + + bzero(&tv, sizeof(tv)); + if (timercmp(&tv_next, &tv, >)) + bcopy(&tv_next, &tv, sizeof(tv)); + + event_set(ev, fd, event, fn, arg); + event_add(ev, &tv); +} diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h index a21a3a4bd7f..dc801ea20c9 100644 --- a/usr.sbin/relayd/relayd.h +++ b/usr.sbin/relayd/relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.13 2007/01/09 13:50:11 pyr Exp $ */ +/* $OpenBSD: relayd.h,v 1.14 2007/01/11 18:05:08 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -126,21 +126,20 @@ struct ctl_id { struct ctl_icmp_event { struct hoststated *env; - int icmp_sock; - int icmp6_sock; - int has_icmp4; - int has_icmp6; + int s; + int af; 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; + int s; + struct buf *buf; + struct host *host; + struct table *table; + struct timeval tv_start; + struct event ev; }; struct address { @@ -156,6 +155,7 @@ TAILQ_HEAD(addresslist, address); #define F_CHECK_DONE 0x02 /* reused for host */ #define F_USED 0x04 #define F_ACTIVE_RULESET 0x04 /* reused for service */ +#define F_CHECK_SENT 0x04 /* reused for host */ #define F_DOWN 0x08 #define F_ADD 0x10 #define F_DEL 0x20 @@ -228,8 +228,6 @@ enum { struct hoststated { u_int8_t opts; struct pfdata *pf; - int icmp_sock; - int icmp6_sock; int tablecount; int servicecount; struct timeval interval; @@ -238,7 +236,14 @@ struct hoststated { struct event ev; struct tablelist tables; struct servicelist services; - struct ctl_icmp_event cie; + u_int16_t id; + + int has_icmp; + int has_icmp6; + struct ctl_icmp_event icmp_send; + struct ctl_icmp_event icmp_recv; + struct ctl_icmp_event icmp6_send; + struct ctl_icmp_event icmp6_recv; }; #define HOSTSTATED_OPT_VERBOSE 0x01 @@ -334,17 +339,18 @@ pid_t hce(struct hoststated *, int [2], int [2], int [2]); void hce_notify_done(struct host *, const char *); /* check_icmp.c */ -void schedule_icmp(struct ctl_icmp_event *, struct table *); -void check_icmp(struct ctl_icmp_event *); +void icmp_init(struct hoststated *); +void schedule_icmp(struct hoststated *, struct host *); +void check_icmp(struct hoststated *, struct timeval *); /* check_tcp.c */ void check_tcp(struct ctl_tcp_event *); /* check_http.c */ -void send_http_request(struct ctl_tcp_event *); +void send_http_request(int, short, void *); /* check_send_expect.c */ -void start_send_expect(struct ctl_tcp_event *); +void start_send_expect(int, short, void *); /* hoststated.c */ struct host *host_find(struct hoststated *, objid_t); @@ -353,3 +359,6 @@ struct service *service_find(struct hoststated *, objid_t); struct host *host_findbyname(struct hoststated *, const char *); struct table *table_findbyname(struct hoststated *, const char *); struct service *service_findbyname(struct hoststated *, const char *); +void event_again(struct event *, int, short, + void (*)(int, short, void *), + struct timeval *, struct timeval *, void *); |