GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/dhclient/dispatch.c Lines: 0 113 0.0 %
Date: 2017-11-07 Branches: 0 86 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: dispatch.c,v 1.146 2017/09/20 22:05:10 krw Exp $	*/
2
3
/*
4
 * Copyright 2004 Henning Brauer <henning@openbsd.org>
5
 * Copyright (c) 1995, 1996, 1997, 1998, 1999
6
 * The Internet Software Consortium.   All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 *
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 * 3. Neither the name of The Internet Software Consortium nor the names
18
 *    of its contributors may be used to endorse or promote products derived
19
 *    from this software without specific prior written permission.
20
 *
21
 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
22
 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
23
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25
 * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
26
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
29
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33
 * SUCH DAMAGE.
34
 *
35
 * This software has been written for the Internet Software Consortium
36
 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
37
 * Enterprises.  To learn more about the Internet Software Consortium,
38
 * see ``http://www.vix.com/isc''.  To learn more about Vixie
39
 * Enterprises, see ``http://www.vix.com''.
40
 */
41
42
#include <sys/ioctl.h>
43
#include <sys/queue.h>
44
#include <sys/socket.h>
45
#include <sys/types.h>
46
47
#include <net/if.h>
48
#include <net/if_arp.h>
49
#include <net/if_media.h>
50
#include <net/route.h>
51
52
#include <netinet/in.h>
53
#include <netinet/if_ether.h>
54
55
#include <arpa/inet.h>
56
57
#include <errno.h>
58
#include <ifaddrs.h>
59
#include <imsg.h>
60
#include <limits.h>
61
#include <poll.h>
62
#include <signal.h>
63
#include <stdio.h>
64
#include <stdint.h>
65
#include <stdlib.h>
66
#include <string.h>
67
#include <unistd.h>
68
69
#include "dhcp.h"
70
#include "dhcpd.h"
71
#include "log.h"
72
#include "privsep.h"
73
74
75
void packethandler(struct interface_info *ifi);
76
void flush_unpriv_ibuf(void);
77
78
/*
79
 * Loop waiting for packets, timeouts or routing messages.
80
 */
81
void
82
dispatch(struct interface_info *ifi, int routefd)
83
{
84
	struct pollfd		 fds[3];
85
	void			(*func)(struct interface_info *);
86
	time_t			 cur_time, howlong;
87
	int			 nfds, to_msec;
88
89
	while (quit == 0 || quit == SIGHUP) {
90
		if (quit == SIGHUP) {
91
			sendhup();
92
			to_msec = 100;
93
		} else if (ifi->timeout_func != NULL) {
94
			time(&cur_time);
95
			if (ifi->timeout <= cur_time) {
96
				func = ifi->timeout_func;
97
				cancel_timeout(ifi);
98
				(*(func))(ifi);
99
				continue;
100
			}
101
			/*
102
			 * Figure timeout in milliseconds, and check for
103
			 * potential overflow, so we can cram into an
104
			 * int for poll, while not polling with a
105
			 * negative timeout and blocking indefinitely.
106
			 */
107
			howlong = ifi->timeout - cur_time;
108
			if (howlong > INT_MAX / 1000)
109
				howlong = INT_MAX / 1000;
110
			to_msec = howlong * 1000;
111
		} else
112
			to_msec = -1;
113
114
		/*
115
		 * Set up the descriptors to be polled.
116
		 *
117
		 *  fds[0] == bpf socket for incoming packets
118
		 *  fds[1] == routing socket for incoming RTM messages
119
		 *  fds[2] == imsg socket to privileged process
120
		 */
121
		fds[0].fd = ifi->bfdesc;
122
		fds[1].fd = routefd;
123
		fds[2].fd = unpriv_ibuf->fd;
124
		fds[0].events = fds[1].events = fds[2].events = POLLIN;
125
126
		if (unpriv_ibuf->w.queued)
127
			fds[2].events |= POLLOUT;
128
129
		nfds = poll(fds, 3, to_msec);
130
		if (nfds == -1) {
131
			if (errno == EINTR)
132
				continue;
133
			log_warn("%s: poll(bfdesc, routefd, unpriv_ibuf)",
134
			    log_procname);
135
			quit = INTERNALSIG;
136
			continue;
137
		}
138
139
		if ((fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) != 0) {
140
			log_warnx("%s: bfdesc: ERR|HUP|NVAL", log_procname);
141
			quit = INTERNALSIG;
142
			continue;
143
		}
144
		if ((fds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) != 0) {
145
			log_warnx("%s: routefd: ERR|HUP|NVAL", log_procname);
146
			quit = INTERNALSIG;
147
			continue;
148
		}
149
		if ((fds[2].revents & (POLLERR | POLLHUP | POLLNVAL)) != 0) {
150
			log_warnx("%s: unpriv_ibuf: ERR|HUP|NVAL", log_procname);
151
			quit = INTERNALSIG;
152
			continue;
153
		}
154
155
		if (nfds == 0)
156
			continue;
157
158
		if ((fds[0].revents & POLLIN) != 0) {
159
			do {
160
				packethandler(ifi);
161
			} while (ifi->rbuf_offset < ifi->rbuf_len);
162
		}
163
		if ((fds[1].revents & POLLIN) != 0)
164
			routehandler(ifi, routefd);
165
		if ((fds[2].revents & POLLOUT) != 0)
166
			flush_unpriv_ibuf();
167
		if ((fds[2].revents & POLLIN) != 0)
168
			quit = INTERNALSIG;
169
	}
170
171
	if (quit != INTERNALSIG && quit != SIGHUP)
172
		fatalx("%s", strsignal(quit));
173
}
174
175
void
176
packethandler(struct interface_info *ifi)
177
{
178
	struct sockaddr_in	 from;
179
	struct ether_addr	 hfrom;
180
	struct in_addr		 ifrom;
181
	struct dhcp_packet	*packet = &ifi->recv_packet;
182
	struct reject_elem	*ap;
183
	struct option_data	*options;
184
	char			*type, *info;
185
	ssize_t			 result;
186
	void			(*handler)(struct interface_info *,
187
	    struct option_data *, char *);
188
	int			 i, rslt;
189
190
	result = receive_packet(ifi, &from, &hfrom);
191
	if (result == -1) {
192
		ifi->errors++;
193
		if (ifi->errors > 20)
194
			fatalx("too many receive_packet failures");
195
		return;
196
	}
197
	ifi->errors = 0;
198
199
	if (result == 0)
200
		return;
201
202
	ifrom.s_addr = from.sin_addr.s_addr;
203
204
	if (packet->hlen != ETHER_ADDR_LEN) {
205
#ifdef DEBUG
206
		log_debug("%s: discarding packet with hlen == %u", log_procname,
207
		    packet->hlen);
208
#endif	/* DEBUG */
209
		return;
210
	} else if (memcmp(&ifi->hw_address, packet->chaddr,
211
	    sizeof(ifi->hw_address))) {
212
#ifdef DEBUG
213
		log_debug("%s: discarding packet with chaddr == %s",
214
		    log_procname,
215
		    ether_ntoa((struct ether_addr *)packet->chaddr));
216
#endif	/* DEBUG */
217
		return;
218
	}
219
220
	if (ifi->xid != packet->xid) {
221
#ifdef DEBUG
222
		log_debug("%s: discarding packet with XID != %u (%u)",
223
		    log_procname, ifi->xid, packet->xid);
224
#endif	/* DEBUG */
225
		return;
226
	}
227
228
	TAILQ_FOREACH(ap, &config->reject_list, next)
229
	    if (ifrom.s_addr == ap->addr.s_addr) {
230
#ifdef DEBUG
231
		    log_debug("%s: discarding packet from address on reject "
232
			"list (%s)", log_procname, inet_ntoa(ifrom));
233
#endif	/* DEBUG */
234
		    return;
235
	    }
236
237
	options = unpack_options(&ifi->recv_packet);
238
239
	/*
240
	 * RFC 6842 says if the server sends a client identifier
241
	 * that doesn't match then the packet must be dropped.
242
	 */
243
	i = DHO_DHCP_CLIENT_IDENTIFIER;
244
	if ((options[i].len != 0) &&
245
	    ((options[i].len != config->send_options[i].len) ||
246
	    memcmp(options[i].data, config->send_options[i].data,
247
	    options[i].len) != 0)) {
248
#ifdef DEBUG
249
		log_debug("%s: discarding packet with client-identifier %s'",
250
		    log_procname, pretty_print_option(i, &options[i], 0));
251
#endif	/* DEBUG */
252
		return;
253
	}
254
255
	type = "<unknown>";
256
	handler = NULL;
257
258
	i = DHO_DHCP_MESSAGE_TYPE;
259
	if (options[i].data != NULL) {
260
		/* Always try a DHCP packet, even if a bad option was seen. */
261
		switch (options[i].data[0]) {
262
		case DHCPOFFER:
263
			handler = dhcpoffer;
264
			type = "DHCPOFFER";
265
			break;
266
		case DHCPNAK:
267
			handler = dhcpnak;
268
			type = "DHCPNACK";
269
			break;
270
		case DHCPACK:
271
			handler = dhcpack;
272
			type = "DHCPACK";
273
			break;
274
		default:
275
#ifdef DEBUG
276
			log_debug("%s: discarding DHCP packet of unknown type "
277
			    "(%d)", log_procname, options[i].data[0]);
278
#endif	/* DEBUG */
279
			return;
280
		}
281
	} else if (packet->op == BOOTREPLY) {
282
		handler = dhcpoffer;
283
		type = "BOOTREPLY";
284
	} else {
285
#ifdef DEBUG
286
		log_debug("%s: discarding packet which is neither DHCP nor "
287
		    "BOOTP", log_procname);
288
#endif	/* DEBUG */
289
		return;
290
	}
291
292
	rslt = asprintf(&info, "%s from %s (%s)", type, inet_ntoa(ifrom),
293
	    ether_ntoa(&hfrom));
294
	if (rslt == -1)
295
		fatal("info string");
296
297
	if (handler != NULL)
298
		(*handler)(ifi, options, info);
299
300
	free(info);
301
}
302
303
/*
304
 * flush_unpriv_ibuf stuffs queued messages into the imsg socket.
305
 */
306
void
307
flush_unpriv_ibuf(void)
308
{
309
	while (unpriv_ibuf->w.queued) {
310
		if (msgbuf_write(&unpriv_ibuf->w) <= 0) {
311
			if (errno == EAGAIN)
312
				break;
313
			if (quit == 0)
314
				quit = INTERNALSIG;
315
			if (errno != EPIPE && errno != 0)
316
				log_warn("%s: msgbuf_write(unpriv_ibuf)",
317
				    log_procname);
318
			break;
319
		}
320
	}
321
}
322
323
void
324
set_timeout(struct interface_info *ifi, time_t secs,
325
    void (*where)(struct interface_info *))
326
{
327
	time(&ifi->timeout);
328
	ifi->timeout += secs;
329
	ifi->timeout_func = where;
330
}
331
332
void
333
cancel_timeout(struct interface_info *ifi)
334
{
335
	ifi->timeout = 0;
336
	ifi->timeout_func = NULL;
337
}
338
339
/*
340
 * Inform the [priv] process a HUP was received.
341
 */
342
void
343
sendhup(void)
344
{
345
	int rslt;
346
347
	rslt = imsg_compose(unpriv_ibuf, IMSG_HUP, 0, 0, -1, NULL, 0);
348
	if (rslt == -1)
349
		log_warn("%s: imsg_compose(IMSG_HUP)", log_procname);
350
}