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