xref: /openbsd-src/usr.sbin/dhcrelay/dispatch.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: dispatch.c,v 1.10 2016/02/07 00:49:28 krw 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/types.h>
43 #include <sys/ioctl.h>
44 #include <sys/socket.h>
45 
46 #include <net/if.h>
47 #include <net/if_dl.h>
48 #include <net/if_media.h>
49 #include <net/if_types.h>
50 
51 #include <netinet/in.h>
52 
53 #include <errno.h>
54 #include <ifaddrs.h>
55 #include <limits.h>
56 #include <poll.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <syslog.h>
60 #include <unistd.h>
61 
62 #include "dhcp.h"
63 #include "dhcpd.h"
64 
65 struct protocol *protocols;
66 struct timeout *timeouts;
67 static struct timeout *free_timeouts;
68 static int interfaces_invalidated;
69 
70 void (*bootp_packet_handler)(struct interface_info *,
71     struct dhcp_packet *, int, unsigned int,
72     struct iaddr, struct hardware *);
73 
74 static int interface_status(struct interface_info *ifinfo);
75 
76 /*
77  * Use getifaddrs() to get a list of all the attached interfaces.  For
78  * each interface that's of type INET and not the loopback interface,
79  * register that interface with the network I/O software, figure out
80  * what subnet it's on, and add it to the list of interfaces.
81  */
82 void
83 discover_interfaces(struct interface_info *iface)
84 {
85 	struct sockaddr_in foo;
86 	struct ifaddrs *ifap, *ifa;
87 	struct ifreq *tif;
88 
89 	if (getifaddrs(&ifap) != 0)
90 		error("getifaddrs failed");
91 
92 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
93 		if ((ifa->ifa_flags & IFF_LOOPBACK) ||
94 		    (ifa->ifa_flags & IFF_POINTOPOINT) ||
95 		    (!(ifa->ifa_flags & IFF_UP)))
96 			continue;
97 
98 		if (strcmp(iface->name, ifa->ifa_name))
99 			continue;
100 
101 		/*
102 		 * If we have the capability, extract link information
103 		 * and record it in a linked list.
104 		 */
105 		if (ifa->ifa_addr->sa_family == AF_LINK) {
106 			struct sockaddr_dl *foo =
107 			    (struct sockaddr_dl *)ifa->ifa_addr;
108 			struct if_data *ifi =
109 			    (struct if_data *)ifa->ifa_data;
110 
111 			iface->index = foo->sdl_index;
112 			iface->hw_address.hlen = foo->sdl_alen;
113 			if (ifi->ifi_type == IFT_ENC)
114 				iface->hw_address.htype = HTYPE_IPSEC_TUNNEL;
115 			else
116 				iface->hw_address.htype = HTYPE_ETHER; /* XXX */
117 			memcpy(iface->hw_address.haddr,
118 			    LLADDR(foo), foo->sdl_alen);
119 		} else if (ifa->ifa_addr->sa_family == AF_INET) {
120 			struct iaddr addr;
121 
122 			memcpy(&foo, ifa->ifa_addr, sizeof(foo));
123 			if (foo.sin_addr.s_addr == htonl(INADDR_LOOPBACK))
124 				continue;
125 			if (!iface->ifp) {
126 				int len = IFNAMSIZ + ifa->ifa_addr->sa_len;
127 
128 				if ((tif = malloc(len)) == NULL)
129 					error("no space to remember ifp");
130 				strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ);
131 				memcpy(&tif->ifr_addr, ifa->ifa_addr,
132 				    ifa->ifa_addr->sa_len);
133 				iface->ifp = tif;
134 				iface->primary_address = foo.sin_addr;
135 			}
136 			addr.len = 4;
137 			memcpy(addr.iabuf, &foo.sin_addr.s_addr, addr.len);
138 		}
139 	}
140 
141 	if (!iface->ifp)
142 		error("%s: not found", iface->name);
143 
144 	/* Register the interface... */
145 	if_register_receive(iface);
146 	if_register_send(iface);
147 	add_protocol(iface->name, iface->rfdesc, got_one, iface);
148 	freeifaddrs(ifap);
149 }
150 
151 /*
152  * Wait for packets to come in using poll().  When a packet comes in,
153  * call receive_packet to receive the packet and possibly strip hardware
154  * addressing information from it, and then call through the
155  * bootp_packet_handler hook to try to do something with it.
156  */
157 void
158 dispatch(void)
159 {
160 	int count, i, to_msec, nfds = 0;
161 	struct protocol *l;
162 	struct pollfd *fds;
163 	time_t howlong;
164 
165 	nfds = 0;
166 	for (l = protocols; l; l = l->next)
167 		nfds++;
168 
169 	fds = calloc(nfds, sizeof(struct pollfd));
170 	if (fds == NULL)
171 		error("Can't allocate poll structures.");
172 
173 	do {
174 		/*
175 		 * Call any expired timeouts, and then if there's still
176 		 * a timeout registered, time out the select call then.
177 		 */
178 another:
179 		if (timeouts) {
180 			if (timeouts->when <= cur_time) {
181 				struct timeout *t = timeouts;
182 
183 				timeouts = timeouts->next;
184 				(*(t->func))(t->what);
185 				t->next = free_timeouts;
186 				free_timeouts = t;
187 				goto another;
188 			}
189 
190 			/*
191 			 * Figure timeout in milliseconds, and check for
192 			 * potential overflow, so we can cram into an
193 			 * int for poll, while not polling with a
194 			 * negative timeout and blocking indefinitely.
195 			 */
196 			howlong = timeouts->when - cur_time;
197 			if (howlong > INT_MAX / 1000)
198 				howlong = INT_MAX / 1000;
199 			to_msec = howlong * 1000;
200 		} else
201 			to_msec = -1;
202 
203 		/* Set up the descriptors to be polled. */
204 		i = 0;
205 
206 		for (l = protocols; l; l = l->next) {
207 			struct interface_info *ip = l->local;
208 
209 			if (ip && (l->handler != got_one || !ip->dead)) {
210 				fds[i].fd = l->fd;
211 				fds[i].events = POLLIN;
212 				fds[i].revents = 0;
213 				i++;
214 			}
215 		}
216 
217 		if (i == 0)
218 			error("No live interfaces to poll on - exiting.");
219 
220 		/* Wait for a packet or a timeout... XXX */
221 		count = poll(fds, nfds, to_msec);
222 
223 		/* Not likely to be transitory... */
224 		if (count == -1) {
225 			if (errno == EAGAIN || errno == EINTR) {
226 				time(&cur_time);
227 				continue;
228 			}
229 			else
230 				error("poll: %m");
231 		}
232 
233 		/* Get the current time... */
234 		time(&cur_time);
235 
236 		i = 0;
237 		for (l = protocols; l; l = l->next) {
238 			struct interface_info *ip = l->local;
239 
240 			if ((fds[i].revents & (POLLIN | POLLHUP))) {
241 				fds[i].revents = 0;
242 				if (ip && (l->handler != got_one ||
243 				    !ip->dead))
244 					(*(l->handler))(l);
245 				if (interfaces_invalidated)
246 					break;
247 			}
248 			i++;
249 		}
250 		interfaces_invalidated = 0;
251 	} while (1);
252 }
253 
254 
255 void
256 got_one(struct protocol *l)
257 {
258 	struct sockaddr_in from;
259 	struct hardware hfrom;
260 	struct iaddr ifrom;
261 	size_t result;
262 	union {
263 		/*
264 		 * Packet input buffer.  Must be as large as largest
265 		 * possible MTU.
266 		 */
267 		unsigned char packbuf[4095];
268 		struct dhcp_packet packet;
269 	} u;
270 	struct interface_info *ip = l->local;
271 
272 	if ((result = receive_packet(ip, u.packbuf, sizeof(u), &from,
273 	    &hfrom)) == -1) {
274 		warning("receive_packet failed on %s: %s", ip->name,
275 		    strerror(errno));
276 		ip->errors++;
277 		if ((!interface_status(ip)) ||
278 		    (ip->noifmedia && ip->errors > 20)) {
279 			/* our interface has gone away. */
280 			warning("Interface %s no longer appears valid.",
281 			    ip->name);
282 			ip->dead = 1;
283 			interfaces_invalidated = 1;
284 			close(l->fd);
285 			remove_protocol(l);
286 			free(ip);
287 		}
288 		return;
289 	}
290 	if (result == 0)
291 		return;
292 
293 	if (bootp_packet_handler) {
294 		ifrom.len = 4;
295 		memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
296 
297 		(*bootp_packet_handler)(ip, &u.packet, result,
298 		    from.sin_port, ifrom, &hfrom);
299 	}
300 }
301 
302 int
303 interface_status(struct interface_info *ifinfo)
304 {
305 	char *ifname = ifinfo->name;
306 	int ifsock = ifinfo->rfdesc;
307 	struct ifreq ifr;
308 	struct ifmediareq ifmr;
309 
310 	/* get interface flags */
311 	memset(&ifr, 0, sizeof(ifr));
312 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
313 	if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) == -1) {
314 		syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) on %s: %m", ifname);
315 		goto inactive;
316 	}
317 	/*
318 	 * if one of UP and RUNNING flags is dropped,
319 	 * the interface is not active.
320 	 */
321 	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
322 		goto inactive;
323 	}
324 	/* Next, check carrier on the interface, if possible */
325 	if (ifinfo->noifmedia)
326 		goto active;
327 	memset(&ifmr, 0, sizeof(ifmr));
328 	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
329 	if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
330 		if (errno != EINVAL) {
331 			syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
332 			    ifname);
333 
334 			ifinfo->noifmedia = 1;
335 			goto active;
336 		}
337 		/*
338 		 * EINVAL (or ENOTTY) simply means that the interface
339 		 * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
340 		 */
341 		ifinfo->noifmedia = 1;
342 		goto active;
343 	}
344 	if (ifmr.ifm_status & IFM_AVALID) {
345 		switch (ifmr.ifm_active & IFM_NMASK) {
346 		case IFM_ETHER:
347 			if (ifmr.ifm_status & IFM_ACTIVE)
348 				goto active;
349 			else
350 				goto inactive;
351 			break;
352 		default:
353 			goto inactive;
354 		}
355 	}
356 inactive:
357 	return (0);
358 active:
359 	return (1);
360 }
361 
362 /* Add a protocol to the list of protocols... */
363 void
364 add_protocol(char *name, int fd, void (*handler)(struct protocol *),
365     void *local)
366 {
367 	struct protocol *p;
368 
369 	p = malloc(sizeof(*p));
370 	if (!p)
371 		error("can't allocate protocol struct for %s", name);
372 
373 	p->fd = fd;
374 	p->handler = handler;
375 	p->local = local;
376 	p->next = protocols;
377 	protocols = p;
378 }
379 
380 void
381 remove_protocol(struct protocol *proto)
382 {
383 	struct protocol *p, *next, *prev;
384 
385 	prev = NULL;
386 	for (p = protocols; p; p = next) {
387 		next = p->next;
388 		if (p == proto) {
389 			if (prev)
390 				prev->next = p->next;
391 			else
392 				protocols = p->next;
393 			free(p);
394 		}
395 	}
396 }
397