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