xref: /freebsd/sbin/dhclient/dispatch.c (revision d6b92ffa)
1 /*	$OpenBSD: dispatch.c,v 1.31 2004/09/21 04:07:03 david 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/cdefs.h>
43 __FBSDID("$FreeBSD$");
44 
45 #include "dhcpd.h"
46 #include "privsep.h"
47 
48 #include <sys/ioctl.h>
49 
50 #include <net/if_media.h>
51 #include <ifaddrs.h>
52 #include <poll.h>
53 
54 struct protocol *protocols;
55 struct timeout *timeouts;
56 static struct timeout *free_timeouts;
57 static int interfaces_invalidated;
58 void (*bootp_packet_handler)(struct interface_info *,
59     struct dhcp_packet *, int, unsigned int,
60     struct iaddr, struct hardware *);
61 
62 static int interface_status(struct interface_info *ifinfo);
63 
64 /*
65  * Use getifaddrs() to get a list of all the attached interfaces.  For
66  * each interface that's of type INET and not the loopback interface,
67  * register that interface with the network I/O software, figure out
68  * what subnet it's on, and add it to the list of interfaces.
69  */
70 void
71 discover_interfaces(struct interface_info *iface)
72 {
73 	struct ifaddrs *ifap, *ifa;
74 	struct sockaddr_in foo;
75 	struct ifreq *tif;
76 
77 	if (getifaddrs(&ifap) != 0)
78 		error("getifaddrs failed");
79 
80 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
81 		if ((ifa->ifa_flags & IFF_LOOPBACK) ||
82 		    (ifa->ifa_flags & IFF_POINTOPOINT) ||
83 		    (!(ifa->ifa_flags & IFF_UP)))
84 			continue;
85 
86 		if (strcmp(iface->name, ifa->ifa_name))
87 			continue;
88 
89 		/*
90 		 * If we have the capability, extract link information
91 		 * and record it in a linked list.
92 		 */
93 		if (ifa->ifa_addr->sa_family == AF_LINK) {
94 			struct sockaddr_dl *foo =
95 			    (struct sockaddr_dl *)ifa->ifa_addr;
96 
97 			iface->index = foo->sdl_index;
98 			iface->hw_address.hlen = foo->sdl_alen;
99 			iface->hw_address.htype = HTYPE_ETHER; /* XXX */
100 			memcpy(iface->hw_address.haddr,
101 			    LLADDR(foo), foo->sdl_alen);
102 		} else if (ifa->ifa_addr->sa_family == AF_INET) {
103 			struct iaddr addr;
104 
105 			memcpy(&foo, ifa->ifa_addr, sizeof(foo));
106 			if (foo.sin_addr.s_addr == htonl(INADDR_LOOPBACK))
107 				continue;
108 			if (!iface->ifp) {
109 				if ((tif = calloc(1, sizeof(struct ifreq)))
110 				    == NULL)
111 					error("no space to remember ifp");
112 				strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ);
113 				memcpy(&tif->ifr_addr, ifa->ifa_addr,
114 				    ifa->ifa_addr->sa_len);
115 				iface->ifp = tif;
116 				iface->primary_address = foo.sin_addr;
117 			}
118 			addr.len = 4;
119 			memcpy(addr.iabuf, &foo.sin_addr.s_addr, addr.len);
120 		}
121 	}
122 
123 	if (!iface->ifp)
124 		error("%s: not found", iface->name);
125 
126 	/* Register the interface... */
127 	if_register_receive(iface);
128 	if_register_send(iface);
129 	add_protocol(iface->name, iface->rfdesc, got_one, iface);
130 	freeifaddrs(ifap);
131 }
132 
133 void
134 reinitialize_interfaces(void)
135 {
136 	interfaces_invalidated = 1;
137 }
138 
139 /*
140  * Wait for packets to come in using poll().  When a packet comes in,
141  * call receive_packet to receive the packet and possibly strip hardware
142  * addressing information from it, and then call through the
143  * bootp_packet_handler hook to try to do something with it.
144  */
145 void
146 dispatch(void)
147 {
148 	int count, live_interfaces, i, to_msec, nfds = 0;
149 	struct protocol *l;
150 	struct pollfd *fds;
151 	time_t howlong;
152 
153 	for (l = protocols; l; l = l->next)
154 		nfds++;
155 
156 	fds = malloc(nfds * sizeof(struct pollfd));
157 	if (fds == NULL)
158 		error("Can't allocate poll structures.");
159 
160 	do {
161 		/*
162 		 * Call any expired timeouts, and then if there's still
163 		 * a timeout registered, time out the select call then.
164 		 */
165 another:
166 		if (timeouts) {
167 			struct timeout *t;
168 
169 			if (timeouts->when <= cur_time) {
170 				t = timeouts;
171 				timeouts = timeouts->next;
172 				(*(t->func))(t->what);
173 				t->next = free_timeouts;
174 				free_timeouts = t;
175 				goto another;
176 			}
177 
178 			/*
179 			 * Figure timeout in milliseconds, and check for
180 			 * potential overflow, so we can cram into an
181 			 * int for poll, while not polling with a
182 			 * negative timeout and blocking indefinitely.
183 			 */
184 			howlong = timeouts->when - cur_time;
185 			if (howlong > INT_MAX / 1000)
186 				howlong = INT_MAX / 1000;
187 			to_msec = howlong * 1000;
188 		} else
189 			to_msec = -1;
190 
191 		/* Set up the descriptors to be polled. */
192 		live_interfaces = 0;
193 		for (i = 0, l = protocols; l; l = l->next) {
194 			struct interface_info *ip = l->local;
195 
196 			if (ip == NULL || ip->dead)
197 				continue;
198 			fds[i].fd = l->fd;
199 			fds[i].events = POLLIN;
200 			fds[i].revents = 0;
201 			i++;
202 			if (l->handler == got_one)
203 				live_interfaces++;
204 		}
205 		if (live_interfaces == 0)
206 			error("No live interfaces to poll on - exiting.");
207 
208 		/* Wait for a packet or a timeout... XXX */
209 		count = poll(fds, nfds, to_msec);
210 
211 		/* Not likely to be transitory... */
212 		if (count == -1) {
213 			if (errno == EAGAIN || errno == EINTR) {
214 				time(&cur_time);
215 				continue;
216 			} else
217 				error("poll: %m");
218 		}
219 
220 		/* Get the current time... */
221 		time(&cur_time);
222 
223 		i = 0;
224 		for (l = protocols; l; l = l->next) {
225 			struct interface_info *ip;
226 			ip = l->local;
227 			if ((fds[i].revents & (POLLIN | POLLHUP))) {
228 				fds[i].revents = 0;
229 				if (ip && (l->handler != got_one ||
230 				    !ip->dead))
231 					(*(l->handler))(l);
232 				if (interfaces_invalidated)
233 					break;
234 			}
235 			i++;
236 		}
237 		interfaces_invalidated = 0;
238 	} while (1);
239 }
240 
241 
242 void
243 got_one(struct protocol *l)
244 {
245 	struct sockaddr_in from;
246 	struct hardware hfrom;
247 	struct iaddr ifrom;
248 	ssize_t result;
249 	union {
250 		/*
251 		 * Packet input buffer.  Must be as large as largest
252 		 * possible MTU.
253 		 */
254 		unsigned char packbuf[4095];
255 		struct dhcp_packet packet;
256 	} u;
257 	struct interface_info *ip = l->local;
258 
259 	if ((result = receive_packet(ip, u.packbuf, sizeof(u), &from,
260 	    &hfrom)) == -1) {
261 		warning("receive_packet failed on %s: %s", ip->name,
262 		    strerror(errno));
263 		ip->errors++;
264 		if ((!interface_status(ip)) ||
265 		    (ip->noifmedia && ip->errors > 20)) {
266 			/* our interface has gone away. */
267 			warning("Interface %s no longer appears valid.",
268 			    ip->name);
269 			ip->dead = 1;
270 			interfaces_invalidated = 1;
271 			close(l->fd);
272 			remove_protocol(l);
273 			free(ip);
274 		}
275 		return;
276 	}
277 	if (result == 0)
278 		return;
279 
280 	if (bootp_packet_handler) {
281 		ifrom.len = 4;
282 		memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
283 
284 		(*bootp_packet_handler)(ip, &u.packet, result,
285 		    from.sin_port, ifrom, &hfrom);
286 	}
287 }
288 
289 int
290 interface_status(struct interface_info *ifinfo)
291 {
292 	char *ifname = ifinfo->name;
293 	int ifsock = ifinfo->rfdesc;
294 	struct ifreq ifr;
295 	struct ifmediareq ifmr;
296 
297 	/* get interface flags */
298 	memset(&ifr, 0, sizeof(ifr));
299 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
300 	if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
301 		syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) on %s: %m", ifname);
302 		goto inactive;
303 	}
304 
305 	/*
306 	 * if one of UP and RUNNING flags is dropped,
307 	 * the interface is not active.
308 	 */
309 	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
310 		goto inactive;
311 
312 	/* Next, check carrier on the interface, if possible */
313 	if (ifinfo->noifmedia)
314 		goto active;
315 	memset(&ifmr, 0, sizeof(ifmr));
316 	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
317 	if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
318 		if (errno != EINVAL) {
319 			syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
320 			    ifname);
321 
322 			ifinfo->noifmedia = 1;
323 			goto active;
324 		}
325 		/*
326 		 * EINVAL (or ENOTTY) simply means that the interface
327 		 * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
328 		 */
329 		ifinfo->noifmedia = 1;
330 		goto active;
331 	}
332 	if (ifmr.ifm_status & IFM_AVALID) {
333 		switch (ifmr.ifm_active & IFM_NMASK) {
334 		case IFM_ETHER:
335 		case IFM_IEEE80211:
336 			if (ifmr.ifm_status & IFM_ACTIVE)
337 				goto active;
338 			else
339 				goto inactive;
340 			break;
341 		default:
342 			goto inactive;
343 		}
344 	}
345 inactive:
346 	return (0);
347 active:
348 	return (1);
349 }
350 
351 void
352 add_timeout(time_t when, void (*where)(void *), void *what)
353 {
354 	struct timeout *t, *q;
355 
356 	/* See if this timeout supersedes an existing timeout. */
357 	t = NULL;
358 	for (q = timeouts; q; q = q->next) {
359 		if (q->func == where && q->what == what) {
360 			if (t)
361 				t->next = q->next;
362 			else
363 				timeouts = q->next;
364 			break;
365 		}
366 		t = q;
367 	}
368 
369 	/* If we didn't supersede a timeout, allocate a timeout
370 	   structure now. */
371 	if (!q) {
372 		if (free_timeouts) {
373 			q = free_timeouts;
374 			free_timeouts = q->next;
375 			q->func = where;
376 			q->what = what;
377 		} else {
378 			q = malloc(sizeof(struct timeout));
379 			if (!q)
380 				error("Can't allocate timeout structure!");
381 			q->func = where;
382 			q->what = what;
383 		}
384 	}
385 
386 	q->when = when;
387 
388 	/* Now sort this timeout into the timeout list. */
389 
390 	/* Beginning of list? */
391 	if (!timeouts || timeouts->when > q->when) {
392 		q->next = timeouts;
393 		timeouts = q;
394 		return;
395 	}
396 
397 	/* Middle of list? */
398 	for (t = timeouts; t->next; t = t->next) {
399 		if (t->next->when > q->when) {
400 			q->next = t->next;
401 			t->next = q;
402 			return;
403 		}
404 	}
405 
406 	/* End of list. */
407 	t->next = q;
408 	q->next = NULL;
409 }
410 
411 void
412 cancel_timeout(void (*where)(void *), void *what)
413 {
414 	struct timeout *t, *q;
415 
416 	/* Look for this timeout on the list, and unlink it if we find it. */
417 	t = NULL;
418 	for (q = timeouts; q; q = q->next) {
419 		if (q->func == where && q->what == what) {
420 			if (t)
421 				t->next = q->next;
422 			else
423 				timeouts = q->next;
424 			break;
425 		}
426 		t = q;
427 	}
428 
429 	/* If we found the timeout, put it on the free list. */
430 	if (q) {
431 		q->next = free_timeouts;
432 		free_timeouts = q;
433 	}
434 }
435 
436 /* Add a protocol to the list of protocols... */
437 void
438 add_protocol(char *name, int fd, void (*handler)(struct protocol *),
439     void *local)
440 {
441 	struct protocol *p;
442 
443 	p = malloc(sizeof(*p));
444 	if (!p)
445 		error("can't allocate protocol struct for %s", name);
446 
447 	p->fd = fd;
448 	p->handler = handler;
449 	p->local = local;
450 	p->next = protocols;
451 	protocols = p;
452 }
453 
454 void
455 remove_protocol(struct protocol *proto)
456 {
457 	struct protocol *p, *next;
458 
459 	for (p = protocols; p; p = next) {
460 		next = p->next;
461 		if (p == proto) {
462 			protocols = p->next;
463 			free(p);
464 		}
465 	}
466 }
467 
468 int
469 interface_link_status(char *ifname)
470 {
471 	struct ifmediareq ifmr;
472 	int sock;
473 
474 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
475 		error("Can't create socket");
476 
477 	memset(&ifmr, 0, sizeof(ifmr));
478 	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
479 	if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
480 		/* EINVAL -> link state unknown. treat as active */
481 		if (errno != EINVAL)
482 			syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
483 			    ifname);
484 		close(sock);
485 		return (1);
486 	}
487 	close(sock);
488 
489 	if (ifmr.ifm_status & IFM_AVALID) {
490 		switch (ifmr.ifm_active & IFM_NMASK) {
491 		case IFM_ETHER:
492 		case IFM_IEEE80211:
493 			if (ifmr.ifm_status & IFM_ACTIVE)
494 				return (1);
495 			else
496 				return (0);
497 		}
498 	}
499 	return (1);
500 }
501 
502 void
503 interface_set_mtu_unpriv(int privfd, u_int16_t mtu)
504 {
505 	struct imsg_hdr hdr;
506 	struct buf *buf;
507 	int errs = 0;
508 
509 	hdr.code = IMSG_SET_INTERFACE_MTU;
510 	hdr.len = sizeof(hdr) +
511 		sizeof(u_int16_t);
512 
513 	if ((buf = buf_open(hdr.len)) == NULL)
514 		error("buf_open: %m");
515 
516 	errs += buf_add(buf, &hdr, sizeof(hdr));
517 	errs += buf_add(buf, &mtu, sizeof(mtu));
518 	if (errs)
519 		error("buf_add: %m");
520 
521 	if (buf_close(privfd, buf) == -1)
522 		error("buf_close: %m");
523 }
524 
525 void
526 interface_set_mtu_priv(char *ifname, u_int16_t mtu)
527 {
528 	struct ifreq ifr;
529 	int sock;
530 
531 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
532 		error("Can't create socket");
533 
534 	memset(&ifr, 0, sizeof(ifr));
535 
536 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
537 	ifr.ifr_mtu = mtu;
538 
539 	if (ioctl(sock, SIOCSIFMTU, &ifr) == -1)
540 		warning("SIOCSIFMTU failed (%d): %s", mtu,
541 			strerror(errno));
542 	close(sock);
543 }
544