-revert janderwalds change until because it breaks the gcc 4.x build
[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 static int interfaces_invalidated = FALSE;
54 void (*bootp_packet_handler)(struct interface_info *,
55 struct dhcp_packet *, int, unsigned int,
56 struct iaddr, struct hardware *);
57
58 static int interface_status(struct interface_info *ifinfo);
59
60 /*
61 * Use getifaddrs() to get a list of all the attached interfaces. For
62 * each interface that's of type INET and not the loopback interface,
63 * register that interface with the network I/O software, figure out
64 * what subnet it's on, and add it to the list of interfaces.
65 */
66 void
67 discover_interfaces(struct interface_info *iface)
68 {
69 PDHCP_ADAPTER Adapter = AdapterFindInfo( iface );
70
71 if_register_receive(iface);
72 if_register_send(iface);
73
74 if( Adapter->DhclientState.state != S_STATIC ) {
75 add_protocol(iface->name, iface->rfdesc, got_one, iface);
76 iface->client->state = S_INIT;
77 state_reboot(iface);
78 }
79 }
80
81 void
82 reinitialize_interfaces(void)
83 {
84 interfaces_invalidated = 1;
85 }
86
87 /*
88 * Wait for packets to come in using poll(). When a packet comes in,
89 * call receive_packet to receive the packet and possibly strip hardware
90 * addressing information from it, and then call through the
91 * bootp_packet_handler hook to try to do something with it.
92 */
93 void
94 dispatch(void)
95 {
96 int count, i, to_msec, nfds = 0;
97 struct protocol *l;
98 fd_set fds;
99 time_t howlong;
100 struct timeval timeval;
101
102 ApiLock();
103
104 for (l = protocols; l; l = l->next)
105 nfds++;
106
107 FD_ZERO(&fds);
108
109 do {
110 /*
111 * Call any expired timeouts, and then if there's still
112 * a timeout registered, time out the select call then.
113 */
114 another:
115 if (timeouts) {
116 struct timeout *t;
117
118 if (timeouts->when <= cur_time) {
119 t = timeouts;
120 timeouts = timeouts->next;
121 (*(t->func))(t->what);
122 t->next = free_timeouts;
123 free_timeouts = t;
124 goto another;
125 }
126
127 /*
128 * Figure timeout in milliseconds, and check for
129 * potential overflow, so we can cram into an
130 * int for poll, while not polling with a
131 * negative timeout and blocking indefinitely.
132 */
133 howlong = timeouts->when - cur_time;
134 if (howlong > INT_MAX / 1000)
135 howlong = INT_MAX / 1000;
136 to_msec = howlong * 1000;
137 } else
138 to_msec = -1;
139
140 /* Set up the descriptors to be polled. */
141 for (i = 0, l = protocols; l; l = l->next) {
142 struct interface_info *ip = l->local;
143
144 if (ip && (l->handler != got_one || !ip->dead)) {
145 FD_SET(l->fd, &fds);
146 i++;
147 }
148 }
149
150 if (i == 0) {
151 /* No interfaces for now, set the select timeout reasonably so
152 * we can recover from that condition later. */
153 timeval.tv_sec = 5;
154 timeval.tv_usec = 0;
155 } else {
156 /* Wait for a packet or a timeout... XXX */
157 timeval.tv_sec = to_msec / 1000;
158 timeval.tv_usec = (to_msec % 1000) * 1000;
159 }
160
161 ApiUnlock();
162
163 count = select(nfds, &fds, NULL, NULL, &timeval);
164
165 DH_DbgPrint(MID_TRACE,("Select: %d\n", count));
166
167 /* Review poll output */
168 for (i = 0, l = protocols; l; l = l->next) {
169 struct interface_info *ip = l->local;
170
171 if (ip && (l->handler != got_one || !ip->dead)) {
172 DH_DbgPrint
173 (MID_TRACE,
174 ("set(%d) -> %s\n",
175 l->fd, FD_ISSET(l->fd, &fds) ? "true" : "false"));
176 i++;
177 }
178 }
179
180
181 ApiLock();
182
183 /* Not likely to be transitory... */
184 if (count == SOCKET_ERROR) {
185 if (errno == EAGAIN || errno == EINTR) {
186 time(&cur_time);
187 continue;
188 } else {
189 error("poll: %m");
190 break;
191 }
192 }
193
194 /* Get the current time... */
195 time(&cur_time);
196
197 i = 0;
198 for (l = protocols; l; l = l->next) {
199 struct interface_info *ip;
200 ip = l->local;
201 if (FD_ISSET(l->fd, &fds)) {
202 if (ip && (l->handler != got_one ||
203 !ip->dead)) {
204 DH_DbgPrint(MID_TRACE,("Handling %x\n", l));
205 (*(l->handler))(l);
206 if (interfaces_invalidated)
207 break;
208 }
209 i++;
210 }
211 interfaces_invalidated = 0;
212 }
213 } while (1);
214
215 ApiUnlock(); /* Not reached currently */
216 }
217
218 void
219 got_one(struct protocol *l)
220 {
221 struct sockaddr_in from;
222 struct hardware hfrom;
223 struct iaddr ifrom;
224 ssize_t result;
225 union {
226 /*
227 * Packet input buffer. Must be as large as largest
228 * possible MTU.
229 */
230 unsigned char packbuf[4095];
231 struct dhcp_packet packet;
232 } u;
233 struct interface_info *ip = l->local;
234
235 if ((result = receive_packet(ip, u.packbuf, sizeof(u), &from,
236 &hfrom)) == -1) {
237 warning("receive_packet failed on %s: %s", ip->name,
238 strerror(errno));
239 ip->errors++;
240 if ((!interface_status(ip)) ||
241 (ip->noifmedia && ip->errors > 20)) {
242 /* our interface has gone away. */
243 warning("Interface %s no longer appears valid.",
244 ip->name);
245 ip->dead = 1;
246 interfaces_invalidated = 1;
247 close(l->fd);
248 remove_protocol(l);
249 free(ip);
250 }
251 return;
252 }
253 if (result == 0)
254 return;
255
256 if (bootp_packet_handler) {
257 ifrom.len = 4;
258 memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
259
260 (*bootp_packet_handler)(ip, &u.packet, result,
261 from.sin_port, ifrom, &hfrom);
262 }
263 }
264
265 #if 0
266 int
267 interface_status(struct interface_info *ifinfo)
268 {
269 char *ifname = ifinfo->name;
270 int ifsock = ifinfo->rfdesc;
271 struct ifreq ifr;
272 struct ifmediareq ifmr;
273
274 /* get interface flags */
275 memset(&ifr, 0, sizeof(ifr));
276 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
277 if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
278 syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) on %s: %m", ifname);
279 goto inactive;
280 }
281
282 /*
283 * if one of UP and RUNNING flags is dropped,
284 * the interface is not active.
285 */
286 if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
287 goto inactive;
288
289 /* Next, check carrier on the interface, if possible */
290 if (ifinfo->noifmedia)
291 goto active;
292 memset(&ifmr, 0, sizeof(ifmr));
293 strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
294 if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
295 if (errno != EINVAL) {
296 syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
297 ifname);
298
299 ifinfo->noifmedia = 1;
300 goto active;
301 }
302 /*
303 * EINVAL (or ENOTTY) simply means that the interface
304 * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
305 */
306 ifinfo->noifmedia = 1;
307 goto active;
308 }
309 if (ifmr.ifm_status & IFM_AVALID) {
310 switch (ifmr.ifm_active & IFM_NMASK) {
311 case IFM_ETHER:
312 if (ifmr.ifm_status & IFM_ACTIVE)
313 goto active;
314 else
315 goto inactive;
316 break;
317 default:
318 goto inactive;
319 }
320 }
321 inactive:
322 return (0);
323 active:
324 return (1);
325 }
326 #else
327 int
328 interface_status(struct interface_info *ifinfo)
329 {
330 return (1);
331 }
332 #endif
333
334 void
335 add_timeout(time_t when, void (*where)(void *), void *what)
336 {
337 struct timeout *t, *q;
338
339 DH_DbgPrint(MID_TRACE,("Adding timeout %x %p %x\n", when, where, what));
340 /* See if this timeout supersedes an existing timeout. */
341 t = NULL;
342 for (q = timeouts; q; q = q->next) {
343 if (q->func == where && q->what == what) {
344 if (t)
345 t->next = q->next;
346 else
347 timeouts = q->next;
348 break;
349 }
350 t = q;
351 }
352
353 /* If we didn't supersede a timeout, allocate a timeout
354 structure now. */
355 if (!q) {
356 if (free_timeouts) {
357 q = free_timeouts;
358 free_timeouts = q->next;
359 q->func = where;
360 q->what = what;
361 } else {
362 q = malloc(sizeof(struct timeout));
363 if (!q)
364 error("Can't allocate timeout structure!");
365 q->func = where;
366 q->what = what;
367 }
368 }
369
370 q->when = when;
371
372 /* Now sort this timeout into the timeout list. */
373
374 /* Beginning of list? */
375 if (!timeouts || timeouts->when > q->when) {
376 q->next = timeouts;
377 timeouts = q;
378 return;
379 }
380
381 /* Middle of list? */
382 for (t = timeouts; t->next; t = t->next) {
383 if (t->next->when > q->when) {
384 q->next = t->next;
385 t->next = q;
386 return;
387 }
388 }
389
390 /* End of list. */
391 t->next = q;
392 q->next = NULL;
393 }
394
395 void
396 cancel_timeout(void (*where)(void *), void *what)
397 {
398 struct timeout *t, *q;
399
400 /* Look for this timeout on the list, and unlink it if we find it. */
401 t = NULL;
402 for (q = timeouts; q; q = q->next) {
403 if (q->func == where && q->what == what) {
404 if (t)
405 t->next = q->next;
406 else
407 timeouts = q->next;
408 break;
409 }
410 t = q;
411 }
412
413 /* If we found the timeout, put it on the free list. */
414 if (q) {
415 q->next = free_timeouts;
416 free_timeouts = q;
417 }
418 }
419
420 /* Add a protocol to the list of protocols... */
421 void
422 add_protocol(char *name, int fd, void (*handler)(struct protocol *),
423 void *local)
424 {
425 struct protocol *p;
426
427 p = malloc(sizeof(*p));
428 if (!p)
429 error("can't allocate protocol struct for %s", name);
430
431 p->fd = fd;
432 p->handler = handler;
433 p->local = local;
434 p->next = protocols;
435 protocols = p;
436 }
437
438 void
439 remove_protocol(struct protocol *proto)
440 {
441 struct protocol *p, *next, *prev;
442
443 prev = NULL;
444 for (p = protocols; p; p = next) {
445 next = p->next;
446 if (p == proto) {
447 if (prev)
448 prev->next = p->next;
449 else
450 protocols = p->next;
451 free(p);
452 }
453 }
454 }
455
456 struct protocol *
457 find_protocol_by_adapter(struct interface_info *info)
458 {
459 struct protocol *p;
460
461 for( p = protocols; p; p = p->next ) {
462 if( p->local == (void *)info ) return p;
463 }
464
465 return NULL;
466 }
467
468 int
469 interface_link_status(char *ifname)
470 {
471 #if 0
472 struct ifmediareq ifmr;
473 int sock;
474
475 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
476 error("Can't create socket");
477
478 memset(&ifmr, 0, sizeof(ifmr));
479 strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
480 if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
481 /* EINVAL -> link state unknown. treat as active */
482 if (errno != EINVAL)
483 syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
484 ifname);
485 close(sock);
486 return (1);
487 }
488 close(sock);
489
490 if (ifmr.ifm_status & IFM_AVALID) {
491 if ((ifmr.ifm_active & IFM_NMASK) == IFM_ETHER) {
492 if (ifmr.ifm_status & IFM_ACTIVE)
493 return (1);
494 else
495 return (0);
496 }
497 }
498 #endif
499 return (1);
500 }