xref: /freebsd/sbin/dhclient/dispatch.c (revision e9ac41698b2f322d55ccf9da50a3596edb2c1800)
1 /*	$OpenBSD: dispatch.c,v 1.31 2004/09/21 04:07:03 david Exp $	*/
2 
3 /*-
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  * Copyright 2004 Henning Brauer <henning@openbsd.org>
7  * Copyright (c) 1995, 1996, 1997, 1998, 1999
8  * The Internet Software Consortium.   All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of The Internet Software Consortium nor the names
20  *    of its contributors may be used to endorse or promote products derived
21  *    from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
24  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
25  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
28  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
31  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
32  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  * This software has been written for the Internet Software Consortium
38  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
39  * Enterprises.  To learn more about the Internet Software Consortium,
40  * see ``http://www.vix.com/isc''.  To learn more about Vixie
41  * Enterprises, see ``http://www.vix.com''.
42  */
43 
44 #include <sys/cdefs.h>
45 #include "dhcpd.h"
46 #include "privsep.h"
47 
48 #include <sys/ioctl.h>
49 
50 #include <assert.h>
51 #include <net/if_media.h>
52 #include <ifaddrs.h>
53 #include <poll.h>
54 
55 /* Assert that pointer p is aligned to at least align bytes */
56 #define assert_aligned(p, align) assert((((uintptr_t)p) & ((align) - 1)) == 0)
57 
58 static struct protocol *protocols;
59 static struct timeout *timeouts;
60 static struct timeout *free_timeouts;
61 static int interfaces_invalidated;
62 void (*bootp_packet_handler)(struct interface_info *,
63     struct dhcp_packet *, int, unsigned int,
64     struct iaddr, struct hardware *);
65 
66 static int interface_status(struct interface_info *ifinfo);
67 
68 /*
69  * Use getifaddrs() to get a list of all the attached interfaces.  For
70  * each interface that's of type INET and not the loopback interface,
71  * register that interface with the network I/O software, figure out
72  * what subnet it's on, and add it to the list of interfaces.
73  */
74 void
75 discover_interfaces(struct interface_info *iface)
76 {
77 	struct ifaddrs *ifap, *ifa;
78 	struct ifreq *tif;
79 
80 	if (getifaddrs(&ifap) != 0)
81 		error("getifaddrs failed");
82 
83 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
84 		if ((ifa->ifa_flags & IFF_LOOPBACK) ||
85 		    (ifa->ifa_flags & IFF_POINTOPOINT) ||
86 		    (!(ifa->ifa_flags & IFF_UP)))
87 			continue;
88 
89 		if (strcmp(iface->name, ifa->ifa_name))
90 			continue;
91 
92 		/*
93 		 * If we have the capability, extract link information
94 		 * and record it in a linked list.
95 		 */
96 		if (ifa->ifa_addr->sa_family == AF_LINK) {
97 			struct sockaddr_dl *foo;
98 
99 			/*
100 			 * The implementation of getifaddrs should guarantee
101 			 * this alignment
102 			 */
103 			assert_aligned(ifa->ifa_addr,
104 				       _Alignof(struct sockaddr_dl));
105 #ifdef __clang__
106 #pragma clang diagnostic push
107 #pragma clang diagnostic ignored "-Wcast-align"
108 #endif
109 			foo = (struct sockaddr_dl *)ifa->ifa_addr;
110 #ifdef __clang__
111 #pragma clang diagnostic pop
112 #endif
113 
114 			iface->index = foo->sdl_index;
115 			iface->hw_address.hlen = foo->sdl_alen;
116 			iface->hw_address.htype = HTYPE_ETHER; /* XXX */
117 			memcpy(iface->hw_address.haddr,
118 			    LLADDR(foo), foo->sdl_alen);
119 		}
120 		if (!iface->ifp) {
121 			if ((tif = calloc(1, sizeof(struct ifreq))) == NULL)
122 				error("no space to remember ifp");
123 			strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ);
124 			iface->ifp = tif;
125 		}
126 
127 	}
128 
129 	if (!iface->ifp)
130 		error("%s: not found", iface->name);
131 
132 	/* Register the interface... */
133 	if_register_receive(iface);
134 	if_register_send(iface);
135 	add_protocol(iface->name, iface->rfdesc, got_one, iface);
136 	freeifaddrs(ifap);
137 }
138 
139 void
140 reinitialize_interfaces(void)
141 {
142 	interfaces_invalidated = 1;
143 }
144 
145 /*
146  * Wait for packets to come in using poll().  When a packet comes in,
147  * call receive_packet to receive the packet and possibly strip hardware
148  * addressing information from it, and then call through the
149  * bootp_packet_handler hook to try to do something with it.
150  */
151 void
152 dispatch(void)
153 {
154 	int count, live_interfaces, i, to_msec, nfds = 0;
155 	struct protocol *l;
156 	struct pollfd *fds;
157 	time_t howlong;
158 
159 	for (l = protocols; l; l = l->next)
160 		nfds++;
161 
162 	fds = malloc(nfds * sizeof(struct pollfd));
163 	if (fds == NULL)
164 		error("Can't allocate poll structures.");
165 
166 	do {
167 		/*
168 		 * Call any expired timeouts, and then if there's still
169 		 * a timeout registered, time out the select call then.
170 		 */
171 another:
172 		if (timeouts) {
173 			struct timeout *t;
174 
175 			if (timeouts->when <= cur_time) {
176 				t = timeouts;
177 				timeouts = timeouts->next;
178 				(*(t->func))(t->what);
179 				t->next = free_timeouts;
180 				free_timeouts = t;
181 				goto another;
182 			}
183 
184 			/*
185 			 * Figure timeout in milliseconds, and check for
186 			 * potential overflow, so we can cram into an
187 			 * int for poll, while not polling with a
188 			 * negative timeout and blocking indefinitely.
189 			 */
190 			howlong = timeouts->when - cur_time;
191 			if (howlong > INT_MAX / 1000)
192 				howlong = INT_MAX / 1000;
193 			to_msec = howlong * 1000;
194 		} else
195 			to_msec = -1;
196 
197 		/* Set up the descriptors to be polled. */
198 		live_interfaces = 0;
199 		for (i = 0, l = protocols; l; l = l->next) {
200 			struct interface_info *ip = l->local;
201 
202 			if (ip == NULL || ip->dead)
203 				continue;
204 			fds[i].fd = l->fd;
205 			fds[i].events = POLLIN;
206 			fds[i].revents = 0;
207 			i++;
208 			if (l->handler == got_one)
209 				live_interfaces++;
210 		}
211 		if (live_interfaces == 0)
212 			error("No live interfaces to poll on - exiting.");
213 
214 		/* Wait for a packet or a timeout... XXX */
215 		count = poll(fds, nfds, to_msec);
216 
217 		/* Not likely to be transitory... */
218 		if (count == -1) {
219 			if (errno == EAGAIN || errno == EINTR) {
220 				time(&cur_time);
221 				continue;
222 			} else
223 				error("poll: %m");
224 		}
225 
226 		/* Get the current time... */
227 		time(&cur_time);
228 
229 		i = 0;
230 		for (l = protocols; l; l = l->next) {
231 			struct interface_info *ip;
232 			ip = l->local;
233 			if ((fds[i].revents & (POLLIN | POLLHUP))) {
234 				fds[i].revents = 0;
235 				if (ip && (l->handler != got_one ||
236 				    !ip->dead))
237 					(*(l->handler))(l);
238 				if (interfaces_invalidated)
239 					break;
240 			}
241 			i++;
242 		}
243 		interfaces_invalidated = 0;
244 	} while (1);
245 }
246 
247 
248 void
249 got_one(struct protocol *l)
250 {
251 	struct sockaddr_in from;
252 	struct hardware hfrom;
253 	struct iaddr ifrom;
254 	ssize_t result;
255 	union {
256 		/*
257 		 * Packet input buffer.  Must be as large as largest
258 		 * possible MTU.
259 		 */
260 		unsigned char packbuf[4095];
261 		struct dhcp_packet packet;
262 	} u;
263 	struct interface_info *ip = l->local;
264 
265 	if ((result = receive_packet(ip, u.packbuf, sizeof(u), &from,
266 	    &hfrom)) == -1) {
267 		warning("receive_packet failed on %s: %s", ip->name,
268 		    strerror(errno));
269 		ip->errors++;
270 		if ((!interface_status(ip)) ||
271 		    (ip->noifmedia && ip->errors > 20)) {
272 			/* our interface has gone away. */
273 			warning("Interface %s no longer appears valid.",
274 			    ip->name);
275 			ip->dead = 1;
276 			interfaces_invalidated = 1;
277 			close(l->fd);
278 			remove_protocol(l);
279 			free(ip);
280 		}
281 		return;
282 	}
283 	if (result == 0)
284 		return;
285 
286 	if (bootp_packet_handler) {
287 		ifrom.len = 4;
288 		memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
289 
290 		(*bootp_packet_handler)(ip, &u.packet, result,
291 		    from.sin_port, ifrom, &hfrom);
292 	}
293 }
294 
295 int
296 interface_status(struct interface_info *ifinfo)
297 {
298 	char *ifname = ifinfo->name;
299 	int ifsock = ifinfo->rfdesc;
300 	struct ifreq ifr;
301 	struct ifmediareq ifmr;
302 
303 	/* get interface flags */
304 	memset(&ifr, 0, sizeof(ifr));
305 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
306 	if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
307 		cap_syslog(capsyslog, LOG_ERR, "ioctl(SIOCGIFFLAGS) on %s: %m",
308 		    ifname);
309 		goto inactive;
310 	}
311 
312 	/*
313 	 * if one of UP and RUNNING flags is dropped,
314 	 * the interface is not active.
315 	 */
316 	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
317 		goto inactive;
318 
319 	/* Next, check carrier on the interface, if possible */
320 	if (ifinfo->noifmedia)
321 		goto active;
322 	memset(&ifmr, 0, sizeof(ifmr));
323 	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
324 	if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
325 		if (errno != EINVAL) {
326 			cap_syslog(capsyslog, LOG_DEBUG,
327 			    "ioctl(SIOCGIFMEDIA) on %s: %m", ifname);
328 			ifinfo->noifmedia = 1;
329 			goto active;
330 		}
331 		/*
332 		 * EINVAL (or ENOTTY) simply means that the interface
333 		 * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
334 		 */
335 		ifinfo->noifmedia = 1;
336 		goto active;
337 	}
338 	if (ifmr.ifm_status & IFM_AVALID) {
339 		switch (ifmr.ifm_active & IFM_NMASK) {
340 		case IFM_ETHER:
341 		case IFM_IEEE80211:
342 			if (ifmr.ifm_status & IFM_ACTIVE)
343 				goto active;
344 			else
345 				goto inactive;
346 			break;
347 		default:
348 			goto inactive;
349 		}
350 	}
351 inactive:
352 	return (0);
353 active:
354 	return (1);
355 }
356 
357 void
358 add_timeout(time_t when, void (*where)(void *), void *what)
359 {
360 	struct timeout *t, *q;
361 
362 	/* See if this timeout supersedes an existing timeout. */
363 	t = NULL;
364 	for (q = timeouts; q; q = q->next) {
365 		if (q->func == where && q->what == what) {
366 			if (t)
367 				t->next = q->next;
368 			else
369 				timeouts = q->next;
370 			break;
371 		}
372 		t = q;
373 	}
374 
375 	/* If we didn't supersede a timeout, allocate a timeout
376 	   structure now. */
377 	if (!q) {
378 		if (free_timeouts) {
379 			q = free_timeouts;
380 			free_timeouts = q->next;
381 			q->func = where;
382 			q->what = what;
383 		} else {
384 			q = malloc(sizeof(struct timeout));
385 			if (!q)
386 				error("Can't allocate timeout structure!");
387 			q->func = where;
388 			q->what = what;
389 		}
390 	}
391 
392 	q->when = when;
393 
394 	/* Now sort this timeout into the timeout list. */
395 
396 	/* Beginning of list? */
397 	if (!timeouts || timeouts->when > q->when) {
398 		q->next = timeouts;
399 		timeouts = q;
400 		return;
401 	}
402 
403 	/* Middle of list? */
404 	for (t = timeouts; t->next; t = t->next) {
405 		if (t->next->when > q->when) {
406 			q->next = t->next;
407 			t->next = q;
408 			return;
409 		}
410 	}
411 
412 	/* End of list. */
413 	t->next = q;
414 	q->next = NULL;
415 }
416 
417 void
418 cancel_timeout(void (*where)(void *), void *what)
419 {
420 	struct timeout *t, *q;
421 
422 	/* Look for this timeout on the list, and unlink it if we find it. */
423 	t = NULL;
424 	for (q = timeouts; q; q = q->next) {
425 		if (q->func == where && q->what == what) {
426 			if (t)
427 				t->next = q->next;
428 			else
429 				timeouts = q->next;
430 			break;
431 		}
432 		t = q;
433 	}
434 
435 	/* If we found the timeout, put it on the free list. */
436 	if (q) {
437 		q->next = free_timeouts;
438 		free_timeouts = q;
439 	}
440 }
441 
442 /* Add a protocol to the list of protocols... */
443 void
444 add_protocol(const char *name, int fd, void (*handler)(struct protocol *),
445     void *local)
446 {
447 	struct protocol *p;
448 
449 	p = malloc(sizeof(*p));
450 	if (!p)
451 		error("can't allocate protocol struct for %s", name);
452 
453 	p->fd = fd;
454 	p->handler = handler;
455 	p->local = local;
456 	p->next = protocols;
457 	protocols = p;
458 }
459 
460 void
461 remove_protocol(struct protocol *proto)
462 {
463 	struct protocol *p, *prev;
464 
465 	for (p = protocols, prev = NULL; p != NULL; prev = p, p = p->next) {
466 		if (p == proto) {
467 			if (prev == NULL)
468 				protocols = p->next;
469 			else
470 				prev->next = p->next;
471 			free(p);
472 			break;
473 		}
474 	}
475 }
476 
477 int
478 interface_link_status(char *ifname)
479 {
480 	struct ifmediareq ifmr;
481 	int sock;
482 
483 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
484 		error("Can't create socket");
485 
486 	memset(&ifmr, 0, sizeof(ifmr));
487 	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
488 	if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
489 		/* EINVAL -> link state unknown. treat as active */
490 		if (errno != EINVAL)
491 			cap_syslog(capsyslog, LOG_DEBUG,
492 			    "ioctl(SIOCGIFMEDIA) on %s: %m", ifname);
493 		close(sock);
494 		return (1);
495 	}
496 	close(sock);
497 
498 	if (ifmr.ifm_status & IFM_AVALID) {
499 		switch (ifmr.ifm_active & IFM_NMASK) {
500 		case IFM_ETHER:
501 		case IFM_IEEE80211:
502 			if (ifmr.ifm_status & IFM_ACTIVE)
503 				return (1);
504 			else
505 				return (0);
506 		}
507 	}
508 	return (1);
509 }
510 
511 void
512 interface_set_mtu_unpriv(int privfd, u_int16_t mtu)
513 {
514 	struct imsg_hdr hdr;
515 	struct buf *buf;
516 	int errs = 0;
517 
518 	hdr.code = IMSG_SET_INTERFACE_MTU;
519 	hdr.len = sizeof(hdr) +
520 		sizeof(u_int16_t);
521 
522 	if ((buf = buf_open(hdr.len)) == NULL)
523 		error("buf_open: %m");
524 
525 	errs += buf_add(buf, &hdr, sizeof(hdr));
526 	errs += buf_add(buf, &mtu, sizeof(mtu));
527 	if (errs)
528 		error("buf_add: %m");
529 
530 	if (buf_close(privfd, buf) == -1)
531 		error("buf_close: %m");
532 }
533 
534 void
535 interface_set_mtu_priv(char *ifname, u_int16_t mtu)
536 {
537 	struct ifreq ifr;
538 	int sock;
539 	u_int16_t old_mtu;
540 
541 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
542 		error("Can't create socket");
543 
544 	memset(&ifr, 0, sizeof(ifr));
545 	old_mtu = 0;
546 
547 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
548 
549 	if (ioctl(sock, SIOCGIFMTU, (caddr_t)&ifr) == -1)
550 		warning("SIOCGIFMTU failed (%s): %s", ifname,
551 			strerror(errno));
552 	else
553 		old_mtu = ifr.ifr_mtu;
554 
555 	if (mtu != old_mtu) {
556 		ifr.ifr_mtu = mtu;
557 
558 		if (ioctl(sock, SIOCSIFMTU, &ifr) == -1)
559 			warning("SIOCSIFMTU failed (%d): %s", mtu,
560 				strerror(errno));
561 	}
562 
563 	close(sock);
564 }
565