xref: /dflybsd-src/sbin/dhclient/dispatch.c (revision c4be1ed54eff4542e7a8605007f73b381092df37)
1 /*     $OpenBSD: src/sbin/dhclient/dispatch.c,v 1.59 2012/10/11 08:05:05 sthen 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/ioctl.h>
43 
44 #include <net/if_media.h>
45 
46 #include <ifaddrs.h>
47 #include <poll.h>
48 
49 #include "dhcpd.h"
50 
51 struct timeout timeout;
52 
53 /*
54  * Use getifaddrs() to get a list of all the attached interfaces.  Find
55  * our interface on the list and store the interesting information about it.
56  */
57 void
58 discover_interface(void)
59 {
60 	struct ifaddrs *ifap, *ifa;
61 	struct ifreq *tif;
62 	int len = IFNAMSIZ + sizeof(struct sockaddr_storage);
63 
64 	if (getifaddrs(&ifap) != 0)
65 		error("getifaddrs failed");
66 
67 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
68 		if ((ifa->ifa_flags & IFF_LOOPBACK) ||
69 		    (ifa->ifa_flags & IFF_POINTOPOINT) ||
70 		    (!(ifa->ifa_flags & IFF_UP)))
71 			continue;
72 
73 		if (strcmp(ifi->name, ifa->ifa_name))
74 			continue;
75 
76 		/*
77 		 * If we have the capability, extract & save link information.
78 		 */
79 		if (ifa->ifa_addr->sa_family == AF_LINK) {
80 			struct sockaddr_dl *foo =
81 			    (struct sockaddr_dl *)ifa->ifa_addr;
82 
83 			ifi->index = foo->sdl_index;
84 			ifi->hw_address.hlen = foo->sdl_alen;
85 			ifi->hw_address.htype = HTYPE_ETHER; /* XXX */
86 			memcpy(ifi->hw_address.haddr,
87 			    LLADDR(foo), foo->sdl_alen);
88 		}
89 		if (!ifi->ifp) {
90 			if ((tif = malloc(len)) == NULL)
91 				error("no space to remember ifp");
92 			strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ);
93 			ifi->ifp = tif;
94 		}
95 	}
96 
97 	if (!ifi->ifp)
98 		error("%s: not found", ifi->name);
99 
100 	/* Register the interface... */
101 	if_register_receive();
102 	if_register_send();
103 	freeifaddrs(ifap);
104 }
105 
106 /*
107  * Loop waiting for packets, timeouts or routing messages.
108  */
109 void
110 dispatch(void)
111 {
112 	int count, to_msec;
113 	struct pollfd fds[2];
114 	time_t cur_time, howlong;
115 	void (*func)(void);
116 
117 	do {
118 		/*
119 		 * Call expired timeout, and then if there's still
120 		 * a timeout registered, time out the select call then.
121 		 */
122 another:
123 		if (!ifi)
124 			error("No interfaces available");
125 
126 		if (timeout.func) {
127 			time(&cur_time);
128 			if (timeout.when <= cur_time) {
129 				func = timeout.func;
130 				cancel_timeout();
131 				(*(func))();
132 				goto another;
133 			}
134 			/*
135 			 * Figure timeout in milliseconds, and check for
136 			 * potential overflow, so we can cram into an
137 			 * int for poll, while not polling with a
138 			 * negative timeout and blocking indefinitely.
139 			 */
140 			howlong = timeout.when - cur_time;
141 			if (howlong > INT_MAX / 1000)
142 				howlong = INT_MAX / 1000;
143 			to_msec = howlong * 1000;
144 		} else
145 			to_msec = -1;
146 
147 		/* Set up the descriptors to be polled. */
148 		if (!ifi || ifi->rfdesc == -1)
149 			error("No live interface to poll on");
150 
151 		fds[0].fd = ifi->rfdesc;
152 		fds[1].fd = routefd; /* Could be -1, which will be ignored. */
153 		fds[0].events = fds[1].events = POLLIN;
154 
155 		/* Wait for a packet or a timeout... XXX */
156 		count = poll(fds, 2, to_msec);
157 
158 		/* Not likely to be transitory... */
159 		if (count == -1) {
160 			if (errno == EAGAIN || errno == EINTR) {
161 				continue;
162 			} else
163 				error("poll: %m");
164 		}
165 
166 		if ((fds[0].revents & (POLLIN | POLLHUP))) {
167 			if (ifi && ifi->linkstat && ifi->rfdesc != -1)
168 				got_one();
169 		}
170 		if ((fds[1].revents & (POLLIN | POLLHUP))) {
171 			if (ifi)
172 				routehandler();
173 		}
174 	} while (1);
175 }
176 
177 void
178 got_one(void)
179 {
180 	struct sockaddr_in from;
181 	struct hardware hfrom;
182 	struct iaddr ifrom;
183 	ssize_t result;
184 
185 	if ((result = receive_packet(&from, &hfrom)) == -1) {
186 		warning("receive_packet failed on %s: %s", ifi->name,
187 		    strerror(errno));
188 		ifi->errors++;
189 		if ((!interface_status(ifi->name)) ||
190 		    (ifi->noifmedia && ifi->errors > 20)) {
191 			/* our interface has gone away. */
192 			error("Interface %s no longer appears valid.",
193 			    ifi->name);
194 		}
195 		return;
196 	}
197 	if (result == 0)
198 		return;
199 
200 	ifrom.len = 4;
201 	memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
202 
203 	do_packet(result, from.sin_port, ifrom, &hfrom);
204 }
205 
206 /*
207  * Normally its ok to force the interface link up, but don't do it
208  * if it is an 80211 interface.
209  */
210 int
211 interface_link_forceup(char *ifname)
212 {
213 	struct ifreq ifr;
214 	struct ifmediareq ifmr;
215 	int sock;
216 
217 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
218 		error("Can't create socket");
219 
220 	memset(&ifr, 0, sizeof(ifr));
221 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
222 	if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) {
223 		close(sock);
224 		return (-1);
225 	}
226 	memset(&ifmr, 0, sizeof(ifmr));
227 	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
228 	if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) != -1) {
229 		if (IFM_TYPE(ifmr.ifm_active) == IFM_IEEE80211) {
230 			return 0;
231 		}
232 	}
233 
234 	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
235 		ifr.ifr_flags |= IFF_UP;
236 		if (ioctl(sock, SIOCSIFFLAGS, (caddr_t)&ifr) == -1) {
237 			close(sock);
238 			return (-1);
239 		}
240 		close(sock);
241 		return (0);
242 	}
243 	close(sock);
244 	return (1);
245 }
246 
247 int
248 interface_status(char *ifname)
249 {
250 	struct ifreq ifr;
251 	struct ifmediareq ifmr;
252 	int sock;
253 
254 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
255 		error("Can't create socket");
256 
257 	/* get interface flags */
258 	memset(&ifr, 0, sizeof(ifr));
259 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
260 	if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) {
261 		error("ioctl(SIOCGIFFLAGS) on %s: %m", ifname);
262 	}
263 
264 	/*
265 	 * if one of UP and RUNNING flags is dropped,
266 	 * the interface is not active.
267 	 */
268 	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
269 		goto inactive;
270 
271 	/* Next, check carrier on the interface, if possible */
272 	if (ifi->noifmedia)
273 		goto active;
274 	memset(&ifmr, 0, sizeof(ifmr));
275 	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
276 	if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
277 		/*
278 		 * EINVAL or ENOTTY simply means that the interface
279 		 * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
280 		 */
281 #ifdef DEBUG
282 		if (errno != EINVAL && errno != ENOTTY)
283 			debug("ioctl(SIOCGIFMEDIA) on %s: %m", ifname);
284 #endif
285 		ifi->noifmedia = 1;
286 		goto active;
287 	}
288 	if (ifmr.ifm_status & IFM_AVALID) {
289 		if (ifmr.ifm_status & IFM_ACTIVE)
290 			goto active;
291 		else
292 			goto inactive;
293 	}
294 
295 	/* Assume 'active' if IFM_AVALID is not set. */
296 
297 active:
298 	close(sock);
299 	return (1);
300 inactive:
301 	close(sock);
302 	return (0);
303 }
304 
305 void
306 set_timeout(time_t when, void (*where)(void))
307 {
308 	timeout.when = when;
309 	timeout.func = where;
310 }
311 
312 void
313 set_timeout_interval(time_t secs, void (*where)(void))
314 {
315 	timeout.when = time(NULL) + secs;
316 	timeout.func = where;
317 }
318 
319 void
320 cancel_timeout(void)
321 {
322 	timeout.when = 0;
323 	timeout.func = NULL;
324 }
325 
326 int
327 subnet_exists(struct client_lease *l)
328 {
329 	struct ifaddrs *ifap, *ifa;
330 	in_addr_t mymask, myaddr, mynet, hismask, hisaddr, hisnet;
331 
332 	bcopy(l->options[DHO_SUBNET_MASK].data, &mymask, 4);
333 	bcopy(l->address.iabuf, &myaddr, 4);
334 	mynet = mymask & myaddr;
335 
336 	if (getifaddrs(&ifap) != 0)
337 		error("getifaddrs failed");
338 
339 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
340 		if (strcmp(ifi->name, ifa->ifa_name) == 0)
341 			continue;
342 
343 		if (ifa->ifa_addr->sa_family != AF_INET)
344 			continue;
345 
346 		hismask = ((struct sockaddr_in *)ifa->ifa_netmask)->
347 		    sin_addr.s_addr;
348 		hisaddr = ((struct sockaddr_in *)ifa->ifa_addr)->
349 		    sin_addr.s_addr;
350 		hisnet = hisaddr & hismask;
351 
352 		if (hisnet == 0)
353 			continue;
354 
355 		/* Would his packets go out *my* interface? */
356 		if (mynet == (hisaddr & mymask)) {
357 			note("interface %s already has the offered subnet!",
358 			    ifa->ifa_name);
359 			return (1);
360 		}
361 
362 		/* Would my packets go out *his* interface? */
363 		if (hisnet == (myaddr & hismask)) {
364 			note("interface %s already has the offered subnet!",
365 			    ifa->ifa_name);
366 			return (1);
367 		}
368 	}
369 
370 	freeifaddrs(ifap);
371 
372 	return (0);
373 }
374