xref: /dragonfly/sbin/dhclient/dispatch.c (revision 0ca59c34)
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 			/* XXX profmakx: I am not sure whether updating the linkstate
168 			  here is the best idea, but it being not up to date leads
169 			  to a busy loop.
170 			  Alternatively we can just remove the link state check since
171 			  the link state is checked in got_one() later on
172 			 */
173 			ifi->linkstat = interface_status(ifi->name);
174 
175 			if (ifi && ifi->linkstat && ifi->rfdesc != -1)
176 				got_one();
177 		}
178 		if ((fds[1].revents & (POLLIN | POLLHUP))) {
179 			if (ifi)
180 				routehandler();
181 		}
182 	} while (1);
183 }
184 
185 void
186 got_one(void)
187 {
188 	struct sockaddr_in from;
189 	struct hardware hfrom;
190 	struct iaddr ifrom;
191 	ssize_t result;
192 
193 	if ((result = receive_packet(&from, &hfrom)) == -1) {
194 		warning("receive_packet failed on %s: %s", ifi->name,
195 		    strerror(errno));
196 		ifi->errors++;
197 		if ((!interface_status(ifi->name)) ||
198 		    (ifi->noifmedia && ifi->errors > 20)) {
199 			/* our interface has gone away. */
200 			error("Interface %s no longer appears valid.",
201 			    ifi->name);
202 		}
203 		return;
204 	}
205 	if (result == 0)
206 		return;
207 
208 	ifrom.len = 4;
209 	memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
210 
211 	do_packet(result, from.sin_port, ifrom, &hfrom);
212 }
213 
214 /*
215  * Normally its ok to force the interface link up, but don't do it
216  * if it is an 80211 interface.
217  */
218 int
219 interface_link_forceup(char *ifname)
220 {
221 	struct ifreq ifr;
222 	struct ifmediareq ifmr;
223 	int sock;
224 
225 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
226 		error("Can't create socket");
227 
228 	memset(&ifr, 0, sizeof(ifr));
229 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
230 	if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) {
231 		close(sock);
232 		return (-1);
233 	}
234 	memset(&ifmr, 0, sizeof(ifmr));
235 	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
236 	if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) != -1) {
237 		if (IFM_TYPE(ifmr.ifm_active) == IFM_IEEE80211) {
238 			return 0;
239 		}
240 	}
241 
242 	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
243 		ifr.ifr_flags |= IFF_UP;
244 		if (ioctl(sock, SIOCSIFFLAGS, (caddr_t)&ifr) == -1) {
245 			close(sock);
246 			return (-1);
247 		}
248 		close(sock);
249 		return (0);
250 	}
251 	close(sock);
252 	return (1);
253 }
254 
255 int
256 interface_status(char *ifname)
257 {
258 	struct ifreq ifr;
259 	struct ifmediareq ifmr;
260 	int sock;
261 
262 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
263 		error("Can't create socket");
264 
265 	/* get interface flags */
266 	memset(&ifr, 0, sizeof(ifr));
267 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
268 	if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) {
269 		error("ioctl(SIOCGIFFLAGS) on %s: %m", ifname);
270 	}
271 
272 	/*
273 	 * if one of UP and RUNNING flags is dropped,
274 	 * the interface is not active.
275 	 */
276 	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
277 		goto inactive;
278 
279 	/* Next, check carrier on the interface, if possible */
280 	if (ifi->noifmedia)
281 		goto active;
282 	memset(&ifmr, 0, sizeof(ifmr));
283 	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
284 	if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
285 		/*
286 		 * EINVAL or ENOTTY simply means that the interface
287 		 * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
288 		 */
289 #ifdef DEBUG
290 		if (errno != EINVAL && errno != ENOTTY)
291 			debug("ioctl(SIOCGIFMEDIA) on %s: %m", ifname);
292 #endif
293 		ifi->noifmedia = 1;
294 		goto active;
295 	}
296 	if (ifmr.ifm_status & IFM_AVALID) {
297 		if (ifmr.ifm_status & IFM_ACTIVE)
298 			goto active;
299 		else
300 			goto inactive;
301 	}
302 
303 	/* Assume 'active' if IFM_AVALID is not set. */
304 
305 active:
306 	close(sock);
307 	return (1);
308 inactive:
309 	close(sock);
310 	return (0);
311 }
312 
313 void
314 set_timeout(time_t when, void (*where)(void))
315 {
316 	timeout.when = when;
317 	timeout.func = where;
318 }
319 
320 void
321 set_timeout_interval(time_t secs, void (*where)(void))
322 {
323 	timeout.when = time(NULL) + secs;
324 	timeout.func = where;
325 }
326 
327 void
328 cancel_timeout(void)
329 {
330 	timeout.when = 0;
331 	timeout.func = NULL;
332 }
333 
334 int
335 subnet_exists(struct client_lease *l)
336 {
337 	struct ifaddrs *ifap, *ifa;
338 	in_addr_t mymask, myaddr, mynet, hismask, hisaddr, hisnet;
339 
340 	bcopy(l->options[DHO_SUBNET_MASK].data, &mymask, 4);
341 	bcopy(l->address.iabuf, &myaddr, 4);
342 	mynet = mymask & myaddr;
343 
344 	if (getifaddrs(&ifap) != 0)
345 		error("getifaddrs failed");
346 
347 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
348 		if (strcmp(ifi->name, ifa->ifa_name) == 0)
349 			continue;
350 
351 		if (ifa->ifa_addr->sa_family != AF_INET)
352 			continue;
353 
354 		hismask = ((struct sockaddr_in *)ifa->ifa_netmask)->
355 		    sin_addr.s_addr;
356 		hisaddr = ((struct sockaddr_in *)ifa->ifa_addr)->
357 		    sin_addr.s_addr;
358 		hisnet = hisaddr & hismask;
359 
360 		if (hisnet == 0)
361 			continue;
362 
363 		/* Would his packets go out *my* interface? */
364 		if (mynet == (hisaddr & mymask)) {
365 			note("interface %s already has the offered subnet!",
366 			    ifa->ifa_name);
367 			return (1);
368 		}
369 
370 		/* Would my packets go out *his* interface? */
371 		if (hisnet == (myaddr & hismask)) {
372 			note("interface %s already has the offered subnet!",
373 			    ifa->ifa_name);
374 			return (1);
375 		}
376 	}
377 
378 	freeifaddrs(ifap);
379 
380 	return (0);
381 }
382