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