- Add support for adapters added after DHCP is started
[reactos.git] / reactos / base / services / dhcp / dispatch.c
1 /* $OpenBSD: dispatch.c,v 1.31 2004/09/21 04:07:03 david 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 "rosdhcp.h"
43 #include "dhcpd.h"
44 //#include <sys/ioctl.h>
45
46 //#include <net/if_media.h>
47 //#include <ifaddrs.h>
48 //#include <poll.h>
49
50 struct protocol *protocols = NULL;
51 struct timeout *timeouts = NULL;
52 static struct timeout *free_timeouts = NULL;
53 void (*bootp_packet_handler)(struct interface_info *,
54 struct dhcp_packet *, int, unsigned int,
55 struct iaddr, struct hardware *);
56
57 /*
58 * Wait for packets to come in using poll(). When a packet comes in,
59 * call receive_packet to receive the packet and possibly strip hardware
60 * addressing information from it, and then call through the
61 * bootp_packet_handler hook to try to do something with it.
62 */
63 void
64 dispatch(void)
65 {
66 int count, i, to_msec, nfds;
67 struct protocol *l;
68 fd_set fds;
69 time_t howlong, cur_time;
70 struct timeval timeval;
71
72 ApiLock();
73
74 do {
75 /*
76 * Call any expired timeouts, and then if there's still
77 * a timeout registered, time out the select call then.
78 */
79 another:
80 AdapterDiscover();
81
82 for (l = protocols, nfds = 0; l; l = l->next)
83 nfds++;
84
85 FD_ZERO(&fds);
86
87 time(&cur_time);
88
89 if (timeouts) {
90 struct timeout *t;
91
92 if (timeouts->when <= cur_time) {
93 t = timeouts;
94 timeouts = timeouts->next;
95 (*(t->func))(t->what);
96 t->next = free_timeouts;
97 free_timeouts = t;
98 goto another;
99 }
100
101 /*
102 * Figure timeout in milliseconds, and check for
103 * potential overflow, so we can cram into an
104 * int for poll, while not polling with a
105 * negative timeout and blocking indefinitely.
106 */
107 howlong = timeouts->when - cur_time;
108 if (howlong > INT_MAX / 1000)
109 howlong = INT_MAX / 1000;
110 to_msec = howlong * 1000;
111 } else
112 to_msec = -1;
113
114 /* Set up the descriptors to be polled. */
115 for (i = 0, l = protocols; l; l = l->next) {
116 struct interface_info *ip = l->local;
117
118 if (ip && (l->handler != got_one || !ip->dead)) {
119 FD_SET(l->fd, &fds);
120 i++;
121 }
122 }
123
124 if (i == 0) {
125 /* No interfaces for now, set the select timeout reasonably so
126 * we can recover from that condition later. */
127 timeval.tv_sec = 5;
128 timeval.tv_usec = 0;
129 } else {
130 /* Wait for a packet or a timeout... XXX */
131 timeval.tv_sec = to_msec / 1000;
132 timeval.tv_usec = (to_msec % 1000) * 1000;
133 }
134
135 ApiUnlock();
136
137 count = select(nfds, &fds, NULL, NULL, &timeval);
138
139 DH_DbgPrint(MID_TRACE,("Select: %d\n", count));
140
141 /* Review poll output */
142 for (i = 0, l = protocols; l; l = l->next) {
143 struct interface_info *ip = l->local;
144
145 if (ip && (l->handler != got_one || !ip->dead)) {
146 DH_DbgPrint
147 (MID_TRACE,
148 ("set(%d) -> %s\n",
149 l->fd, FD_ISSET(l->fd, &fds) ? "true" : "false"));
150 i++;
151 }
152 }
153
154
155 ApiLock();
156
157 /* Not likely to be transitory... */
158 if (count == SOCKET_ERROR) {
159 if (errno == EAGAIN || errno == EINTR) {
160 continue;
161 } else {
162 error("poll: %s", strerror(errno));
163 break;
164 }
165 }
166
167 i = 0;
168 for (l = protocols; l; l = l->next) {
169 struct interface_info *ip;
170 ip = l->local;
171 if (FD_ISSET(l->fd, &fds)) {
172 if (ip && (l->handler != got_one ||
173 !ip->dead)) {
174 DH_DbgPrint(MID_TRACE,("Handling %x\n", l));
175 (*(l->handler))(l);
176 }
177 i++;
178 }
179 }
180 } while (1);
181
182 ApiUnlock(); /* Not reached currently */
183 }
184
185 void
186 got_one(struct protocol *l)
187 {
188 struct sockaddr_in from;
189 struct hardware hfrom;
190 struct iaddr ifrom;
191 ssize_t result;
192 union {
193 /*
194 * Packet input buffer. Must be as large as largest
195 * possible MTU.
196 */
197 unsigned char packbuf[4095];
198 struct dhcp_packet packet;
199 } u;
200 struct interface_info *ip = l->local;
201 PDHCP_ADAPTER adapter;
202
203 if ((result = receive_packet(ip, u.packbuf, sizeof(u), &from,
204 &hfrom)) == -1) {
205 warning("receive_packet failed on %s: %s", ip->name,
206 strerror(errno));
207 ip->errors++;
208 if (ip->errors > 20) {
209 /* our interface has gone away. */
210 warning("Interface %s no longer appears valid.",
211 ip->name);
212 ip->dead = 1;
213 close(l->fd);
214 remove_protocol(l);
215 adapter = AdapterFindInfo(ip);
216 if (adapter) {
217 RemoveEntryList(&adapter->ListEntry);
218 free(adapter);
219 }
220 }
221 return;
222 }
223 if (result == 0)
224 return;
225
226 if (bootp_packet_handler) {
227 ifrom.len = 4;
228 memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
229
230
231 adapter = AdapterFindByHardwareAddress(u.packet.chaddr,
232 u.packet.hlen);
233
234 if (!adapter) {
235 warning("Discarding packet with a non-matching target physical address\n");
236 return;
237 }
238
239 (*bootp_packet_handler)(&adapter->DhclientInfo, &u.packet, result,
240 from.sin_port, ifrom, &hfrom);
241 }
242 }
243
244 void
245 add_timeout(time_t when, void (*where)(void *), void *what)
246 {
247 struct timeout *t, *q;
248
249 DH_DbgPrint(MID_TRACE,("Adding timeout %x %p %x\n", when, where, what));
250 /* See if this timeout supersedes an existing timeout. */
251 t = NULL;
252 for (q = timeouts; q; q = q->next) {
253 if (q->func == where && q->what == what) {
254 if (t)
255 t->next = q->next;
256 else
257 timeouts = q->next;
258 break;
259 }
260 t = q;
261 }
262
263 /* If we didn't supersede a timeout, allocate a timeout
264 structure now. */
265 if (!q) {
266 if (free_timeouts) {
267 q = free_timeouts;
268 free_timeouts = q->next;
269 q->func = where;
270 q->what = what;
271 } else {
272 q = malloc(sizeof(struct timeout));
273 if (!q)
274 error("Can't allocate timeout structure!");
275 q->func = where;
276 q->what = what;
277 }
278 }
279
280 q->when = when;
281
282 /* Now sort this timeout into the timeout list. */
283
284 /* Beginning of list? */
285 if (!timeouts || timeouts->when > q->when) {
286 q->next = timeouts;
287 timeouts = q;
288 return;
289 }
290
291 /* Middle of list? */
292 for (t = timeouts; t->next; t = t->next) {
293 if (t->next->when > q->when) {
294 q->next = t->next;
295 t->next = q;
296 return;
297 }
298 }
299
300 /* End of list. */
301 t->next = q;
302 q->next = NULL;
303 }
304
305 void
306 cancel_timeout(void (*where)(void *), void *what)
307 {
308 struct timeout *t, *q;
309
310 /* Look for this timeout on the list, and unlink it if we find it. */
311 t = NULL;
312 for (q = timeouts; q; q = q->next) {
313 if (q->func == where && q->what == what) {
314 if (t)
315 t->next = q->next;
316 else
317 timeouts = q->next;
318 break;
319 }
320 t = q;
321 }
322
323 /* If we found the timeout, put it on the free list. */
324 if (q) {
325 q->next = free_timeouts;
326 free_timeouts = q;
327 }
328 }
329
330 /* Add a protocol to the list of protocols... */
331 void
332 add_protocol(char *name, int fd, void (*handler)(struct protocol *),
333 void *local)
334 {
335 struct protocol *p;
336
337 p = malloc(sizeof(*p));
338 if (!p)
339 error("can't allocate protocol struct for %s", name);
340
341 p->fd = fd;
342 p->handler = handler;
343 p->local = local;
344 p->next = protocols;
345 protocols = p;
346 }
347
348 void
349 remove_protocol(struct protocol *proto)
350 {
351 struct protocol *p, *next, *prev;
352
353 prev = NULL;
354 for (p = protocols; p; p = next) {
355 next = p->next;
356 if (p == proto) {
357 if (prev)
358 prev->next = p->next;
359 else
360 protocols = p->next;
361 free(p);
362 }
363 }
364 }
365
366 struct protocol *
367 find_protocol_by_adapter(struct interface_info *info)
368 {
369 struct protocol *p;
370
371 for( p = protocols; p; p = p->next ) {
372 if( p->local == (void *)info ) return p;
373 }
374
375 return NULL;
376 }
377
378 int
379 interface_link_status(char *ifname)
380 {
381 #if 0
382 struct ifmediareq ifmr;
383 int sock;
384
385 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
386 error("Can't create socket");
387
388 memset(&ifmr, 0, sizeof(ifmr));
389 strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
390 if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
391 /* EINVAL -> link state unknown. treat as active */
392 if (errno != EINVAL)
393 syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
394 ifname);
395 close(sock);
396 return (1);
397 }
398 close(sock);
399
400 if (ifmr.ifm_status & IFM_AVALID) {
401 if ((ifmr.ifm_active & IFM_NMASK) == IFM_ETHER) {
402 if (ifmr.ifm_status & IFM_ACTIVE)
403 return (1);
404 else
405 return (0);
406 }
407 }
408 #endif
409 return (1);
410 }