GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/relayd/check_icmp.c Lines: 0 172 0.0 %
Date: 2017-11-13 Branches: 0 107 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: check_icmp.c,v 1.47 2017/07/12 22:57:40 jca 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("%s: socket", __func__);
57
	val = ICMP_RCVBUF_SIZE;
58
	if (setsockopt(cie->s, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) == -1)
59
		fatal("%s: setsockopt", __func__);
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_conf.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
			ttl = host->conf.ttl;
224
			switch(cie->af) {
225
			case AF_INET:
226
				if (ttl > 0) {
227
					if (setsockopt(s, IPPROTO_IP, IP_TTL,
228
					    &ttl, sizeof(ttl)) == -1)
229
						log_warn("%s: setsockopt",
230
						    __func__);
231
				} else {
232
					/* Revert to default TTL */
233
					len = sizeof(ttl);
234
					if (getsockopt(s, IPPROTO_IP,
235
					    IP_IPDEFTTL, &ttl, &len) == 0) {
236
						if (setsockopt(s, IPPROTO_IP,
237
						    IP_TTL, &ttl, len) == -1)
238
							log_warn(
239
							    "%s: setsockopt",
240
							    __func__);
241
					} else
242
						log_warn("%s: getsockopt",
243
						    __func__);
244
				}
245
				break;
246
			case AF_INET6:
247
				if (ttl > 0) {
248
					if (setsockopt(s, IPPROTO_IPV6,
249
					    IPV6_UNICAST_HOPS, &ttl,
250
					    sizeof(ttl)) == -1)
251
						log_warn("%s: setsockopt",
252
						    __func__);
253
				} else {
254
					/* Revert to default hop limit */
255
					ttl = -1;
256
					if (setsockopt(s, IPPROTO_IPV6,
257
					    IPV6_UNICAST_HOPS, &ttl,
258
					    sizeof(ttl)) == -1)
259
						log_warn("%s: setsockopt",
260
						    __func__);
261
				}
262
				break;
263
			}
264
265
			r = sendto(s, packet, sizeof(packet), 0, to, slen);
266
			if (r == -1) {
267
				if (errno == EAGAIN || errno == EINTR)
268
					goto retry;
269
				host->flags |= F_CHECK_SENT|F_CHECK_DONE;
270
				host->up = HOST_DOWN;
271
			} else if (r != sizeof(packet))
272
				goto retry;
273
			host->flags |= F_CHECK_SENT;
274
		}
275
	}
276
277
	return;
278
279
 retry:
280
	event_again(&cie->ev, s, EV_TIMEOUT|EV_WRITE, send_icmp,
281
	    &cie->tv_start, &cie->env->sc_conf.timeout, cie);
282
}
283
284
void
285
recv_icmp(int s, short event, void *arg)
286
{
287
	struct ctl_icmp_event	*cie = arg;
288
	u_char			 packet[ICMP_BUF_SIZE];
289
	socklen_t		 slen;
290
	struct sockaddr_storage	 ss;
291
	struct icmp		*icp;
292
	struct icmp6_hdr	*icp6;
293
	u_int16_t		 icpid;
294
	struct host		*host;
295
	ssize_t			 r;
296
	u_int32_t		 id;
297
298
	if (event == EV_TIMEOUT) {
299
		icmp_checks_timeout(cie, HCE_ICMP_READ_TIMEOUT);
300
		return;
301
	}
302
303
	bzero(&packet, sizeof(packet));
304
	bzero(&ss, sizeof(ss));
305
	slen = sizeof(ss);
306
307
	r = recvfrom(s, packet, sizeof(packet), 0,
308
	    (struct sockaddr *)&ss, &slen);
309
	if (r == -1 || r != ICMP_BUF_SIZE) {
310
		if (r == -1 && errno != EAGAIN && errno != EINTR)
311
			log_debug("%s: receive error", __func__);
312
		goto retry;
313
	}
314
315
	if (cie->af == AF_INET) {
316
		icp = (struct icmp *)(packet + sizeof(struct ip));
317
		icpid = ntohs(icp->icmp_id);
318
		id = icp->icmp_mask;
319
	} else {
320
		icp6 = (struct icmp6_hdr *)packet;
321
		icpid = ntohs(icp6->icmp6_id);
322
		memcpy(&id, packet + sizeof(*icp6), sizeof(id));
323
	}
324
	if (icpid != cie->env->sc_id)
325
		goto retry;
326
	id = ntohl(id);
327
	host = host_find(cie->env, id);
328
	if (host == NULL) {
329
		log_warn("%s: ping for unknown host received", __func__);
330
		goto retry;
331
	}
332
	if (bcmp(&ss, &host->conf.ss, slen)) {
333
		log_warnx("%s: forged icmp packet?", __func__);
334
		goto retry;
335
	}
336
337
	host->up = HOST_UP;
338
	host->flags |= F_CHECK_DONE;
339
	hce_notify_done(host, HCE_ICMP_OK);
340
341
	if (icmp_checks_done(cie))
342
		return;
343
344
 retry:
345
	event_again(&cie->ev, s, EV_TIMEOUT|EV_READ, recv_icmp,
346
	    &cie->tv_start, &cie->env->sc_conf.timeout, cie);
347
}
348
349
/* in_cksum from ping.c --
350
 *	Checksum routine for Internet Protocol family headers (C Version)
351
 *
352
 * Copyright (c) 1989, 1993
353
 *	The Regents of the University of California.  All rights reserved.
354
 *
355
 * This code is derived from software contributed to Berkeley by
356
 * Mike Muuss.
357
 *
358
 * Redistribution and use in source and binary forms, with or without
359
 * modification, are permitted provided that the following conditions
360
 * are met:
361
 * 1. Redistributions of source code must retain the above copyright
362
 *    notice, this list of conditions and the following disclaimer.
363
 * 2. Redistributions in binary form must reproduce the above copyright
364
 *    notice, this list of conditions and the following disclaimer in the
365
 *    documentation and/or other materials provided with the distribution.
366
 * 3. Neither the name of the University nor the names of its contributors
367
 *    may be used to endorse or promote products derived from this software
368
 *    without specific prior written permission.
369
 *
370
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
371
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
372
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
373
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
374
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
375
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
376
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
377
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
378
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
379
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
380
 * SUCH DAMAGE.
381
 */
382
int
383
in_cksum(u_short *addr, int len)
384
{
385
	int nleft = len;
386
	u_short *w = addr;
387
	int sum = 0;
388
	u_short answer = 0;
389
390
	/*
391
	 * Our algorithm is simple, using a 32 bit accumulator (sum), we add
392
	 * sequential 16 bit words to it, and at the end, fold back all the
393
	 * carry bits from the top 16 bits into the lower 16 bits.
394
	 */
395
	while (nleft > 1)  {
396
		sum += *w++;
397
		nleft -= 2;
398
	}
399
400
	/* mop up an odd byte, if necessary */
401
	if (nleft == 1) {
402
		*(u_char *)(&answer) = *(u_char *)w ;
403
		sum += answer;
404
	}
405
406
	/* add back carry outs from top 16 bits to low 16 bits */
407
	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
408
	sum += (sum >> 16);			/* add carry */
409
	answer = ~sum;				/* truncate to 16 bits */
410
411
	return (answer);
412
}