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