xref: /dragonfly/sbin/dhclient/dispatch.c (revision 9f3fc534)
1 /*	$OpenBSD: dispatch.c,v 1.41 2008/05/09 05:19:14 reyk Exp $	*/
2 /*	$DragonFly: src/sbin/dhclient/dispatch.c,v 1.1 2008/08/30 16:07:58 hasso Exp $	*/
3 
4 /*
5  * Copyright 2004 Henning Brauer <henning@openbsd.org>
6  * Copyright (c) 1995, 1996, 1997, 1998, 1999
7  * The Internet Software Consortium.   All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of The Internet Software Consortium nor the names
19  *    of its contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * This software has been written for the Internet Software Consortium
37  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38  * Enterprises.  To learn more about the Internet Software Consortium,
39  * see ``http://www.vix.com/isc''.  To learn more about Vixie
40  * Enterprises, see ``http://www.vix.com''.
41  */
42 
43 #include <sys/ioctl.h>
44 
45 #include <net/if_media.h>
46 
47 #include <ifaddrs.h>
48 #include <poll.h>
49 
50 #include "dhcpd.h"
51 
52 struct timeout *timeouts;
53 static struct timeout *free_timeouts;
54 static int interfaces_invalidated;
55 
56 /*
57  * Use getifaddrs() to get a list of all the attached interfaces.  For
58  * each interface that's of type INET and not the loopback interface,
59  * register that interface with the network I/O software, figure out
60  * what subnet it's on, and add it to the list of interfaces.
61  */
62 void
63 discover_interface(void)
64 {
65 	struct ifaddrs *ifap, *ifa;
66 	struct ifreq *tif;
67 	int len = IFNAMSIZ + sizeof(struct sockaddr_storage);
68 
69 	if (getifaddrs(&ifap) != 0)
70 		error("getifaddrs failed");
71 
72 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
73 		if ((ifa->ifa_flags & IFF_LOOPBACK) ||
74 		    (ifa->ifa_flags & IFF_POINTOPOINT) ||
75 		    (!(ifa->ifa_flags & IFF_UP)))
76 			continue;
77 
78 		if (strcmp(ifi->name, ifa->ifa_name))
79 			continue;
80 
81 		/*
82 		 * If we have the capability, extract link information
83 		 * and record it in a linked list.
84 		 */
85 		if (ifa->ifa_addr->sa_family == AF_LINK) {
86 			struct sockaddr_dl *foo =
87 			    (struct sockaddr_dl *)ifa->ifa_addr;
88 
89 			ifi->index = foo->sdl_index;
90 			ifi->hw_address.hlen = foo->sdl_alen;
91 			ifi->hw_address.htype = HTYPE_ETHER; /* XXX */
92 			memcpy(ifi->hw_address.haddr,
93 			    LLADDR(foo), foo->sdl_alen);
94 		}
95 		if (!ifi->ifp) {
96 			if ((tif = malloc(len)) == NULL)
97 				error("no space to remember ifp");
98 			strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ);
99 			ifi->ifp = tif;
100 		}
101 	}
102 
103 	if (!ifi->ifp)
104 		error("%s: not found", ifi->name);
105 
106 	/* Register the interface... */
107 	if_register_receive();
108 	if_register_send();
109 	freeifaddrs(ifap);
110 }
111 
112 void
113 reinitialize_interface(void)
114 {
115 	interfaces_invalidated = 1;
116 }
117 
118 /*
119  * Wait for packets to come in using poll().  When a packet comes in, call
120  * receive_packet to receive the packet and possibly strip hardware addressing
121  * information from it, and then call do_packet to try to do something with it.
122  */
123 void
124 dispatch(void)
125 {
126 	int count, to_msec;
127 	struct pollfd fds[2];
128 	time_t howlong;
129 
130 	do {
131 		/*
132 		 * Call any expired timeouts, and then if there's still
133 		 * a timeout registered, time out the select call then.
134 		 */
135 another:
136 		if (!ifi->linkstat)
137 			interfaces_invalidated = 0;
138 
139 		if (timeouts) {
140 			struct timeout *t;
141 
142 			if (timeouts->when <= cur_time) {
143 				t = timeouts;
144 				timeouts = timeouts->next;
145 				(*(t->func))();
146 				t->next = free_timeouts;
147 				free_timeouts = t;
148 				goto another;
149 			}
150 
151 			/*
152 			 * Figure timeout in milliseconds, and check for
153 			 * potential overflow, so we can cram into an
154 			 * int for poll, while not polling with a
155 			 * negative timeout and blocking indefinitely.
156 			 */
157 			howlong = timeouts->when - cur_time;
158 			if (howlong > INT_MAX / 1000)
159 				howlong = INT_MAX / 1000;
160 			to_msec = howlong * 1000;
161 		} else
162 			to_msec = -1;
163 
164 		/* Set up the descriptors to be polled. */
165 		if (!ifi || ifi->rfdesc == -1)
166 			error("No live interface to poll on");
167 
168 		fds[0].fd = ifi->rfdesc;
169 		fds[1].fd = routefd; /* Could be -1, which will be ignored. */
170 		fds[0].events = fds[1].events = POLLIN;
171 
172 		/* Wait for a packet or a timeout... XXX */
173 		count = poll(fds, 2, to_msec);
174 
175 		/* Get the current time... */
176 		time(&cur_time);
177 
178 		/* Not likely to be transitory... */
179 		if (count == -1) {
180 			if (errno == EAGAIN || errno == EINTR) {
181 				continue;
182 			} else
183 				error("poll: %m");
184 		}
185 
186 		if ((fds[0].revents & (POLLIN | POLLHUP))) {
187 			if (ifi->linkstat &&
188 			    ifi && ifi->rfdesc != -1)
189 				got_one();
190 		}
191 		if ((fds[1].revents & (POLLIN | POLLHUP))) {
192 			if (ifi && !interfaces_invalidated)
193 				routehandler();
194 		}
195 
196 		interfaces_invalidated = 0;
197 	} while (1);
198 }
199 
200 void
201 got_one(void)
202 {
203 	struct sockaddr_in from;
204 	struct hardware hfrom;
205 	struct iaddr ifrom;
206 	ssize_t result;
207 
208 	if ((result = receive_packet(&from, &hfrom)) == -1) {
209 		warning("receive_packet failed on %s: %s", ifi->name,
210 		    strerror(errno));
211 		ifi->errors++;
212 		if ((!interface_status(ifi->name)) ||
213 		    (ifi->noifmedia && ifi->errors > 20)) {
214 			/* our interface has gone away. */
215 			warning("Interface %s no longer appears valid.",
216 			    ifi->name);
217 			interfaces_invalidated = 1;
218 			close(ifi->rfdesc);
219 			ifi->rfdesc = -1;
220 		}
221 		return;
222 	}
223 	if (result == 0)
224 		return;
225 
226 	ifrom.len = 4;
227 	memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
228 
229 	do_packet(result, from.sin_port, ifrom, &hfrom);
230 }
231 
232 int
233 interface_link_forceup(char *ifname)
234 {
235 	struct ifreq ifr;
236 	int sock;
237 
238 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
239 		error("Can't create socket");
240 
241 	memset(&ifr, 0, sizeof(ifr));
242 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
243 	if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) {
244 		close(sock);
245 		return (-1);
246 	}
247 
248 	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
249 		ifr.ifr_flags |= IFF_UP;
250 		if (ioctl(sock, SIOCSIFFLAGS, (caddr_t)&ifr) == -1) {
251 			close(sock);
252 			return (-1);
253 		}
254 		close(sock);
255 		return (0);
256 	}
257 	close(sock);
258 	return (1);
259 }
260 
261 void
262 interface_link_forcedown(char *ifname)
263 {
264 	struct ifreq ifr;
265 	int sock;
266 
267 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
268 		error("Can't create socket");
269 
270 	memset(&ifr, 0, sizeof(ifr));
271 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
272 	if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) {
273 		close(sock);
274 		return;
275 	}
276 
277 	if ((ifr.ifr_flags & IFF_UP) == IFF_UP) {
278 		ifr.ifr_flags &= ~IFF_UP;
279 		if (ioctl(sock, SIOCSIFFLAGS, (caddr_t)&ifr) == -1) {
280 			close(sock);
281 			return;
282 		}
283 	}
284 
285 	close(sock);
286 }
287 
288 int
289 interface_status(char *ifname)
290 {
291 	struct ifreq ifr;
292 	struct ifmediareq ifmr;
293 	int sock;
294 
295 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
296 		error("Can't create socket");
297 
298 	/* get interface flags */
299 	memset(&ifr, 0, sizeof(ifr));
300 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
301 	if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
302 		warning("ioctl(SIOCGIFFLAGS) on %s: %m", ifname);
303 		goto inactive;
304 	}
305 
306 	/*
307 	 * if one of UP and RUNNING flags is dropped,
308 	 * the interface is not active.
309 	 */
310 	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
311 		goto inactive;
312 
313 	/* Next, check carrier on the interface, if possible */
314 	if (ifi->noifmedia)
315 		goto active;
316 	memset(&ifmr, 0, sizeof(ifmr));
317 	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
318 	if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
319 		/*
320 		 * EINVAL or ENOTTY simply means that the interface
321 		 * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
322 		 */
323 		if (errno != EINVAL && errno != ENOTTY)
324 			debug("ioctl(SIOCGIFMEDIA) on %s: %m", ifname);
325 
326 		ifi->noifmedia = 1;
327 		goto active;
328 	}
329 	if (ifmr.ifm_status & IFM_AVALID) {
330 		if (ifmr.ifm_status & IFM_ACTIVE)
331 			goto active;
332 		else
333 			goto inactive;
334 	}
335 inactive:
336 	close(sock);
337 	return (0);
338 active:
339 	close(sock);
340 	return (1);
341 }
342 
343 void
344 add_timeout(time_t when, void (*where)(void))
345 {
346 	struct timeout *t, *q;
347 
348 	/* See if this timeout supersedes an existing timeout. */
349 	t = NULL;
350 	for (q = timeouts; q; q = q->next) {
351 		if (q->func == where) {
352 			if (t)
353 				t->next = q->next;
354 			else
355 				timeouts = q->next;
356 			break;
357 		}
358 		t = q;
359 	}
360 
361 	/* If we didn't supersede a timeout, allocate a timeout
362 	   structure now. */
363 	if (!q) {
364 		if (free_timeouts) {
365 			q = free_timeouts;
366 			free_timeouts = q->next;
367 			q->func = where;
368 		} else {
369 			q = malloc(sizeof(struct timeout));
370 			if (!q)
371 				error("Can't allocate timeout structure!");
372 			q->func = where;
373 		}
374 	}
375 
376 	q->when = when;
377 
378 	/* Now sort this timeout into the timeout list. */
379 
380 	/* Beginning of list? */
381 	if (!timeouts || timeouts->when > q->when) {
382 		q->next = timeouts;
383 		timeouts = q;
384 		return;
385 	}
386 
387 	/* Middle of list? */
388 	for (t = timeouts; t->next; t = t->next) {
389 		if (t->next->when > q->when) {
390 			q->next = t->next;
391 			t->next = q;
392 			return;
393 		}
394 	}
395 
396 	/* End of list. */
397 	t->next = q;
398 	q->next = NULL;
399 }
400 
401 void
402 cancel_timeout(void (*where)(void))
403 {
404 	struct timeout *t, *q;
405 
406 	/* Look for this timeout on the list, and unlink it if we find it. */
407 	t = NULL;
408 	for (q = timeouts; q; q = q->next) {
409 		if (q->func == where) {
410 			if (t)
411 				t->next = q->next;
412 			else
413 				timeouts = q->next;
414 			break;
415 		}
416 		t = q;
417 	}
418 
419 	/* If we found the timeout, put it on the free list. */
420 	if (q) {
421 		q->next = free_timeouts;
422 		free_timeouts = q;
423 	}
424 }
425 
426 int
427 interface_link_status(char *ifname)
428 {
429 	struct ifmediareq ifmr;
430 	int sock;
431 
432 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
433 		error("Can't create socket");
434 
435 	memset(&ifmr, 0, sizeof(ifmr));
436 	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
437 	if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
438 		/* EINVAL/ENOTTY -> link state unknown. treat as active */
439 		if (errno != EINVAL && errno != ENOTTY)
440 			debug("ioctl(SIOCGIFMEDIA) on %s: %m", ifname);
441 		close(sock);
442 		return (1);
443 	}
444 	close(sock);
445 
446 	if (ifmr.ifm_status & IFM_AVALID) {
447 		if (ifmr.ifm_status & IFM_ACTIVE)
448 			return (1);
449 		else
450 			return (0);
451 	}
452 	return (1);
453 }
454