GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/dhcrelay/dispatch.c Lines: 0 177 0.0 %
Date: 2017-11-07 Branches: 0 124 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: dispatch.c,v 1.22 2017/07/07 17:25:09 reyk 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/types.h>
43
#include <sys/ioctl.h>
44
#include <sys/socket.h>
45
46
#include <net/if.h>
47
#include <net/if_dl.h>
48
#include <net/if_media.h>
49
#include <net/if_types.h>
50
51
#include <netinet/in.h>
52
#include <netinet/if_ether.h>
53
54
#include <errno.h>
55
#include <ifaddrs.h>
56
#include <limits.h>
57
#include <poll.h>
58
#include <stdlib.h>
59
#include <string.h>
60
#include <syslog.h>
61
#include <time.h>
62
#include <unistd.h>
63
64
#include "dhcp.h"
65
#include "dhcpd.h"
66
#include "log.h"
67
68
/*
69
 * Macros implementation used to generate link-local addresses. This
70
 * code was copied from: sys/netinet6/in6_ifattach.c.
71
 */
72
#define EUI64_UBIT		0x02
73
#define EUI64_TO_IFID(in6) \
74
	do { (in6)->s6_addr[8] ^= EUI64_UBIT; } while (0)
75
76
struct protocol *protocols;
77
struct timeout *timeouts;
78
static struct timeout *free_timeouts;
79
static int interfaces_invalidated;
80
81
void (*bootp_packet_handler)(struct interface_info *,
82
    struct dhcp_packet *, int, struct packet_ctx *);
83
84
static int interface_status(struct interface_info *ifinfo);
85
86
struct interface_info *
87
iflist_getbyname(const char *name)
88
{
89
	struct interface_info	*intf;
90
91
	TAILQ_FOREACH(intf, &intflist, entry) {
92
		if (strcmp(intf->name, name) != 0)
93
			continue;
94
95
		return intf;
96
	}
97
98
	return NULL;
99
}
100
101
void
102
setup_iflist(void)
103
{
104
	struct interface_info		*intf;
105
	struct sockaddr_dl		*sdl;
106
	struct ifaddrs			*ifap, *ifa;
107
	struct if_data			*ifi;
108
	struct sockaddr_in		*sin;
109
	struct sockaddr_in6		*sin6;
110
111
	TAILQ_INIT(&intflist);
112
	if (getifaddrs(&ifap))
113
		fatalx("getifaddrs failed");
114
115
	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
116
		if ((ifa->ifa_flags & IFF_LOOPBACK) ||
117
		    (ifa->ifa_flags & IFF_POINTOPOINT))
118
			continue;
119
120
		/* Find interface or create it. */
121
		intf = iflist_getbyname(ifa->ifa_name);
122
		if (intf == NULL) {
123
			intf = calloc(1, sizeof(*intf));
124
			if (intf == NULL)
125
				fatal("calloc");
126
127
			strlcpy(intf->name, ifa->ifa_name,
128
			    sizeof(intf->name));
129
			TAILQ_INSERT_HEAD(&intflist, intf, entry);
130
		}
131
132
		/* Signal disabled interface. */
133
		if ((ifa->ifa_flags & IFF_UP) == 0)
134
			intf->dead = 1;
135
136
		if (ifa->ifa_addr->sa_family == AF_LINK) {
137
			sdl = (struct sockaddr_dl *)ifa->ifa_addr;
138
			ifi = (struct if_data *)ifa->ifa_data;
139
140
			/* Skip unsupported interfaces. */
141
			if (ifi->ifi_type != IFT_ETHER &&
142
			    ifi->ifi_type != IFT_ENC &&
143
			    ifi->ifi_type != IFT_CARP) {
144
				TAILQ_REMOVE(&intflist, intf, entry);
145
				free(intf);
146
				continue;
147
			}
148
149
			if (ifi->ifi_type == IFT_ENC)
150
				intf->hw_address.htype = HTYPE_IPSEC_TUNNEL;
151
			else
152
				intf->hw_address.htype = HTYPE_ETHER;
153
154
			intf->index = sdl->sdl_index;
155
			intf->hw_address.hlen = sdl->sdl_alen;
156
			memcpy(intf->hw_address.haddr,
157
			    LLADDR(sdl), sdl->sdl_alen);
158
		} else if (ifa->ifa_addr->sa_family == AF_INET) {
159
			sin = (struct sockaddr_in *)ifa->ifa_addr;
160
			if (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK) ||
161
			    intf->primary_address.s_addr != INADDR_ANY)
162
				continue;
163
164
			intf->primary_address = sin->sin_addr;
165
		} else if (ifa->ifa_addr->sa_family == AF_INET6) {
166
			sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
167
			/* Remove the scope from address if link-local. */
168
			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
169
				intf->linklocal = sin6->sin6_addr;
170
				intf->linklocal.s6_addr[2] = 0;
171
				intf->linklocal.s6_addr[3] = 0;
172
			} else
173
				intf->gipv6 = 1;
174
175
			/* At least one IPv6 address was found. */
176
			intf->ipv6 = 1;
177
		}
178
	}
179
180
	freeifaddrs(ifap);
181
182
	/*
183
	 * Generate link-local IPv6 address for interfaces without it.
184
	 *
185
	 * For IPv6 DHCP Relay it doesn't matter what is used for
186
	 * link-addr field, so let's generate an address that won't
187
	 * change during execution so we can always find the interface
188
	 * to relay packets back. This is only used for layer 2 relaying
189
	 * when the interface might not have an address.
190
	 */
191
	TAILQ_FOREACH(intf, &intflist, entry) {
192
		if (memcmp(&intf->linklocal, &in6addr_any,
193
		    sizeof(in6addr_any)) != 0)
194
			continue;
195
196
		intf->linklocal.s6_addr[0] = 0xfe;
197
		intf->linklocal.s6_addr[1] = 0x80;
198
		intf->linklocal.s6_addr[8] = intf->hw_address.haddr[0];
199
		intf->linklocal.s6_addr[9] = intf->hw_address.haddr[1];
200
		intf->linklocal.s6_addr[10] = intf->hw_address.haddr[2];
201
		intf->linklocal.s6_addr[11] = 0xff;
202
		intf->linklocal.s6_addr[12] = 0xfe;
203
		intf->linklocal.s6_addr[13] = intf->hw_address.haddr[3];
204
		intf->linklocal.s6_addr[14] = intf->hw_address.haddr[4];
205
		intf->linklocal.s6_addr[15] = intf->hw_address.haddr[5];
206
		EUI64_TO_IFID(&intf->linklocal);
207
	}
208
}
209
210
struct interface_info *
211
register_interface(const char *ifname, void (*handler)(struct protocol *),
212
    int isserver)
213
{
214
	struct interface_info		*intf;
215
216
	if ((intf = iflist_getbyname(ifname)) == NULL)
217
		return NULL;
218
219
	/* Don't register disabled interfaces. */
220
	if (intf->dead)
221
		return NULL;
222
223
	/* Check if we already registered the interface. */
224
	if (intf->ifr.ifr_name[0] != 0)
225
		return intf;
226
227
	if (strlcpy(intf->ifr.ifr_name, ifname,
228
	    sizeof(intf->ifr.ifr_name)) >= sizeof(intf->ifr.ifr_name))
229
		fatalx("interface name '%s' too long", ifname);
230
231
	if_register_receive(intf, isserver);
232
	if_register_send(intf);
233
	add_protocol(intf->name, intf->rfdesc, handler, intf);
234
235
	return intf;
236
}
237
238
/*
239
 * Wait for packets to come in using poll().  When a packet comes in,
240
 * call receive_packet to receive the packet and possibly strip hardware
241
 * addressing information from it, and then call through the
242
 * bootp_packet_handler hook to try to do something with it.
243
 */
244
void
245
dispatch(void)
246
{
247
	int count, i, to_msec, nfds = 0;
248
	struct protocol *l;
249
	struct pollfd *fds;
250
	time_t howlong;
251
252
	nfds = 0;
253
	for (l = protocols; l; l = l->next)
254
		nfds++;
255
256
	fds = calloc(nfds, sizeof(struct pollfd));
257
	if (fds == NULL)
258
		fatalx("Can't allocate poll structures.");
259
260
	do {
261
		/*
262
		 * Call any expired timeouts, and then if there's still
263
		 * a timeout registered, time out the select call then.
264
		 */
265
another:
266
		if (timeouts) {
267
			if (timeouts->when <= cur_time) {
268
				struct timeout *t = timeouts;
269
270
				timeouts = timeouts->next;
271
				(*(t->func))(t->what);
272
				t->next = free_timeouts;
273
				free_timeouts = t;
274
				goto another;
275
			}
276
277
			/*
278
			 * Figure timeout in milliseconds, and check for
279
			 * potential overflow, so we can cram into an
280
			 * int for poll, while not polling with a
281
			 * negative timeout and blocking indefinitely.
282
			 */
283
			howlong = timeouts->when - cur_time;
284
			if (howlong > INT_MAX / 1000)
285
				howlong = INT_MAX / 1000;
286
			to_msec = howlong * 1000;
287
		} else
288
			to_msec = -1;
289
290
		/* Set up the descriptors to be polled. */
291
		i = 0;
292
293
		for (l = protocols; l; l = l->next) {
294
			struct interface_info *ip = l->local;
295
296
			if (ip && (l->handler != got_one || !ip->dead)) {
297
				fds[i].fd = l->fd;
298
				fds[i].events = POLLIN;
299
				fds[i].revents = 0;
300
				i++;
301
			}
302
		}
303
304
		if (i == 0)
305
			fatalx("No live interfaces to poll on - exiting.");
306
307
		/* Wait for a packet or a timeout... XXX */
308
		count = poll(fds, nfds, to_msec);
309
310
		/* Not likely to be transitory... */
311
		if (count == -1) {
312
			if (errno == EAGAIN || errno == EINTR) {
313
				time(&cur_time);
314
				continue;
315
			}
316
			else
317
				fatal("poll");
318
		}
319
320
		/* Get the current time... */
321
		time(&cur_time);
322
323
		i = 0;
324
		for (l = protocols; l; l = l->next) {
325
			struct interface_info *ip = l->local;
326
327
			if ((fds[i].revents & (POLLIN | POLLHUP))) {
328
				fds[i].revents = 0;
329
				if (ip && (l->handler != got_one ||
330
				    !ip->dead))
331
					(*(l->handler))(l);
332
				if (interfaces_invalidated)
333
					break;
334
			}
335
			i++;
336
		}
337
		interfaces_invalidated = 0;
338
	} while (1);
339
}
340
341
342
void
343
got_one(struct protocol *l)
344
{
345
	struct packet_ctx pc;
346
	ssize_t result;
347
	union {
348
		/*
349
		 * Packet input buffer.  Must be as large as largest
350
		 * possible MTU.
351
		 */
352
		unsigned char packbuf[4095];
353
		struct dhcp_packet packet;
354
	} u;
355
	struct interface_info *ip = l->local;
356
357
	memset(&pc, 0, sizeof(pc));
358
359
	if ((result = receive_packet(ip, u.packbuf, sizeof(u), &pc)) == -1) {
360
		log_warn("receive_packet failed on %s", ip->name);
361
		ip->errors++;
362
		if ((!interface_status(ip)) ||
363
		    (ip->noifmedia && ip->errors > 20)) {
364
			/* our interface has gone away. */
365
			log_warnx("Interface %s no longer appears valid.",
366
			    ip->name);
367
			ip->dead = 1;
368
			interfaces_invalidated = 1;
369
			close(l->fd);
370
			remove_protocol(l);
371
			free(ip);
372
		}
373
		return;
374
	}
375
	if (result == 0)
376
		return;
377
378
	if (bootp_packet_handler)
379
		(*bootp_packet_handler)(ip, &u.packet, result, &pc);
380
}
381
382
int
383
interface_status(struct interface_info *ifinfo)
384
{
385
	char *ifname = ifinfo->name;
386
	int ifsock = ifinfo->rfdesc;
387
	struct ifreq ifr;
388
	struct ifmediareq ifmr;
389
390
	/* get interface flags */
391
	memset(&ifr, 0, sizeof(ifr));
392
	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
393
	if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) == -1) {
394
		log_warn("ioctl(SIOCGIFFLAGS) on %s", ifname);
395
		goto inactive;
396
	}
397
	/*
398
	 * if one of UP and RUNNING flags is dropped,
399
	 * the interface is not active.
400
	 */
401
	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
402
		goto inactive;
403
	}
404
	/* Next, check carrier on the interface, if possible */
405
	if (ifinfo->noifmedia)
406
		goto active;
407
	memset(&ifmr, 0, sizeof(ifmr));
408
	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
409
	if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
410
		if (errno != EINVAL) {
411
			log_debug("ioctl(SIOCGIFMEDIA) on %s", ifname);
412
			ifinfo->noifmedia = 1;
413
			goto active;
414
		}
415
		/*
416
		 * EINVAL (or ENOTTY) simply means that the interface
417
		 * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
418
		 */
419
		ifinfo->noifmedia = 1;
420
		goto active;
421
	}
422
	if (ifmr.ifm_status & IFM_AVALID) {
423
		switch (ifmr.ifm_active & IFM_NMASK) {
424
		case IFM_ETHER:
425
			if (ifmr.ifm_status & IFM_ACTIVE)
426
				goto active;
427
			else
428
				goto inactive;
429
			break;
430
		default:
431
			goto inactive;
432
		}
433
	}
434
inactive:
435
	return (0);
436
active:
437
	return (1);
438
}
439
440
/* Add a protocol to the list of protocols... */
441
void
442
add_protocol(char *name, int fd, void (*handler)(struct protocol *),
443
    void *local)
444
{
445
	struct protocol *p;
446
447
	p = malloc(sizeof(*p));
448
	if (!p)
449
		fatalx("can't allocate protocol struct for %s", name);
450
451
	p->fd = fd;
452
	p->handler = handler;
453
	p->local = local;
454
	p->next = protocols;
455
	protocols = p;
456
}
457
458
void
459
remove_protocol(struct protocol *proto)
460
{
461
	struct protocol *p, *next, *prev;
462
463
	prev = NULL;
464
	for (p = protocols; p; p = next) {
465
		next = p->next;
466
		if (p == proto) {
467
			if (prev)
468
				prev->next = p->next;
469
			else
470
				protocols = p->next;
471
			free(p);
472
		}
473
	}
474
}