xref: /openbsd-src/usr.sbin/relayd/check_icmp.c (revision df69c215c7c66baf660f3f65414fd34796c96152)
1*df69c215Sderaadt /*	$OpenBSD: check_icmp.c,v 1.48 2019/06/28 13:32:50 deraadt Exp $	*/
2feb9ff76Sreyk 
3feb9ff76Sreyk /*
436f5dc5eSpyr  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
5feb9ff76Sreyk  *
6feb9ff76Sreyk  * Permission to use, copy, modify, and distribute this software for any
7feb9ff76Sreyk  * purpose with or without fee is hereby granted, provided that the above
8feb9ff76Sreyk  * copyright notice and this permission notice appear in all copies.
9feb9ff76Sreyk  *
10feb9ff76Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11feb9ff76Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12feb9ff76Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13feb9ff76Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14feb9ff76Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15feb9ff76Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16feb9ff76Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17feb9ff76Sreyk  */
18feb9ff76Sreyk 
19feb9ff76Sreyk #include <sys/types.h>
20feb9ff76Sreyk #include <sys/queue.h>
21feb9ff76Sreyk #include <sys/socket.h>
22b7194bbaSreyk #include <sys/sysctl.h>
23f04ff968Sreyk #include <sys/time.h>
240ca734d7Sreyk 
25feb9ff76Sreyk #include <netinet/in.h>
26feb9ff76Sreyk #include <netinet/ip.h>
27feb9ff76Sreyk #include <netinet/ip_icmp.h>
28feb9ff76Sreyk #include <netinet/icmp6.h>
29f04ff968Sreyk #include <arpa/inet.h>
300ca734d7Sreyk 
31feb9ff76Sreyk #include <event.h>
32feb9ff76Sreyk #include <errno.h>
33feb9ff76Sreyk #include <unistd.h>
34feb9ff76Sreyk #include <string.h>
357e351ffdSreyk #include <stdlib.h>
36feb9ff76Sreyk 
37748ceb64Sreyk #include "relayd.h"
38feb9ff76Sreyk 
39748ceb64Sreyk void	icmp_setup(struct relayd *, struct ctl_icmp_event *, int);
4001d85ec5Sreyk void	check_icmp_add(struct ctl_icmp_event *, int, struct timeval *,
4101d85ec5Sreyk 	    void (*)(int, short, void *));
4201d85ec5Sreyk int	icmp_checks_done(struct ctl_icmp_event *);
43c0dc99f6Sreyk void	icmp_checks_timeout(struct ctl_icmp_event *, enum host_error);
4401d85ec5Sreyk void	send_icmp(int, short, void *);
4501d85ec5Sreyk void	recv_icmp(int, short, void *);
46feb9ff76Sreyk int	in_cksum(u_short *, int);
47feb9ff76Sreyk 
487e351ffdSreyk void
icmp_setup(struct relayd * env,struct ctl_icmp_event * cie,int af)49748ceb64Sreyk icmp_setup(struct relayd *env, struct ctl_icmp_event *cie, int af)
50feb9ff76Sreyk {
51403ae8a8Syasuoka 	int proto = IPPROTO_ICMP, val;
527e351ffdSreyk 
5301d85ec5Sreyk 	if (af == AF_INET6)
5401d85ec5Sreyk 		proto = IPPROTO_ICMPV6;
55*df69c215Sderaadt 	if ((cie->s = socket(af, SOCK_RAW | SOCK_NONBLOCK, proto)) == -1)
56efc39811Sbenno 		fatal("%s: socket", __func__);
57403ae8a8Syasuoka 	val = ICMP_RCVBUF_SIZE;
58403ae8a8Syasuoka 	if (setsockopt(cie->s, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) == -1)
59efc39811Sbenno 		fatal("%s: setsockopt", __func__);
6001d85ec5Sreyk 	cie->env = env;
6101d85ec5Sreyk 	cie->af = af;
62feb9ff76Sreyk }
63feb9ff76Sreyk 
647e351ffdSreyk void
icmp_init(struct relayd * env)65748ceb64Sreyk icmp_init(struct relayd *env)
6601d85ec5Sreyk {
6735d10c30Sreyk 	icmp_setup(env, &env->sc_icmp_send, AF_INET);
6835d10c30Sreyk 	icmp_setup(env, &env->sc_icmp_recv, AF_INET);
6935d10c30Sreyk 	icmp_setup(env, &env->sc_icmp6_send, AF_INET6);
7035d10c30Sreyk 	icmp_setup(env, &env->sc_icmp6_recv, AF_INET6);
7135d10c30Sreyk 	env->sc_id = getpid() & 0xffff;
7201d85ec5Sreyk }
7301d85ec5Sreyk 
7401d85ec5Sreyk void
schedule_icmp(struct relayd * env,struct host * host)75748ceb64Sreyk schedule_icmp(struct relayd *env, struct host *host)
7601d85ec5Sreyk {
7701d85ec5Sreyk 	host->last_up = host->up;
7801d85ec5Sreyk 	host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE);
7901d85ec5Sreyk 
8068b79041Spyr 	if (((struct sockaddr *)&host->conf.ss)->sa_family == AF_INET)
8135d10c30Sreyk 		env->sc_has_icmp = 1;
8201d85ec5Sreyk 	else
8335d10c30Sreyk 		env->sc_has_icmp6 = 1;
8401d85ec5Sreyk }
8501d85ec5Sreyk 
8601d85ec5Sreyk void
check_icmp_add(struct ctl_icmp_event * cie,int flags,struct timeval * start,void (* fn)(int,short,void *))8701d85ec5Sreyk check_icmp_add(struct ctl_icmp_event *cie, int flags, struct timeval *start,
8801d85ec5Sreyk     void (*fn)(int, short, void *))
897e351ffdSreyk {
907e351ffdSreyk 	struct timeval	 tv;
917e351ffdSreyk 
9201d85ec5Sreyk 	if (start != NULL)
9301d85ec5Sreyk 		bcopy(start, &cie->tv_start, sizeof(cie->tv_start));
94586b5f8aSreyk 	bcopy(&cie->env->sc_conf.timeout, &tv, sizeof(tv));
95fd1841a3Sreyk 	getmonotime(&cie->tv_start);
9601d85ec5Sreyk 	event_del(&cie->ev);
9701d85ec5Sreyk 	event_set(&cie->ev, cie->s, EV_TIMEOUT|flags, fn, cie);
9801d85ec5Sreyk 	event_add(&cie->ev, &tv);
9901d85ec5Sreyk }
10001d85ec5Sreyk 
10101d85ec5Sreyk void
check_icmp(struct relayd * env,struct timeval * tv)102748ceb64Sreyk check_icmp(struct relayd *env, struct timeval *tv)
10301d85ec5Sreyk {
10435d10c30Sreyk 	if (env->sc_has_icmp) {
10535d10c30Sreyk 		check_icmp_add(&env->sc_icmp_recv, EV_READ, tv, recv_icmp);
10635d10c30Sreyk 		check_icmp_add(&env->sc_icmp_send, EV_WRITE, tv, send_icmp);
10701d85ec5Sreyk 	}
10835d10c30Sreyk 	if (env->sc_has_icmp6) {
10935d10c30Sreyk 		check_icmp_add(&env->sc_icmp6_recv, EV_READ, tv, recv_icmp);
11035d10c30Sreyk 		check_icmp_add(&env->sc_icmp6_send, EV_WRITE, tv, send_icmp);
11101d85ec5Sreyk 	}
1127e351ffdSreyk }
1137e351ffdSreyk 
1147e351ffdSreyk int
icmp_checks_done(struct ctl_icmp_event * cie)11501d85ec5Sreyk icmp_checks_done(struct ctl_icmp_event *cie)
1167e351ffdSreyk {
1177e351ffdSreyk 	struct table	*table;
1187e351ffdSreyk 	struct host	*host;
1197e351ffdSreyk 
12035d10c30Sreyk 	TAILQ_FOREACH(table, cie->env->sc_tables, entry) {
12168b79041Spyr 		if (table->conf.flags & F_DISABLE ||
12268b79041Spyr 		    table->conf.check != CHECK_ICMP)
1237e351ffdSreyk 			continue;
1247e351ffdSreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
12568b79041Spyr 			if (((struct sockaddr *)&host->conf.ss)->sa_family !=
1260cf01c27Spyr 			    cie->af)
1277e351ffdSreyk 				continue;
1287e351ffdSreyk 			if (!(host->flags & F_CHECK_DONE))
1297e351ffdSreyk 				return (0);
1307e351ffdSreyk 		}
1317e351ffdSreyk 	}
1327e351ffdSreyk 	return (1);
1337e351ffdSreyk }
1347e351ffdSreyk 
13501d85ec5Sreyk void
icmp_checks_timeout(struct ctl_icmp_event * cie,enum host_error he)136c0dc99f6Sreyk icmp_checks_timeout(struct ctl_icmp_event *cie, enum host_error he)
1377e351ffdSreyk {
1387e351ffdSreyk 	struct table	*table;
1397e351ffdSreyk 	struct host	*host;
1407e351ffdSreyk 
14135d10c30Sreyk 	TAILQ_FOREACH(table, cie->env->sc_tables, entry) {
14268b79041Spyr 		if (table->conf.flags & F_DISABLE ||
14368b79041Spyr 		    table->conf.check != CHECK_ICMP)
1447e351ffdSreyk 			continue;
1457e351ffdSreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
14668b79041Spyr 			if (((struct sockaddr *)&host->conf.ss)->sa_family !=
1470cf01c27Spyr 			    cie->af)
1487e351ffdSreyk 				continue;
149fa11ebceSreyk 			if (!(host->flags & (F_CHECK_DONE|F_DISABLE))) {
1507e351ffdSreyk 				host->up = HOST_DOWN;
151c0dc99f6Sreyk 				hce_notify_done(host, he);
15201d85ec5Sreyk 			}
1537e351ffdSreyk 		}
154feb9ff76Sreyk 	}
1555b3df522Spyr }
156feb9ff76Sreyk 
1577e351ffdSreyk void
send_icmp(int s,short event,void * arg)15801d85ec5Sreyk send_icmp(int s, short event, void *arg)
159feb9ff76Sreyk {
16048240b8fSbluhm 	struct ctl_icmp_event	*cie = arg;
16101d85ec5Sreyk 	struct table		*table;
16201d85ec5Sreyk 	struct host		*host;
163feb9ff76Sreyk 	struct sockaddr		*to;
164feb9ff76Sreyk 	struct icmp		*icp;
16501d85ec5Sreyk 	struct icmp6_hdr	*icp6;
16601d85ec5Sreyk 	ssize_t			 r;
1675e0350ceSreyk 	u_char			 packet[ICMP_BUF_SIZE];
168210013a3Sbenno 	socklen_t		 slen, len;
169210013a3Sbenno 	int			 i = 0, ttl;
1705921f119Sreyk 	u_int32_t		 id;
171feb9ff76Sreyk 
17201d85ec5Sreyk 	if (event == EV_TIMEOUT) {
173c0dc99f6Sreyk 		icmp_checks_timeout(cie, HCE_ICMP_WRITE_TIMEOUT);
17401d85ec5Sreyk 		return;
17501d85ec5Sreyk 	}
17601d85ec5Sreyk 
177feb9ff76Sreyk 	bzero(&packet, sizeof(packet));
178feb9ff76Sreyk 	icp = (struct icmp *)packet;
17901d85ec5Sreyk 	icp6 = (struct icmp6_hdr *)packet;
18001d85ec5Sreyk 	if (cie->af == AF_INET) {
1817e351ffdSreyk 		icp->icmp_type = ICMP_ECHO;
182feb9ff76Sreyk 		icp->icmp_code = 0;
18335d10c30Sreyk 		icp->icmp_id = htons(cie->env->sc_id);
184feb9ff76Sreyk 		icp->icmp_cksum = 0;
18501d85ec5Sreyk 		slen = sizeof(struct sockaddr_in);
18601d85ec5Sreyk 	} else {
18701d85ec5Sreyk 		icp6->icmp6_type = ICMP6_ECHO_REQUEST;
18801d85ec5Sreyk 		icp6->icmp6_code = 0;
18901d85ec5Sreyk 		icp6->icmp6_cksum = 0;
19035d10c30Sreyk 		icp6->icmp6_id = htons(cie->env->sc_id);
19101d85ec5Sreyk 		slen = sizeof(struct sockaddr_in6);
192feb9ff76Sreyk 	}
193feb9ff76Sreyk 
19435d10c30Sreyk 	TAILQ_FOREACH(table, cie->env->sc_tables, entry) {
19568b79041Spyr 		if (table->conf.check != CHECK_ICMP ||
19668b79041Spyr 		    table->conf.flags & F_DISABLE)
1977e351ffdSreyk 			continue;
1987e351ffdSreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
199c723f8edSreyk 			if (host->flags & (F_DISABLE | F_CHECK_SENT) ||
200c723f8edSreyk 			    host->conf.parentid)
2017e351ffdSreyk 				continue;
20268b79041Spyr 			if (((struct sockaddr *)&host->conf.ss)->sa_family !=
2033b2f90cfSpyr 			    cie->af)
2047e351ffdSreyk 				continue;
20501d85ec5Sreyk 			i++;
20668b79041Spyr 			to = (struct sockaddr *)&host->conf.ss;
2075921f119Sreyk 			id = htonl(host->conf.id);
2085921f119Sreyk 
20901d85ec5Sreyk 			if (cie->af == AF_INET) {
21001d85ec5Sreyk 				icp->icmp_seq = htons(i);
21101d85ec5Sreyk 				icp->icmp_cksum = 0;
2125921f119Sreyk 				icp->icmp_mask = id;
21301d85ec5Sreyk 				icp->icmp_cksum = in_cksum((u_short *)icp,
21401d85ec5Sreyk 				    sizeof(packet));
21501d85ec5Sreyk 			} else {
21601d85ec5Sreyk 				icp6->icmp6_seq = htons(i);
21701d85ec5Sreyk 				icp6->icmp6_cksum = 0;
2185921f119Sreyk 				memcpy(packet + sizeof(*icp6), &id, sizeof(id));
21901d85ec5Sreyk 				icp6->icmp6_cksum = in_cksum((u_short *)icp6,
22001d85ec5Sreyk 				    sizeof(packet));
22101d85ec5Sreyk 			}
22201d85ec5Sreyk 
2236c27a5c2Sjca 			ttl = host->conf.ttl;
22464eb568aSflorian 			switch(cie->af) {
22564eb568aSflorian 			case AF_INET:
2266c27a5c2Sjca 				if (ttl > 0) {
22764eb568aSflorian 					if (setsockopt(s, IPPROTO_IP, IP_TTL,
2286c27a5c2Sjca 					    &ttl, sizeof(ttl)) == -1)
22964eb568aSflorian 						log_warn("%s: setsockopt",
23064eb568aSflorian 						    __func__);
23164eb568aSflorian 				} else {
23259354cb3Sreyk 					/* Revert to default TTL */
233b7194bbaSreyk 					len = sizeof(ttl);
23464eb568aSflorian 					if (getsockopt(s, IPPROTO_IP,
23564eb568aSflorian 					    IP_IPDEFTTL, &ttl, &len) == 0) {
23664eb568aSflorian 						if (setsockopt(s, IPPROTO_IP,
23764eb568aSflorian 						    IP_TTL, &ttl, len) == -1)
23864eb568aSflorian 							log_warn(
23964eb568aSflorian 							    "%s: setsockopt",
24064eb568aSflorian 							    __func__);
24164eb568aSflorian 					} else
24264eb568aSflorian 						log_warn("%s: getsockopt",
24364eb568aSflorian 						    __func__);
24464eb568aSflorian 				}
24564eb568aSflorian 				break;
24664eb568aSflorian 			case AF_INET6:
2476c27a5c2Sjca 				if (ttl > 0) {
24864eb568aSflorian 					if (setsockopt(s, IPPROTO_IPV6,
2496c27a5c2Sjca 					    IPV6_UNICAST_HOPS, &ttl,
2506c27a5c2Sjca 					    sizeof(ttl)) == -1)
25164eb568aSflorian 						log_warn("%s: setsockopt",
25264eb568aSflorian 						    __func__);
25364eb568aSflorian 				} else {
25464eb568aSflorian 					/* Revert to default hop limit */
25564eb568aSflorian 					ttl = -1;
25664eb568aSflorian 					if (setsockopt(s, IPPROTO_IPV6,
25764eb568aSflorian 					    IPV6_UNICAST_HOPS, &ttl,
2586c27a5c2Sjca 					    sizeof(ttl)) == -1)
25964eb568aSflorian 						log_warn("%s: setsockopt",
26064eb568aSflorian 						    __func__);
26164eb568aSflorian 				}
26264eb568aSflorian 				break;
26359354cb3Sreyk 			}
26459354cb3Sreyk 
26501d85ec5Sreyk 			r = sendto(s, packet, sizeof(packet), 0, to, slen);
26601d85ec5Sreyk 			if (r == -1) {
267892e8926Spyr 				if (errno == EAGAIN || errno == EINTR)
26801d85ec5Sreyk 					goto retry;
26901d85ec5Sreyk 				host->flags |= F_CHECK_SENT|F_CHECK_DONE;
2707e351ffdSreyk 				host->up = HOST_DOWN;
27101d85ec5Sreyk 			} else if (r != sizeof(packet))
27201d85ec5Sreyk 				goto retry;
27301d85ec5Sreyk 			host->flags |= F_CHECK_SENT;
2747e351ffdSreyk 		}
2757e351ffdSreyk 	}
27601d85ec5Sreyk 
2777e351ffdSreyk 	return;
27801d85ec5Sreyk 
27901d85ec5Sreyk  retry:
28001d85ec5Sreyk 	event_again(&cie->ev, s, EV_TIMEOUT|EV_WRITE, send_icmp,
281586b5f8aSreyk 	    &cie->tv_start, &cie->env->sc_conf.timeout, cie);
2827e351ffdSreyk }
2837e351ffdSreyk 
2847e351ffdSreyk void
recv_icmp(int s,short event,void * arg)28501d85ec5Sreyk recv_icmp(int s, short event, void *arg)
2867e351ffdSreyk {
28748240b8fSbluhm 	struct ctl_icmp_event	*cie = arg;
28801d85ec5Sreyk 	u_char			 packet[ICMP_BUF_SIZE];
28901d85ec5Sreyk 	socklen_t		 slen;
29001d85ec5Sreyk 	struct sockaddr_storage	 ss;
2917e351ffdSreyk 	struct icmp		*icp;
29201d85ec5Sreyk 	struct icmp6_hdr	*icp6;
29301d85ec5Sreyk 	u_int16_t		 icpid;
2947e351ffdSreyk 	struct host		*host;
29501d85ec5Sreyk 	ssize_t			 r;
2965921f119Sreyk 	u_int32_t		 id;
2977e351ffdSreyk 
2987e351ffdSreyk 	if (event == EV_TIMEOUT) {
299c0dc99f6Sreyk 		icmp_checks_timeout(cie, HCE_ICMP_READ_TIMEOUT);
3007e351ffdSreyk 		return;
3017e351ffdSreyk 	}
3027e351ffdSreyk 
3037e351ffdSreyk 	bzero(&packet, sizeof(packet));
3047e351ffdSreyk 	bzero(&ss, sizeof(ss));
30505bf3f40Sreyk 	slen = sizeof(ss);
30601d85ec5Sreyk 
3073b2f90cfSpyr 	r = recvfrom(s, packet, sizeof(packet), 0,
3083b2f90cfSpyr 	    (struct sockaddr *)&ss, &slen);
30901d85ec5Sreyk 	if (r == -1 || r != ICMP_BUF_SIZE) {
31001d85ec5Sreyk 		if (r == -1 && errno != EAGAIN && errno != EINTR)
31185a8c65fSreyk 			log_debug("%s: receive error", __func__);
31201d85ec5Sreyk 		goto retry;
3137e351ffdSreyk 	}
3147e351ffdSreyk 
31501d85ec5Sreyk 	if (cie->af == AF_INET) {
316feb9ff76Sreyk 		icp = (struct icmp *)(packet + sizeof(struct ip));
31701d85ec5Sreyk 		icpid = ntohs(icp->icmp_id);
3185921f119Sreyk 		id = icp->icmp_mask;
31901d85ec5Sreyk 	} else {
32001d85ec5Sreyk 		icp6 = (struct icmp6_hdr *)packet;
32101d85ec5Sreyk 		icpid = ntohs(icp6->icmp6_id);
32201d85ec5Sreyk 		memcpy(&id, packet + sizeof(*icp6), sizeof(id));
32301d85ec5Sreyk 	}
32435d10c30Sreyk 	if (icpid != cie->env->sc_id)
32501d85ec5Sreyk 		goto retry;
3265921f119Sreyk 	id = ntohl(id);
3277e351ffdSreyk 	host = host_find(cie->env, id);
3287e351ffdSreyk 	if (host == NULL) {
32985a8c65fSreyk 		log_warn("%s: ping for unknown host received", __func__);
33001d85ec5Sreyk 		goto retry;
331feb9ff76Sreyk 	}
33268b79041Spyr 	if (bcmp(&ss, &host->conf.ss, slen)) {
33385a8c65fSreyk 		log_warnx("%s: forged icmp packet?", __func__);
33401d85ec5Sreyk 		goto retry;
335feb9ff76Sreyk 	}
33601d85ec5Sreyk 
3377e351ffdSreyk 	host->up = HOST_UP;
3387e351ffdSreyk 	host->flags |= F_CHECK_DONE;
339c0dc99f6Sreyk 	hce_notify_done(host, HCE_ICMP_OK);
34001d85ec5Sreyk 
34101d85ec5Sreyk 	if (icmp_checks_done(cie))
3427e351ffdSreyk 		return;
3437e351ffdSreyk 
34401d85ec5Sreyk  retry:
34501d85ec5Sreyk 	event_again(&cie->ev, s, EV_TIMEOUT|EV_READ, recv_icmp,
346586b5f8aSreyk 	    &cie->tv_start, &cie->env->sc_conf.timeout, cie);
347feb9ff76Sreyk }
348feb9ff76Sreyk 
349cab47949Sreyk /* in_cksum from ping.c --
350cab47949Sreyk  *	Checksum routine for Internet Protocol family headers (C Version)
351cab47949Sreyk  *
352cab47949Sreyk  * Copyright (c) 1989, 1993
353cab47949Sreyk  *	The Regents of the University of California.  All rights reserved.
354cab47949Sreyk  *
355cab47949Sreyk  * This code is derived from software contributed to Berkeley by
356cab47949Sreyk  * Mike Muuss.
357cab47949Sreyk  *
358cab47949Sreyk  * Redistribution and use in source and binary forms, with or without
359cab47949Sreyk  * modification, are permitted provided that the following conditions
360cab47949Sreyk  * are met:
361cab47949Sreyk  * 1. Redistributions of source code must retain the above copyright
362cab47949Sreyk  *    notice, this list of conditions and the following disclaimer.
363cab47949Sreyk  * 2. Redistributions in binary form must reproduce the above copyright
364cab47949Sreyk  *    notice, this list of conditions and the following disclaimer in the
365cab47949Sreyk  *    documentation and/or other materials provided with the distribution.
366cab47949Sreyk  * 3. Neither the name of the University nor the names of its contributors
367cab47949Sreyk  *    may be used to endorse or promote products derived from this software
368cab47949Sreyk  *    without specific prior written permission.
369cab47949Sreyk  *
370cab47949Sreyk  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
371cab47949Sreyk  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
372cab47949Sreyk  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
373cab47949Sreyk  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
374cab47949Sreyk  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
375cab47949Sreyk  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
376cab47949Sreyk  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
377cab47949Sreyk  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
378cab47949Sreyk  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
379cab47949Sreyk  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
380cab47949Sreyk  * SUCH DAMAGE.
381cab47949Sreyk  */
382feb9ff76Sreyk int
in_cksum(u_short * addr,int len)383feb9ff76Sreyk in_cksum(u_short *addr, int len)
384feb9ff76Sreyk {
385feb9ff76Sreyk 	int nleft = len;
386feb9ff76Sreyk 	u_short *w = addr;
387feb9ff76Sreyk 	int sum = 0;
388feb9ff76Sreyk 	u_short answer = 0;
389feb9ff76Sreyk 
390feb9ff76Sreyk 	/*
391feb9ff76Sreyk 	 * Our algorithm is simple, using a 32 bit accumulator (sum), we add
392feb9ff76Sreyk 	 * sequential 16 bit words to it, and at the end, fold back all the
393feb9ff76Sreyk 	 * carry bits from the top 16 bits into the lower 16 bits.
394feb9ff76Sreyk 	 */
395feb9ff76Sreyk 	while (nleft > 1)  {
396feb9ff76Sreyk 		sum += *w++;
397feb9ff76Sreyk 		nleft -= 2;
398feb9ff76Sreyk 	}
399feb9ff76Sreyk 
400feb9ff76Sreyk 	/* mop up an odd byte, if necessary */
401feb9ff76Sreyk 	if (nleft == 1) {
402feb9ff76Sreyk 		*(u_char *)(&answer) = *(u_char *)w ;
403feb9ff76Sreyk 		sum += answer;
404feb9ff76Sreyk 	}
405feb9ff76Sreyk 
406feb9ff76Sreyk 	/* add back carry outs from top 16 bits to low 16 bits */
407feb9ff76Sreyk 	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
408feb9ff76Sreyk 	sum += (sum >> 16);			/* add carry */
409feb9ff76Sreyk 	answer = ~sum;				/* truncate to 16 bits */
410cab47949Sreyk 
411feb9ff76Sreyk 	return (answer);
412feb9ff76Sreyk }
413