1 /* $OpenBSD: check_icmp.c,v 1.26 2008/12/05 16:37:55 reyk Exp $ */ 2 3 /* 4 * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/queue.h> 21 #include <sys/socket.h> 22 23 #include <net/if.h> 24 #include <netinet/in_systm.h> 25 #include <netinet/in.h> 26 #include <netinet/ip.h> 27 #include <netinet/ip_icmp.h> 28 #include <netinet/icmp6.h> 29 #include <arpa/inet.h> 30 31 #include <limits.h> 32 #include <event.h> 33 #include <errno.h> 34 #include <unistd.h> 35 #include <string.h> 36 #include <stdlib.h> 37 38 #include <openssl/ssl.h> 39 40 #include "relayd.h" 41 42 void icmp_setup(struct relayd *, struct ctl_icmp_event *, int); 43 void check_icmp_add(struct ctl_icmp_event *, int, struct timeval *, 44 void (*)(int, short, void *)); 45 int icmp_checks_done(struct ctl_icmp_event *); 46 void icmp_checks_timeout(struct ctl_icmp_event *, enum host_error); 47 void send_icmp(int, short, void *); 48 void recv_icmp(int, short, void *); 49 int in_cksum(u_short *, int); 50 51 void 52 icmp_setup(struct relayd *env, struct ctl_icmp_event *cie, int af) 53 { 54 int proto = IPPROTO_ICMP; 55 56 if (af == AF_INET6) 57 proto = IPPROTO_ICMPV6; 58 if ((cie->s = socket(af, SOCK_RAW, proto)) < 0) 59 fatal("icmp_init: socket"); 60 session_socket_blockmode(cie->s, BM_NONBLOCK); 61 cie->env = env; 62 cie->af = af; 63 } 64 65 void 66 icmp_init(struct relayd *env) 67 { 68 icmp_setup(env, &env->sc_icmp_send, AF_INET); 69 icmp_setup(env, &env->sc_icmp_recv, AF_INET); 70 icmp_setup(env, &env->sc_icmp6_send, AF_INET6); 71 icmp_setup(env, &env->sc_icmp6_recv, AF_INET6); 72 env->sc_id = getpid() & 0xffff; 73 } 74 75 void 76 schedule_icmp(struct relayd *env, struct host *host) 77 { 78 host->last_up = host->up; 79 host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE); 80 81 if (((struct sockaddr *)&host->conf.ss)->sa_family == AF_INET) 82 env->sc_has_icmp = 1; 83 else 84 env->sc_has_icmp6 = 1; 85 } 86 87 void 88 check_icmp_add(struct ctl_icmp_event *cie, int flags, struct timeval *start, 89 void (*fn)(int, short, void *)) 90 { 91 struct timeval tv; 92 93 if (start != NULL) 94 bcopy(start, &cie->tv_start, sizeof(cie->tv_start)); 95 bcopy(&cie->env->sc_timeout, &tv, sizeof(tv)); 96 if (gettimeofday(&cie->tv_start, NULL) == -1) 97 fatal("check_icmp_add: gettimeofday"); 98 event_del(&cie->ev); 99 event_set(&cie->ev, cie->s, EV_TIMEOUT|flags, fn, cie); 100 event_add(&cie->ev, &tv); 101 } 102 103 void 104 check_icmp(struct relayd *env, struct timeval *tv) 105 { 106 if (env->sc_has_icmp) { 107 check_icmp_add(&env->sc_icmp_recv, EV_READ, tv, recv_icmp); 108 check_icmp_add(&env->sc_icmp_send, EV_WRITE, tv, send_icmp); 109 } 110 if (env->sc_has_icmp6) { 111 check_icmp_add(&env->sc_icmp6_recv, EV_READ, tv, recv_icmp); 112 check_icmp_add(&env->sc_icmp6_send, EV_WRITE, tv, send_icmp); 113 } 114 } 115 116 int 117 icmp_checks_done(struct ctl_icmp_event *cie) 118 { 119 struct table *table; 120 struct host *host; 121 122 TAILQ_FOREACH(table, cie->env->sc_tables, entry) { 123 if (table->conf.flags & F_DISABLE || 124 table->conf.check != CHECK_ICMP) 125 continue; 126 TAILQ_FOREACH(host, &table->hosts, entry) { 127 if (((struct sockaddr *)&host->conf.ss)->sa_family != 128 cie->af) 129 continue; 130 if (!(host->flags & F_CHECK_DONE)) 131 return (0); 132 } 133 } 134 return (1); 135 } 136 137 void 138 icmp_checks_timeout(struct ctl_icmp_event *cie, enum host_error he) 139 { 140 struct table *table; 141 struct host *host; 142 143 TAILQ_FOREACH(table, cie->env->sc_tables, entry) { 144 if (table->conf.flags & F_DISABLE || 145 table->conf.check != CHECK_ICMP) 146 continue; 147 TAILQ_FOREACH(host, &table->hosts, entry) { 148 if (((struct sockaddr *)&host->conf.ss)->sa_family != 149 cie->af) 150 continue; 151 if (!(host->flags & F_CHECK_DONE)) { 152 host->up = HOST_DOWN; 153 hce_notify_done(host, he); 154 } 155 } 156 } 157 } 158 159 void 160 send_icmp(int s, short event, void *arg) 161 { 162 struct ctl_icmp_event *cie = (struct ctl_icmp_event *)arg; 163 struct table *table; 164 struct host *host; 165 struct sockaddr *to; 166 struct icmp *icp; 167 struct icmp6_hdr *icp6; 168 ssize_t r; 169 u_char packet[ICMP_BUF_SIZE]; 170 socklen_t slen; 171 int i = 0; 172 173 if (event == EV_TIMEOUT) { 174 icmp_checks_timeout(cie, HCE_ICMP_WRITE_TIMEOUT); 175 return; 176 } 177 178 bzero(&packet, sizeof(packet)); 179 icp = (struct icmp *)packet; 180 icp6 = (struct icmp6_hdr *)packet; 181 if (cie->af == AF_INET) { 182 icp->icmp_type = ICMP_ECHO; 183 icp->icmp_code = 0; 184 icp->icmp_id = htons(cie->env->sc_id); 185 icp->icmp_cksum = 0; 186 slen = sizeof(struct sockaddr_in); 187 } else { 188 icp6->icmp6_type = ICMP6_ECHO_REQUEST; 189 icp6->icmp6_code = 0; 190 icp6->icmp6_cksum = 0; 191 icp6->icmp6_id = htons(cie->env->sc_id); 192 slen = sizeof(struct sockaddr_in6); 193 } 194 195 TAILQ_FOREACH(table, cie->env->sc_tables, entry) { 196 if (table->conf.check != CHECK_ICMP || 197 table->conf.flags & F_DISABLE) 198 continue; 199 TAILQ_FOREACH(host, &table->hosts, entry) { 200 if (host->flags & (F_DISABLE | F_CHECK_SENT) || 201 host->conf.parentid) 202 continue; 203 if (((struct sockaddr *)&host->conf.ss)->sa_family != 204 cie->af) 205 continue; 206 i++; 207 to = (struct sockaddr *)&host->conf.ss; 208 if (cie->af == AF_INET) { 209 icp->icmp_seq = htons(i); 210 icp->icmp_cksum = 0; 211 memcpy(icp->icmp_data, &host->conf.id, 212 sizeof(host->conf.id)); 213 icp->icmp_cksum = in_cksum((u_short *)icp, 214 sizeof(packet)); 215 } else { 216 icp6->icmp6_seq = htons(i); 217 icp6->icmp6_cksum = 0; 218 memcpy(packet + sizeof(*icp6), &host->conf.id, 219 sizeof(host->conf.id)); 220 icp6->icmp6_cksum = in_cksum((u_short *)icp6, 221 sizeof(packet)); 222 } 223 224 r = sendto(s, packet, sizeof(packet), 0, to, slen); 225 if (r == -1) { 226 if (errno == EAGAIN || errno == EINTR) 227 goto retry; 228 host->flags |= F_CHECK_SENT|F_CHECK_DONE; 229 host->up = HOST_DOWN; 230 } else if (r != sizeof(packet)) 231 goto retry; 232 host->flags |= F_CHECK_SENT; 233 } 234 } 235 236 return; 237 238 retry: 239 event_again(&cie->ev, s, EV_TIMEOUT|EV_WRITE, send_icmp, 240 &cie->tv_start, &cie->env->sc_timeout, cie); 241 } 242 243 void 244 recv_icmp(int s, short event, void *arg) 245 { 246 struct ctl_icmp_event *cie = (struct ctl_icmp_event *)arg; 247 u_char packet[ICMP_BUF_SIZE]; 248 socklen_t slen; 249 struct sockaddr_storage ss; 250 struct icmp *icp; 251 struct icmp6_hdr *icp6; 252 u_int16_t icpid; 253 struct host *host; 254 ssize_t r; 255 objid_t id; 256 257 if (event == EV_TIMEOUT) { 258 icmp_checks_timeout(cie, HCE_ICMP_READ_TIMEOUT); 259 return; 260 } 261 262 bzero(&packet, sizeof(packet)); 263 bzero(&ss, sizeof(ss)); 264 265 r = recvfrom(s, packet, sizeof(packet), 0, 266 (struct sockaddr *)&ss, &slen); 267 if (r == -1 || r != ICMP_BUF_SIZE) { 268 if (r == -1 && errno != EAGAIN && errno != EINTR) 269 log_debug("recv_icmp: receive error"); 270 goto retry; 271 } 272 273 if (cie->af == AF_INET) { 274 icp = (struct icmp *)(packet + sizeof(struct ip)); 275 icpid = ntohs(icp->icmp_id); 276 memcpy(&id, icp->icmp_data, sizeof(id)); 277 } else { 278 icp6 = (struct icmp6_hdr *)packet; 279 icpid = ntohs(icp6->icmp6_id); 280 memcpy(&id, packet + sizeof(*icp6), sizeof(id)); 281 } 282 if (icpid != cie->env->sc_id) 283 goto retry; 284 host = host_find(cie->env, id); 285 if (host == NULL) { 286 log_warn("recv_icmp: ping for unknown host received"); 287 goto retry; 288 } 289 if (bcmp(&ss, &host->conf.ss, slen)) { 290 log_warnx("recv_icmp: forged icmp packet?"); 291 goto retry; 292 } 293 294 host->up = HOST_UP; 295 host->flags |= F_CHECK_DONE; 296 hce_notify_done(host, HCE_ICMP_OK); 297 298 if (icmp_checks_done(cie)) 299 return; 300 301 retry: 302 event_again(&cie->ev, s, EV_TIMEOUT|EV_READ, recv_icmp, 303 &cie->tv_start, &cie->env->sc_timeout, cie); 304 } 305 306 /* in_cksum from ping.c -- 307 * Checksum routine for Internet Protocol family headers (C Version) 308 * 309 * Copyright (c) 1989, 1993 310 * The Regents of the University of California. All rights reserved. 311 * 312 * This code is derived from software contributed to Berkeley by 313 * Mike Muuss. 314 * 315 * Redistribution and use in source and binary forms, with or without 316 * modification, are permitted provided that the following conditions 317 * are met: 318 * 1. Redistributions of source code must retain the above copyright 319 * notice, this list of conditions and the following disclaimer. 320 * 2. Redistributions in binary form must reproduce the above copyright 321 * notice, this list of conditions and the following disclaimer in the 322 * documentation and/or other materials provided with the distribution. 323 * 3. Neither the name of the University nor the names of its contributors 324 * may be used to endorse or promote products derived from this software 325 * without specific prior written permission. 326 * 327 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 328 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 329 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 330 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 331 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 332 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 333 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 334 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 335 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 336 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 337 * SUCH DAMAGE. 338 */ 339 int 340 in_cksum(u_short *addr, int len) 341 { 342 int nleft = len; 343 u_short *w = addr; 344 int sum = 0; 345 u_short answer = 0; 346 347 /* 348 * Our algorithm is simple, using a 32 bit accumulator (sum), we add 349 * sequential 16 bit words to it, and at the end, fold back all the 350 * carry bits from the top 16 bits into the lower 16 bits. 351 */ 352 while (nleft > 1) { 353 sum += *w++; 354 nleft -= 2; 355 } 356 357 /* mop up an odd byte, if necessary */ 358 if (nleft == 1) { 359 *(u_char *)(&answer) = *(u_char *)w ; 360 sum += answer; 361 } 362 363 /* add back carry outs from top 16 bits to low 16 bits */ 364 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 365 sum += (sum >> 16); /* add carry */ 366 answer = ~sum; /* truncate to 16 bits */ 367 368 return (answer); 369 } 370