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