1 /* $OpenBSD: check_icmp.c,v 1.36 2013/03/10 23:32:53 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_setup: socket"); 62 socket_set_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 getmonotime(&cie->tv_start); 99 event_del(&cie->ev); 100 event_set(&cie->ev, cie->s, EV_TIMEOUT|flags, fn, cie); 101 event_add(&cie->ev, &tv); 102 } 103 104 void 105 check_icmp(struct relayd *env, struct timeval *tv) 106 { 107 if (env->sc_has_icmp) { 108 check_icmp_add(&env->sc_icmp_recv, EV_READ, tv, recv_icmp); 109 check_icmp_add(&env->sc_icmp_send, EV_WRITE, tv, send_icmp); 110 } 111 if (env->sc_has_icmp6) { 112 check_icmp_add(&env->sc_icmp6_recv, EV_READ, tv, recv_icmp); 113 check_icmp_add(&env->sc_icmp6_send, EV_WRITE, tv, send_icmp); 114 } 115 } 116 117 int 118 icmp_checks_done(struct ctl_icmp_event *cie) 119 { 120 struct table *table; 121 struct host *host; 122 123 TAILQ_FOREACH(table, cie->env->sc_tables, entry) { 124 if (table->conf.flags & F_DISABLE || 125 table->conf.check != CHECK_ICMP) 126 continue; 127 TAILQ_FOREACH(host, &table->hosts, entry) { 128 if (((struct sockaddr *)&host->conf.ss)->sa_family != 129 cie->af) 130 continue; 131 if (!(host->flags & F_CHECK_DONE)) 132 return (0); 133 } 134 } 135 return (1); 136 } 137 138 void 139 icmp_checks_timeout(struct ctl_icmp_event *cie, enum host_error he) 140 { 141 struct table *table; 142 struct host *host; 143 144 TAILQ_FOREACH(table, cie->env->sc_tables, entry) { 145 if (table->conf.flags & F_DISABLE || 146 table->conf.check != CHECK_ICMP) 147 continue; 148 TAILQ_FOREACH(host, &table->hosts, entry) { 149 if (((struct sockaddr *)&host->conf.ss)->sa_family != 150 cie->af) 151 continue; 152 if (!(host->flags & (F_CHECK_DONE|F_DISABLE))) { 153 host->up = HOST_DOWN; 154 hce_notify_done(host, he); 155 } 156 } 157 } 158 } 159 160 void 161 send_icmp(int s, short event, void *arg) 162 { 163 struct ctl_icmp_event *cie = arg; 164 struct table *table; 165 struct host *host; 166 struct sockaddr *to; 167 struct icmp *icp; 168 struct icmp6_hdr *icp6; 169 ssize_t r; 170 u_char packet[ICMP_BUF_SIZE]; 171 socklen_t slen; 172 int i = 0, ttl, mib[4]; 173 size_t len; 174 u_int32_t id; 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 id = htonl(host->conf.id); 212 213 if (cie->af == AF_INET) { 214 icp->icmp_seq = htons(i); 215 icp->icmp_cksum = 0; 216 icp->icmp_mask = id; 217 icp->icmp_cksum = in_cksum((u_short *)icp, 218 sizeof(packet)); 219 } else { 220 icp6->icmp6_seq = htons(i); 221 icp6->icmp6_cksum = 0; 222 memcpy(packet + sizeof(*icp6), &id, sizeof(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 = 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 u_int32_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 slen = sizeof(ss); 283 284 r = recvfrom(s, packet, sizeof(packet), 0, 285 (struct sockaddr *)&ss, &slen); 286 if (r == -1 || r != ICMP_BUF_SIZE) { 287 if (r == -1 && errno != EAGAIN && errno != EINTR) 288 log_debug("%s: receive error", __func__); 289 goto retry; 290 } 291 292 if (cie->af == AF_INET) { 293 icp = (struct icmp *)(packet + sizeof(struct ip)); 294 icpid = ntohs(icp->icmp_id); 295 id = icp->icmp_mask; 296 } else { 297 icp6 = (struct icmp6_hdr *)packet; 298 icpid = ntohs(icp6->icmp6_id); 299 memcpy(&id, packet + sizeof(*icp6), sizeof(id)); 300 } 301 if (icpid != cie->env->sc_id) 302 goto retry; 303 id = ntohl(id); 304 host = host_find(cie->env, id); 305 if (host == NULL) { 306 log_warn("%s: ping for unknown host received", __func__); 307 goto retry; 308 } 309 if (bcmp(&ss, &host->conf.ss, slen)) { 310 log_warnx("%s: forged icmp packet?", __func__); 311 goto retry; 312 } 313 314 host->up = HOST_UP; 315 host->flags |= F_CHECK_DONE; 316 hce_notify_done(host, HCE_ICMP_OK); 317 318 if (icmp_checks_done(cie)) 319 return; 320 321 retry: 322 event_again(&cie->ev, s, EV_TIMEOUT|EV_READ, recv_icmp, 323 &cie->tv_start, &cie->env->sc_timeout, cie); 324 } 325 326 /* in_cksum from ping.c -- 327 * Checksum routine for Internet Protocol family headers (C Version) 328 * 329 * Copyright (c) 1989, 1993 330 * The Regents of the University of California. All rights reserved. 331 * 332 * This code is derived from software contributed to Berkeley by 333 * Mike Muuss. 334 * 335 * Redistribution and use in source and binary forms, with or without 336 * modification, are permitted provided that the following conditions 337 * are met: 338 * 1. Redistributions of source code must retain the above copyright 339 * notice, this list of conditions and the following disclaimer. 340 * 2. Redistributions in binary form must reproduce the above copyright 341 * notice, this list of conditions and the following disclaimer in the 342 * documentation and/or other materials provided with the distribution. 343 * 3. Neither the name of the University nor the names of its contributors 344 * may be used to endorse or promote products derived from this software 345 * without specific prior written permission. 346 * 347 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 348 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 349 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 350 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 351 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 352 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 353 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 354 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 355 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 356 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 357 * SUCH DAMAGE. 358 */ 359 int 360 in_cksum(u_short *addr, int len) 361 { 362 int nleft = len; 363 u_short *w = addr; 364 int sum = 0; 365 u_short answer = 0; 366 367 /* 368 * Our algorithm is simple, using a 32 bit accumulator (sum), we add 369 * sequential 16 bit words to it, and at the end, fold back all the 370 * carry bits from the top 16 bits into the lower 16 bits. 371 */ 372 while (nleft > 1) { 373 sum += *w++; 374 nleft -= 2; 375 } 376 377 /* mop up an odd byte, if necessary */ 378 if (nleft == 1) { 379 *(u_char *)(&answer) = *(u_char *)w ; 380 sum += answer; 381 } 382 383 /* add back carry outs from top 16 bits to low 16 bits */ 384 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 385 sum += (sum >> 16); /* add carry */ 386 answer = ~sum; /* truncate to 16 bits */ 387 388 return (answer); 389 } 390