diff options
Diffstat (limited to 'usr.sbin/hoststated/check_icmp.c')
-rw-r--r-- | usr.sbin/hoststated/check_icmp.c | 376 |
1 files changed, 172 insertions, 204 deletions
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 -- |