1 /**	$MirOS: src/usr.sbin/dhcpd/dispatch.c,v 1.4 2006/09/20 20:03:32 tg Exp $ */
2 /*	$OpenBSD: dispatch.c,v 1.21 2006/05/30 23:43:46 ckuethe Exp $ */
3 
4 /*
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 "dhcpd.h"
43 #include <ifaddrs.h>
44 #include <sys/ioctl.h>
45 #include <poll.h>
46 #include <net/if_media.h>
47 
48 
49 /* Most boxes has less than 16 interfaces, so this might be a good guess.  */
50 #define INITIAL_IFREQ_COUNT 16
51 
52 struct interface_info *interfaces;
53 struct protocol *protocols;
54 struct dhcpd_timeout *timeouts;
55 static struct dhcpd_timeout *free_timeouts;
56 static int interfaces_invalidated;
57 void (*bootp_packet_handler)(struct interface_info *,
58     struct dhcp_packet *, int, unsigned int, struct iaddr, struct hardware *);
59 
60 static int interface_status(struct interface_info *ifinfo);
61 
62 /* Use getifaddrs() to get a list of all the attached interfaces.
63    For each interface that's of type INET and not the loopback interface,
64    register that interface with the network I/O software, figure out what
65    subnet it's on, and add it to the list of interfaces. */
66 
67 void
discover_interfaces(void)68 discover_interfaces(void)
69 {
70 	struct interface_info *tmp;
71 	struct interface_info *last, *next;
72 	struct subnet *subnet;
73 	struct shared_network *share;
74 	struct sockaddr_in foo;
75 	int ir = 0;
76 	struct ifreq *tif;
77 	struct ifaddrs *ifap, *ifa;
78 #ifdef ALIAS_NAMES_PERMUTED
79 	char *s;
80 #endif
81 
82 	if (getifaddrs(&ifap) != 0)
83 		error("getifaddrs failed");
84 
85 	/*
86 	 * If we already have a list of interfaces, the interfaces were
87 	 * requested.
88 	 */
89 	if (interfaces != NULL)
90 		ir = 1;
91 
92 	/* Cycle through the list of interfaces looking for IP addresses. */
93 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
94 		/*
95 		 * See if this is the sort of interface we want to
96 		 * deal with.  Skip loopback, point-to-point and down
97 		 * interfaces, except don't skip down interfaces if we're
98 		 * trying to get a list of configurable interfaces.
99 		 */
100 		if ((ifa->ifa_flags & IFF_LOOPBACK) ||
101 		    (ifa->ifa_flags & IFF_POINTOPOINT) ||
102 		    (!(ifa->ifa_flags & IFF_UP)))
103 			continue;
104 
105 		/* See if we've seen an interface that matches this one. */
106 		for (tmp = interfaces; tmp; tmp = tmp->next)
107 			if (!strcmp(tmp->name, ifa->ifa_name))
108 				break;
109 
110 		/* If we are looking for specific interfaces, ignore others. */
111 		if (tmp == NULL && ir)
112 			continue;
113 
114 		/* If there isn't already an interface by this name,
115 		   allocate one. */
116 		if (tmp == NULL) {
117 			tmp = ((struct interface_info *)dmalloc(sizeof *tmp,
118 			    "discover_interfaces"));
119 			if (!tmp)
120 				error("Insufficient memory to %s %s",
121 				    "record interface", ifa->ifa_name);
122 			strlcpy(tmp->name, ifa->ifa_name, sizeof(tmp->name));
123 			tmp->next = interfaces;
124 			tmp->noifmedia = tmp->dead = tmp->errors = 0;
125 			interfaces = tmp;
126 		}
127 
128 		/* If we have the capability, extract link information
129 		   and record it in a linked list. */
130 		if (ifa->ifa_addr->sa_family == AF_LINK) {
131 			struct sockaddr_dl *foo =
132 			    ((struct sockaddr_dl *)(ifa->ifa_addr));
133 			tmp->index = foo->sdl_index;
134 			tmp->hw_address.hlen = foo->sdl_alen;
135 			tmp->hw_address.htype = HTYPE_ETHER; /* XXX */
136 			memcpy(tmp->hw_address.haddr,
137 			    LLADDR(foo), foo->sdl_alen);
138 		} else if (ifa->ifa_addr->sa_family == AF_INET) {
139 			struct iaddr addr;
140 
141 			/* Get a pointer to the address... */
142 			memmove(&foo, ifa->ifa_addr, sizeof(foo));
143 
144 			/* We don't want the loopback interface. */
145 			if (foo.sin_addr.s_addr == htonl (INADDR_LOOPBACK))
146 				continue;
147 
148 			/* If this is the first real IP address we've
149 			   found, keep a pointer to ifreq structure in
150 			   which we found it. */
151 			if (!tmp->ifp) {
152 				int len = (IFNAMSIZ + ifa->ifa_addr->sa_len);
153 				tif = (struct ifreq *)malloc(len);
154 				if (!tif)
155 					error("no space to remember ifp.");
156 				strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ);
157 				memcpy(&tif->ifr_addr, ifa->ifa_addr,
158 				    ifa->ifa_addr->sa_len);
159 				tmp->ifp = tif;
160 				tmp->primary_address = foo.sin_addr;
161 			}
162 
163 			/* Grab the address... */
164 			addr.len = 4;
165 			memcpy(addr.iabuf, &foo.sin_addr.s_addr, addr.len);
166 
167 			/* If there's a registered subnet for this address,
168 			   connect it together... */
169 			if ((subnet = find_subnet(addr))) {
170 				/* If this interface has multiple aliases
171 				   on the same subnet, ignore all but the
172 				   first we encounter. */
173 				if (!subnet->interface) {
174 					subnet->interface = tmp;
175 					subnet->interface_address = addr;
176 				} else if (subnet->interface != tmp) {
177 					warning("Multiple %s %s: %s %s",
178 					    "interfaces match the",
179 					    "same subnet",
180 					    subnet->interface->name,
181 					    tmp->name);
182 				}
183 				share = subnet->shared_network;
184 				if (tmp->shared_network &&
185 				    tmp->shared_network != share) {
186 					warning("Interface %s matches %s",
187 					    tmp->name,
188 					    "multiple shared networks");
189 				} else {
190 					tmp->shared_network = share;
191 				}
192 
193 				if (!share->interface) {
194 					share->interface = tmp;
195 				} else if (share->interface != tmp) {
196 					warning("Multiple %s %s: %s %s",
197 					    "interfaces match the",
198 					    "same shared network",
199 					    share->interface->name,
200 					    tmp->name);
201 				}
202 			}
203 		}
204 	}
205 
206 	/* Discard interfaces we can't listen on. */
207 	last = NULL;
208 	for (tmp = interfaces; tmp; tmp = next) {
209 		next = tmp->next;
210 
211 		if (!tmp->ifp) {
212 			warning("Can't listen on %s - it has no IP address.",
213 			    tmp->name);
214 			/* Remove tmp from the list of interfaces. */
215 			if (!last)
216 				interfaces = interfaces->next;
217 			else
218 				last->next = tmp->next;
219 			continue;
220 		}
221 
222 		memcpy(&foo, &tmp->ifp->ifr_addr, sizeof tmp->ifp->ifr_addr);
223 
224 		if (!tmp->shared_network) {
225 			warning("Can't listen on %s - dhcpd.conf has no subnet "
226 			    "declaration for %s.", tmp->name,
227 			    inet_ntoa(foo.sin_addr));
228 			/* Remove tmp from the list of interfaces. */
229 			if (!last)
230 				interfaces = interfaces->next;
231 			else
232 				last->next = tmp->next;
233 			continue;
234 		}
235 
236 		last = tmp;
237 
238 		/* Find subnets that don't have valid interface addresses. */
239 		for (subnet = (tmp->shared_network ? tmp->shared_network->subnets :
240 		    NULL); subnet; subnet = subnet->next_sibling) {
241 			if (!subnet->interface_address.len) {
242 				/*
243 				 * Set the interface address for this subnet
244 				 * to the first address we found.
245 				 */
246 				subnet->interface_address.len = 4;
247 				memcpy(subnet->interface_address.iabuf,
248 				    &foo.sin_addr.s_addr, 4);
249 			}
250 		}
251 
252 		/* Register the interface... */
253 		if_register_receive(tmp);
254 		if_register_send(tmp);
255 	}
256 
257 	if (interfaces == NULL)
258 		error("No interfaces to listen on.");
259 
260 	/* Now register all the remaining interfaces as protocols. */
261 	for (tmp = interfaces; tmp; tmp = tmp->next)
262 		add_protocol(tmp->name, tmp->rfdesc, got_one, tmp);
263 
264 	freeifaddrs(ifap);
265 }
266 
267 /*
268  * Wait for packets to come in using poll().  When a packet comes in,
269  * call receive_packet to receive the packet and possibly strip hardware
270  * addressing information from it, and then call through the
271  * bootp_packet_handler hook to try to do something with it.
272  */
273 void
dispatch(void)274 dispatch(void)
275 {
276 	int nfds, i, to_msec;
277 	struct protocol *l;
278 	static struct pollfd *fds;
279 	static int nfds_max;
280 	time_t howlong;
281 
282 	for (nfds = 0, l = protocols; l; l = l->next)
283 		nfds++;
284 	if (nfds > nfds_max) {
285 		fds = realloc(fds, nfds * sizeof(struct pollfd));
286 		if (fds == NULL)
287 			error("Can't allocate poll structures.");
288 		nfds_max = nfds;
289 	}
290 
291 	for (;;) {
292 		/*
293 		 * Call any expired timeouts, and then if there's
294 		 * still a timeout registered, time out the poll
295 		 * call then.
296 		 */
297 		time(&cur_time);
298 another:
299 		if (timeouts) {
300 			if (timeouts->when <= cur_time) {
301 				struct dhcpd_timeout *t = timeouts;
302 				timeouts = timeouts->next;
303 				(*(t->func))(t->what);
304 				t->next = free_timeouts;
305 				free_timeouts = t;
306 				goto another;
307 			}
308 
309 			/*
310 			 * Figure timeout in milliseconds, and check for
311 			 * potential overflow, so we can cram into an int
312 			 * for poll, while not polling with a negative
313 			 * timeout and blocking indefinitely.
314 			 */
315 			howlong = timeouts->when - cur_time;
316 			if (howlong > INT_MAX / 1000)
317 				howlong = INT_MAX / 1000;
318 			to_msec = howlong * 1000;
319 		} else
320 			to_msec = -1;
321 
322 		/* Set up the descriptors to be polled. */
323 		for (i = 0, l = protocols; l; l = l->next) {
324 			struct interface_info *ip = l->local;
325 
326 			if (ip && (l->handler != got_one || !ip->dead)) {
327 				fds[i].fd = l->fd;
328 				fds[i].events = POLLIN;
329 				++i;
330 			}
331 		}
332 		if (i == 0)
333 			error("No live interfaces to poll on - exiting.");
334 
335 		/* Wait for a packet or a timeout... */
336 		switch (poll(fds, nfds, to_msec)) {
337 		case -1:
338 			if (errno != EAGAIN && errno != EINTR)
339 				error("poll: %m");
340 			/* FALLTHROUGH */
341 		case 0:
342 			continue;	/* no packets */
343 		}
344 
345 		for (i = 0, l = protocols; l; l = l->next) {
346 			struct interface_info *ip = l->local;
347 
348 			if ((fds[i].revents & (POLLIN | POLLHUP))) {
349 				if (ip && (l->handler != got_one ||
350 				    !ip->dead))
351 					(*(l->handler))(l);
352 				if (interfaces_invalidated)
353 					break;
354 			}
355 			++i;
356 		}
357 		interfaces_invalidated = 0;
358 	}
359 }
360 
361 
362 void
got_one(struct protocol * l)363 got_one(struct protocol *l)
364 {
365 	struct sockaddr_in from;
366 	struct hardware hfrom;
367 	struct iaddr ifrom;
368 	ssize_t result;
369 	union {
370 		unsigned char packbuf[4095];
371 		struct dhcp_packet packet;
372 	} u;
373 	struct interface_info *ip = l->local;
374 
375 	if ((result = receive_packet(ip, u.packbuf, sizeof u,
376 	    &from, &hfrom)) == -1) {
377 		warning("receive_packet failed on %s: %s", ip->name,
378 		    strerror(errno));
379 		ip->errors++;
380 		if ((!interface_status(ip)) ||
381 		    (ip->noifmedia && ip->errors > 20)) {
382 			/* our interface has gone away. */
383 			warning("Interface %s no longer appears valid.",
384 			    ip->name);
385 			ip->dead = 1;
386 			interfaces_invalidated = 1;
387 			close(l->fd);
388 			remove_protocol(l);
389 			free(ip);
390 		}
391 		return;
392 	}
393 	if (result == 0)
394 		return;
395 
396 	if (bootp_packet_handler) {
397 		ifrom.len = 4;
398 		memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
399 
400 		(*bootp_packet_handler)(ip, &u.packet, result,
401 		    from.sin_port, ifrom, &hfrom);
402 	}
403 }
404 
405 int
interface_status(struct interface_info * ifinfo)406 interface_status(struct interface_info *ifinfo)
407 {
408 	char * ifname = ifinfo->name;
409 	int ifsock = ifinfo->rfdesc;
410 	struct ifreq ifr;
411 	struct ifmediareq ifmr;
412 
413 	/* get interface flags */
414 	memset(&ifr, 0, sizeof(ifr));
415 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
416 	if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
417 		syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) on %s: %m", ifname);
418 		goto inactive;
419 	}
420 	/*
421 	 * if one of UP and RUNNING flags is dropped,
422 	 * the interface is not active.
423 	 */
424 	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
425 		goto inactive;
426 
427 	/* Next, check carrier on the interface, if possible */
428 	if (ifinfo->noifmedia)
429 		goto active;
430 	memset(&ifmr, 0, sizeof(ifmr));
431 	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
432 	if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
433 		if (errno != EINVAL) {
434 			syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
435 			    ifname);
436 			ifinfo->noifmedia = 1;
437 			goto active;
438 		}
439 		/*
440 		 * EINVAL (or ENOTTY) simply means that the interface
441 		 * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
442 		 */
443 		ifinfo->noifmedia = 1;
444 		goto active;
445 	}
446 	if (ifmr.ifm_status & IFM_AVALID) {
447 		switch (ifmr.ifm_active & IFM_NMASK) {
448 		case IFM_ETHER:
449 			if (ifmr.ifm_status & IFM_ACTIVE)
450 				goto active;
451 			else
452 				goto inactive;
453 			break;
454 		default:
455 			goto inactive;
456 		}
457 	}
458  inactive:
459 	return (0);
460  active:
461 	return (1);
462 }
463 
464 int
locate_network(struct packet * packet)465 locate_network(struct packet *packet)
466 {
467 	struct iaddr ia;
468 
469 	/* If this came through a gateway, find the corresponding subnet... */
470 	if (packet->raw->giaddr.s_addr) {
471 		struct subnet *subnet;
472 
473 		ia.len = 4;
474 		memcpy(ia.iabuf, &packet->raw->giaddr, 4);
475 		subnet = find_subnet(ia);
476 		if (subnet)
477 			packet->shared_network = subnet->shared_network;
478 		else
479 			packet->shared_network = NULL;
480 	} else {
481 		packet->shared_network = packet->interface->shared_network;
482 	}
483 	if (packet->shared_network)
484 		return 1;
485 	return 0;
486 }
487 
488 void
add_timeout(time_t when,void (* where)(void *),void * what)489 add_timeout(time_t when, void (*where)(void *), void *what)
490 {
491 	struct dhcpd_timeout *t, *q;
492 
493 	/* See if this timeout supersedes an existing timeout. */
494 	t = NULL;
495 	for (q = timeouts; q; q = q->next) {
496 		if (q->func == where && q->what == what) {
497 			if (t)
498 				t->next = q->next;
499 			else
500 				timeouts = q->next;
501 			break;
502 		}
503 		t = q;
504 	}
505 
506 	/* If we didn't supersede a timeout, allocate a timeout
507 	   structure now. */
508 	if (!q) {
509 		if (free_timeouts) {
510 			q = free_timeouts;
511 			free_timeouts = q->next;
512 			q->func = where;
513 			q->what = what;
514 		} else {
515 			q = (struct dhcpd_timeout *)malloc(sizeof (struct dhcpd_timeout));
516 			if (!q)
517 				error("Can't allocate timeout structure!");
518 			q->func = where;
519 			q->what = what;
520 		}
521 	}
522 
523 	q->when = when;
524 
525 	/* Now sort this timeout into the timeout list. */
526 
527 	/* Beginning of list? */
528 	if (!timeouts || timeouts->when > q->when) {
529 		q->next = timeouts;
530 		timeouts = q;
531 		return;
532 	}
533 
534 	/* Middle of list? */
535 	for (t = timeouts; t->next; t = t->next) {
536 		if (t->next->when > q->when) {
537 			q->next = t->next;
538 			t->next = q;
539 			return;
540 		}
541 	}
542 
543 	/* End of list. */
544 	t->next = q;
545 	q->next = NULL;
546 }
547 
548 void
cancel_timeout(void (* where)(void *),void * what)549 cancel_timeout(void (*where)(void *), void *what)
550 {
551 	struct dhcpd_timeout *t, *q;
552 
553 	/* Look for this timeout on the list, and unlink it if we find it. */
554 	t = NULL;
555 	for (q = timeouts; q; q = q->next) {
556 		if (q->func == where && q->what == what) {
557 			if (t)
558 				t->next = q->next;
559 			else
560 				timeouts = q->next;
561 			break;
562 		}
563 		t = q;
564 	}
565 
566 	/* If we found the timeout, put it on the free list. */
567 	if (q) {
568 		q->next = free_timeouts;
569 		free_timeouts = q;
570 	}
571 }
572 
573 /* Add a protocol to the list of protocols... */
574 void
add_protocol(char * name,int fd,void (* handler)(struct protocol *),void * local)575 add_protocol(char *name, int fd, void (*handler)(struct protocol *),
576     void *local)
577 {
578 	struct protocol *p;
579 
580 	p = (struct protocol *)malloc(sizeof *p);
581 	if (!p)
582 		error("can't allocate protocol struct for %s", name);
583 	p->fd = fd;
584 	p->handler = handler;
585 	p->local = local;
586 	p->next = protocols;
587 	protocols = p;
588 }
589 
590 void
remove_protocol(struct protocol * proto)591 remove_protocol(struct protocol *proto)
592 {
593 	struct protocol *p, *next, *prev = NULL;
594 
595 	for (p = protocols; p; p = next) {
596 		next = p->next;
597 		if (p == proto) {
598 			if (prev)
599 				prev->next = p->next;
600 			else
601 				protocols = p->next;
602 			free(p);
603 		}
604 	}
605 }
606