summaryrefslogtreecommitdiff
path: root/usr.sbin/hostated/check_icmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/hostated/check_icmp.c')
-rw-r--r--usr.sbin/hostated/check_icmp.c377
1 files changed, 266 insertions, 111 deletions
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 --