dispatch.c revision 1.37
1/*	$OpenBSD: dispatch.c,v 1.37 2016/08/27 01:26:22 guenther Exp $ */
2
3/*
4 * Copyright (c) 1995, 1996, 1997, 1998, 1999
5 * The Internet Software Consortium.   All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of The Internet Software Consortium nor the names
17 *    of its contributors may be used to endorse or promote products derived
18 *    from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
21 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
22 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * This software has been written for the Internet Software Consortium
35 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
36 * Enterprises.  To learn more about the Internet Software Consortium,
37 * see ``http://www.vix.com/isc''.  To learn more about Vixie
38 * Enterprises, see ``http://www.vix.com''.
39 */
40
41#include <sys/types.h>
42#include <sys/ioctl.h>
43#include <sys/socket.h>
44
45#include <arpa/inet.h>
46
47#include <net/if.h>
48#include <net/if_dl.h>
49#include <net/if_media.h>
50
51#include <netinet/in.h>
52
53#include <errno.h>
54#include <ifaddrs.h>
55#include <limits.h>
56#include <poll.h>
57#include <stdio.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 "tree.h"
66#include "dhcpd.h"
67#include "sync.h"
68
69extern int syncfd;
70
71struct interface_info *interfaces;
72struct protocol *protocols;
73struct dhcpd_timeout *timeouts;
74static struct dhcpd_timeout *free_timeouts;
75static int interfaces_invalidated;
76
77static int interface_status(struct interface_info *ifinfo);
78int get_rdomain(char *);
79
80/* Use getifaddrs() to get a list of all the attached interfaces.
81   For each interface that's of type INET and not the loopback interface,
82   register that interface with the network I/O software, figure out what
83   subnet it's on, and add it to the list of interfaces. */
84
85void
86discover_interfaces(int *rdomain)
87{
88	struct interface_info *tmp;
89	struct interface_info *last, *next;
90	struct subnet *subnet;
91	struct shared_network *share;
92	struct sockaddr_in foo;
93	int ir = 0, ird;
94	struct ifreq *tif;
95	struct ifaddrs *ifap, *ifa;
96
97	if (getifaddrs(&ifap) != 0)
98		error("getifaddrs failed");
99
100	/*
101	 * If we already have a list of interfaces, the interfaces were
102	 * requested.
103	 */
104	if (interfaces != NULL)
105		ir = 1;
106	else
107		/* must specify an interface when rdomains are used */
108		*rdomain = 0;
109
110	/* Cycle through the list of interfaces looking for IP addresses. */
111	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
112		/*
113		 * See if this is the sort of interface we want to
114		 * deal with.  Skip loopback, point-to-point and down
115		 * interfaces, except don't skip down interfaces if we're
116		 * trying to get a list of configurable interfaces.
117		 */
118		if ((ifa->ifa_flags & IFF_LOOPBACK) ||
119		    (ifa->ifa_flags & IFF_POINTOPOINT) ||
120		    (!(ifa->ifa_flags & IFF_UP)) ||
121		    (!(ifa->ifa_flags & IFF_BROADCAST)))
122			continue;
123
124		/* See if we've seen an interface that matches this one. */
125		for (tmp = interfaces; tmp; tmp = tmp->next)
126			if (!strcmp(tmp->name, ifa->ifa_name))
127				break;
128
129		/* If we are looking for specific interfaces, ignore others. */
130		if (tmp == NULL && ir)
131			continue;
132
133		ird = get_rdomain(ifa->ifa_name);
134		if (*rdomain == -1)
135			*rdomain = ird;
136		else if (*rdomain != ird && ir)
137			error("Interface %s is not in rdomain %d",
138			    tmp->name, *rdomain);
139		else if (*rdomain != ird && !ir)
140			continue;
141
142		/* If there isn't already an interface by this name,
143		   allocate one. */
144		if (tmp == NULL) {
145			tmp = calloc(1, sizeof *tmp);
146			if (!tmp)
147				error("Insufficient memory to %s %s",
148				    "record interface", ifa->ifa_name);
149			strlcpy(tmp->name, ifa->ifa_name, sizeof(tmp->name));
150			tmp->next = interfaces;
151			tmp->noifmedia = tmp->dead = tmp->errors = 0;
152			interfaces = tmp;
153		}
154
155		/* If we have the capability, extract link information
156		   and record it in a linked list. */
157		if (ifa->ifa_addr->sa_family == AF_LINK) {
158			struct sockaddr_dl *foo =
159			    ((struct sockaddr_dl *)(ifa->ifa_addr));
160			tmp->index = foo->sdl_index;
161			tmp->hw_address.hlen = foo->sdl_alen;
162			tmp->hw_address.htype = HTYPE_ETHER; /* XXX */
163			memcpy(tmp->hw_address.haddr,
164			    LLADDR(foo), foo->sdl_alen);
165		} else if (ifa->ifa_addr->sa_family == AF_INET) {
166			struct iaddr addr;
167
168			/* Get a pointer to the address... */
169			memcpy(&foo, ifa->ifa_addr, sizeof(foo));
170
171			/* We don't want the loopback interface. */
172			if (foo.sin_addr.s_addr == htonl (INADDR_LOOPBACK))
173				continue;
174
175			/* If this is the first real IP address we've
176			   found, keep a pointer to ifreq structure in
177			   which we found it. */
178			if (!tmp->ifp) {
179				int len = (IFNAMSIZ + ifa->ifa_addr->sa_len);
180				tif = malloc(len);
181				if (!tif)
182					error("no space to remember ifp.");
183				strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ);
184				memcpy(&tif->ifr_addr, ifa->ifa_addr,
185				    ifa->ifa_addr->sa_len);
186				tmp->ifp = tif;
187				tmp->primary_address = foo.sin_addr;
188			}
189
190			/* Grab the address... */
191			addr.len = 4;
192			memcpy(addr.iabuf, &foo.sin_addr.s_addr, addr.len);
193
194			/* If there's a registered subnet for this address,
195			   connect it together... */
196			if ((subnet = find_subnet(addr))) {
197				/* If this interface has multiple aliases
198				   on the same subnet, ignore all but the
199				   first we encounter. */
200				if (!subnet->interface) {
201					subnet->interface = tmp;
202					subnet->interface_address = addr;
203				} else if (subnet->interface != tmp) {
204					warning("Multiple %s %s: %s %s",
205					    "interfaces match the",
206					    "same subnet",
207					    subnet->interface->name,
208					    tmp->name);
209				}
210				share = subnet->shared_network;
211				if (tmp->shared_network &&
212				    tmp->shared_network != share) {
213					warning("Interface %s matches %s",
214					    tmp->name,
215					    "multiple shared networks");
216				} else {
217					tmp->shared_network = share;
218				}
219
220				if (!share->interface) {
221					share->interface = tmp;
222				} else if (share->interface != tmp) {
223					warning("Multiple %s %s: %s %s",
224					    "interfaces match the",
225					    "same shared network",
226					    share->interface->name,
227					    tmp->name);
228				}
229			}
230		}
231	}
232
233	/* Discard interfaces we can't listen on. */
234	last = NULL;
235	for (tmp = interfaces; tmp; tmp = next) {
236		next = tmp->next;
237
238		if (!tmp->ifp) {
239			warning("Can't listen on %s - it has no IP address.",
240			    tmp->name);
241			/* Remove tmp from the list of interfaces. */
242			if (!last)
243				interfaces = interfaces->next;
244			else
245				last->next = tmp->next;
246			continue;
247		}
248
249		memcpy(&foo, &tmp->ifp->ifr_addr, sizeof tmp->ifp->ifr_addr);
250
251		if (!tmp->shared_network) {
252			warning("Can't listen on %s - dhcpd.conf has no subnet "
253			    "declaration for %s.", tmp->name,
254			    inet_ntoa(foo.sin_addr));
255			/* Remove tmp from the list of interfaces. */
256			if (!last)
257				interfaces = interfaces->next;
258			else
259				last->next = tmp->next;
260			continue;
261		}
262
263		last = tmp;
264
265		/* Find subnets that don't have valid interface addresses. */
266		for (subnet = (tmp->shared_network ? tmp->shared_network->subnets :
267		    NULL); subnet; subnet = subnet->next_sibling) {
268			if (!subnet->interface_address.len) {
269				/*
270				 * Set the interface address for this subnet
271				 * to the first address we found.
272				 */
273				subnet->interface_address.len = 4;
274				memcpy(subnet->interface_address.iabuf,
275				    &foo.sin_addr.s_addr, 4);
276			}
277		}
278
279		/* Register the interface... */
280		if_register_receive(tmp);
281		if_register_send(tmp);
282		note("Listening on %s (%s).", tmp->name,
283		    inet_ntoa(foo.sin_addr));
284	}
285
286	if (interfaces == NULL)
287		error("No interfaces to listen on.");
288
289	/* Now register all the remaining interfaces as protocols. */
290	for (tmp = interfaces; tmp; tmp = tmp->next)
291		add_protocol(tmp->name, tmp->rfdesc, got_one, tmp);
292
293	freeifaddrs(ifap);
294}
295
296/*
297 * Wait for packets to come in using poll().  When a packet comes in,
298 * call receive_packet to receive the packet and possibly strip hardware
299 * addressing information from it, and then process it in do_packet.
300 */
301void
302dispatch(void)
303{
304	int nfds, i, to_msec;
305	struct protocol *l;
306	static struct pollfd *fds;
307	static int nfds_max;
308	time_t howlong;
309
310	for (nfds = 0, l = protocols; l; l = l->next)
311		nfds++;
312	if (syncfd != -1)
313		nfds++;
314	if (nfds > nfds_max) {
315		fds = reallocarray(fds, nfds, sizeof(struct pollfd));
316		if (fds == NULL)
317			error("Can't allocate poll structures.");
318		nfds_max = nfds;
319	}
320
321	for (;;) {
322		/*
323		 * Call any expired timeouts, and then if there's
324		 * still a timeout registered, time out the poll
325		 * call then.
326		 */
327		time(&cur_time);
328another:
329		if (timeouts) {
330			if (timeouts->when <= cur_time) {
331				struct dhcpd_timeout *t = timeouts;
332				timeouts = timeouts->next;
333				(*(t->func))(t->what);
334				t->next = free_timeouts;
335				free_timeouts = t;
336				goto another;
337			}
338
339			/*
340			 * Figure timeout in milliseconds, and check for
341			 * potential overflow, so we can cram into an int
342			 * for poll, while not polling with a negative
343			 * timeout and blocking indefinitely.
344			 */
345			howlong = timeouts->when - cur_time;
346			if (howlong > INT_MAX / 1000)
347				howlong = INT_MAX / 1000;
348			to_msec = howlong * 1000;
349		} else
350			to_msec = -1;
351
352		/* Set up the descriptors to be polled. */
353		for (i = 0, l = protocols; l; l = l->next) {
354			struct interface_info *ip = l->local;
355
356			if (ip && (l->handler != got_one || !ip->dead)) {
357				fds[i].fd = l->fd;
358				fds[i].events = POLLIN;
359				++i;
360			}
361		}
362
363		if (i == 0)
364			error("No live interfaces to poll on - exiting.");
365
366		if (syncfd != -1) {
367			/* add syncer */
368			fds[i].fd = syncfd;
369			fds[i].events = POLLIN;
370		}
371
372		/* Wait for a packet or a timeout... */
373		switch (poll(fds, nfds, to_msec)) {
374		case -1:
375			if (errno != EAGAIN && errno != EINTR)
376				error("poll: %m");
377			/* FALLTHROUGH */
378		case 0:
379			continue;	/* no packets */
380		}
381		time(&cur_time);
382
383		for (i = 0, l = protocols; l; l = l->next) {
384			struct interface_info *ip = l->local;
385
386			if ((fds[i].revents & (POLLIN | POLLHUP))) {
387				if (ip && (l->handler != got_one ||
388				    !ip->dead))
389					(*(l->handler))(l);
390				if (interfaces_invalidated)
391					break;
392			}
393			++i;
394		}
395		if ((syncfd != -1) && (fds[i].revents & (POLLIN | POLLHUP)))
396			sync_recv();
397		interfaces_invalidated = 0;
398	}
399}
400
401
402void
403got_one(struct protocol *l)
404{
405	struct sockaddr_in from;
406	struct hardware hfrom;
407	struct iaddr ifrom;
408	ssize_t result;
409	union {
410		unsigned char packbuf[4095];
411		struct dhcp_packet packet;
412	} u;
413	struct interface_info *ip = l->local;
414
415	bzero(&u, sizeof(u));
416
417	if ((result = receive_packet(ip, u.packbuf, sizeof u,
418	    &from, &hfrom)) == -1) {
419		warning("receive_packet failed on %s: %s", ip->name,
420		    strerror(errno));
421		ip->errors++;
422		if ((!interface_status(ip)) ||
423		    (ip->noifmedia && ip->errors > 20)) {
424			/* our interface has gone away. */
425			warning("Interface %s no longer appears valid.",
426			    ip->name);
427			ip->dead = 1;
428			interfaces_invalidated = 1;
429			close(l->fd);
430			remove_protocol(l);
431			free(ip);
432		}
433		return;
434	}
435	if (result == 0)
436		return;
437
438	ifrom.len = 4;
439	memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
440
441	do_packet(ip, &u.packet, result, from.sin_port, ifrom, &hfrom);
442}
443
444int
445interface_status(struct interface_info *ifinfo)
446{
447	char * ifname = ifinfo->name;
448	int ifsock = ifinfo->rfdesc;
449	struct ifreq ifr;
450	struct ifmediareq ifmr;
451
452	/* get interface flags */
453	memset(&ifr, 0, sizeof(ifr));
454	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
455	if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) == -1) {
456		syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) on %s: %m", ifname);
457		goto inactive;
458	}
459	/*
460	 * if one of UP and RUNNING flags is dropped,
461	 * the interface is not active.
462	 */
463	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
464		goto inactive;
465
466	/* Next, check carrier on the interface, if possible */
467	if (ifinfo->noifmedia)
468		goto active;
469	memset(&ifmr, 0, sizeof(ifmr));
470	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
471	if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
472		if (errno != EINVAL) {
473			syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
474			    ifname);
475			ifinfo->noifmedia = 1;
476			goto active;
477		}
478		/*
479		 * EINVAL (or ENOTTY) simply means that the interface
480		 * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
481		 */
482		ifinfo->noifmedia = 1;
483		goto active;
484	}
485	if (ifmr.ifm_status & IFM_AVALID) {
486		switch (ifmr.ifm_active & IFM_NMASK) {
487		case IFM_ETHER:
488			if (ifmr.ifm_status & IFM_ACTIVE)
489				goto active;
490			else
491				goto inactive;
492			break;
493		default:
494			goto inactive;
495		}
496	}
497 inactive:
498	return (0);
499 active:
500	return (1);
501}
502
503int
504locate_network(struct packet *packet)
505{
506	struct iaddr ia;
507
508	/* If this came through a gateway, find the corresponding subnet... */
509	if (packet->raw->giaddr.s_addr) {
510		struct subnet *subnet;
511
512		ia.len = 4;
513		memcpy(ia.iabuf, &packet->raw->giaddr, 4);
514		subnet = find_subnet(ia);
515		if (subnet)
516			packet->shared_network = subnet->shared_network;
517		else
518			packet->shared_network = NULL;
519	} else {
520		packet->shared_network = packet->interface->shared_network;
521	}
522	if (packet->shared_network)
523		return 1;
524	return 0;
525}
526
527void
528add_timeout(time_t when, void (*where)(void *), void *what)
529{
530	struct dhcpd_timeout *t, *q;
531
532	/* See if this timeout supersedes an existing timeout. */
533	t = NULL;
534	for (q = timeouts; q; q = q->next) {
535		if (q->func == where && q->what == what) {
536			if (t)
537				t->next = q->next;
538			else
539				timeouts = q->next;
540			break;
541		}
542		t = q;
543	}
544
545	/* If we didn't supersede a timeout, allocate a timeout
546	   structure now. */
547	if (!q) {
548		if (free_timeouts) {
549			q = free_timeouts;
550			free_timeouts = q->next;
551			q->func = where;
552			q->what = what;
553		} else {
554			q = malloc(sizeof (struct dhcpd_timeout));
555			if (!q)
556				error("Can't allocate timeout structure!");
557			q->func = where;
558			q->what = what;
559		}
560	}
561
562	q->when = when;
563
564	/* Now sort this timeout into the timeout list. */
565
566	/* Beginning of list? */
567	if (!timeouts || timeouts->when > q->when) {
568		q->next = timeouts;
569		timeouts = q;
570		return;
571	}
572
573	/* Middle of list? */
574	for (t = timeouts; t->next; t = t->next) {
575		if (t->next->when > q->when) {
576			q->next = t->next;
577			t->next = q;
578			return;
579		}
580	}
581
582	/* End of list. */
583	t->next = q;
584	q->next = NULL;
585}
586
587void
588cancel_timeout(void (*where)(void *), void *what)
589{
590	struct dhcpd_timeout *t, *q;
591
592	/* Look for this timeout on the list, and unlink it if we find it. */
593	t = NULL;
594	for (q = timeouts; q; q = q->next) {
595		if (q->func == where && q->what == what) {
596			if (t)
597				t->next = q->next;
598			else
599				timeouts = q->next;
600			break;
601		}
602		t = q;
603	}
604
605	/* If we found the timeout, put it on the free list. */
606	if (q) {
607		q->next = free_timeouts;
608		free_timeouts = q;
609	}
610}
611
612/* Add a protocol to the list of protocols... */
613void
614add_protocol(char *name, int fd, void (*handler)(struct protocol *),
615    void *local)
616{
617	struct protocol *p;
618
619	p = malloc(sizeof *p);
620	if (!p)
621		error("can't allocate protocol struct for %s", name);
622	p->fd = fd;
623	p->handler = handler;
624	p->local = local;
625	p->next = protocols;
626	protocols = p;
627}
628
629void
630remove_protocol(struct protocol *proto)
631{
632	struct protocol *p, *next, *prev = NULL;
633
634	for (p = protocols; p; p = next) {
635		next = p->next;
636		if (p == proto) {
637			if (prev)
638				prev->next = p->next;
639			else
640				protocols = p->next;
641			free(p);
642		}
643	}
644}
645
646int
647get_rdomain(char *name)
648{
649	int rv = 0, s;
650	struct  ifreq ifr;
651
652	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
653		error("get_rdomain socket: %m");
654
655	bzero(&ifr, sizeof(ifr));
656	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
657	if (ioctl(s, SIOCGIFRDOMAIN, (caddr_t)&ifr) != -1)
658		rv = ifr.ifr_rdomainid;
659
660	close(s);
661	return rv;
662}
663