Merge branch 'vendor/OPENSSH'
[dragonfly.git] / sbin / dhclient / dispatch.c
1 /*      $OpenBSD: dispatch.c,v 1.41 2008/05/09 05:19:14 reyk Exp $      */
2 /*      $DragonFly: src/sbin/dhclient/dispatch.c,v 1.1 2008/08/30 16:07:58 hasso Exp $  */
3
4 /*
5  * Copyright 2004 Henning Brauer <henning@openbsd.org>
6  * Copyright (c) 1995, 1996, 1997, 1998, 1999
7  * The Internet Software Consortium.   All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of The Internet Software Consortium nor the names
19  *    of its contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * This software has been written for the Internet Software Consortium
37  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38  * Enterprises.  To learn more about the Internet Software Consortium,
39  * see ``http://www.vix.com/isc''.  To learn more about Vixie
40  * Enterprises, see ``http://www.vix.com''.
41  */
42
43 #include <sys/ioctl.h>
44
45 #include <net/if_media.h>
46
47 #include <ifaddrs.h>
48 #include <poll.h>
49
50 #include "dhcpd.h"
51
52 struct timeout *timeouts;
53 static struct timeout *free_timeouts;
54 static int interfaces_invalidated;
55
56 /*
57  * Use getifaddrs() to get a list of all the attached interfaces.  For
58  * each interface that's of type INET and not the loopback interface,
59  * register that interface with the network I/O software, figure out
60  * what subnet it's on, and add it to the list of interfaces.
61  */
62 void
63 discover_interface(void)
64 {
65         struct ifaddrs *ifap, *ifa;
66         struct ifreq *tif;
67         int len = IFNAMSIZ + sizeof(struct sockaddr_storage);
68
69         if (getifaddrs(&ifap) != 0)
70                 error("getifaddrs failed");
71
72         for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
73                 if ((ifa->ifa_flags & IFF_LOOPBACK) ||
74                     (ifa->ifa_flags & IFF_POINTOPOINT) ||
75                     (!(ifa->ifa_flags & IFF_UP)))
76                         continue;
77
78                 if (strcmp(ifi->name, ifa->ifa_name))
79                         continue;
80
81                 /*
82                  * If we have the capability, extract link information
83                  * and record it in a linked list.
84                  */
85                 if (ifa->ifa_addr->sa_family == AF_LINK) {
86                         struct sockaddr_dl *foo =
87                             (struct sockaddr_dl *)ifa->ifa_addr;
88
89                         ifi->index = foo->sdl_index;
90                         ifi->hw_address.hlen = foo->sdl_alen;
91                         ifi->hw_address.htype = HTYPE_ETHER; /* XXX */
92                         memcpy(ifi->hw_address.haddr,
93                             LLADDR(foo), foo->sdl_alen);
94                 }
95                 if (!ifi->ifp) {
96                         if ((tif = malloc(len)) == NULL)
97                                 error("no space to remember ifp");
98                         strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ);
99                         ifi->ifp = tif;
100                 }
101         }
102
103         if (!ifi->ifp)
104                 error("%s: not found", ifi->name);
105
106         /* Register the interface... */
107         if_register_receive();
108         if_register_send();
109         freeifaddrs(ifap);
110 }
111
112 void
113 reinitialize_interface(void)
114 {
115         interfaces_invalidated = 1;
116 }
117
118 /*
119  * Wait for packets to come in using poll().  When a packet comes in, call
120  * receive_packet to receive the packet and possibly strip hardware addressing
121  * information from it, and then call do_packet to try to do something with it.
122  */
123 void
124 dispatch(void)
125 {
126         int count, to_msec;
127         struct pollfd fds[2];
128         time_t howlong;
129
130         do {
131                 /*
132                  * Call any expired timeouts, and then if there's still
133                  * a timeout registered, time out the select call then.
134                  */
135 another:
136                 if (!ifi->linkstat)
137                         interfaces_invalidated = 0;
138
139                 if (timeouts) {
140                         struct timeout *t;
141
142                         if (timeouts->when <= cur_time) {
143                                 t = timeouts;
144                                 timeouts = timeouts->next;
145                                 (*(t->func))();
146                                 t->next = free_timeouts;
147                                 free_timeouts = t;
148                                 goto another;
149                         }
150
151                         /*
152                          * Figure timeout in milliseconds, and check for
153                          * potential overflow, so we can cram into an
154                          * int for poll, while not polling with a
155                          * negative timeout and blocking indefinitely.
156                          */
157                         howlong = timeouts->when - cur_time;
158                         if (howlong > INT_MAX / 1000)
159                                 howlong = INT_MAX / 1000;
160                         to_msec = howlong * 1000;
161                 } else
162                         to_msec = -1;
163
164                 /* Set up the descriptors to be polled. */
165                 if (!ifi || ifi->rfdesc == -1)
166                         error("No live interface to poll on");
167
168                 fds[0].fd = ifi->rfdesc;
169                 fds[1].fd = routefd; /* Could be -1, which will be ignored. */
170                 fds[0].events = fds[1].events = POLLIN;
171
172                 /* Wait for a packet or a timeout... XXX */
173                 count = poll(fds, 2, to_msec);
174
175                 /* Get the current time... */
176                 time(&cur_time);
177
178                 /* Not likely to be transitory... */
179                 if (count == -1) {
180                         if (errno == EAGAIN || errno == EINTR) {
181                                 continue;
182                         } else
183                                 error("poll: %m");
184                 }
185
186                 if ((fds[0].revents & (POLLIN | POLLHUP))) {
187                         if (ifi->linkstat &&
188                             ifi && ifi->rfdesc != -1)
189                                 got_one();
190                 }
191                 if ((fds[1].revents & (POLLIN | POLLHUP))) {
192                         if (ifi && !interfaces_invalidated)
193                                 routehandler();
194                 }
195
196                 interfaces_invalidated = 0;
197         } while (1);
198 }
199
200 void
201 got_one(void)
202 {
203         struct sockaddr_in from;
204         struct hardware hfrom;
205         struct iaddr ifrom;
206         ssize_t result;
207
208         if ((result = receive_packet(&from, &hfrom)) == -1) {
209                 warning("receive_packet failed on %s: %s", ifi->name,
210                     strerror(errno));
211                 ifi->errors++;
212                 if ((!interface_status(ifi->name)) ||
213                     (ifi->noifmedia && ifi->errors > 20)) {
214                         /* our interface has gone away. */
215                         warning("Interface %s no longer appears valid.",
216                             ifi->name);
217                         interfaces_invalidated = 1;
218                         close(ifi->rfdesc);
219                         ifi->rfdesc = -1;
220                 }
221                 return;
222         }
223         if (result == 0)
224                 return;
225
226         ifrom.len = 4;
227         memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
228
229         do_packet(result, from.sin_port, ifrom, &hfrom);
230 }
231
232 int
233 interface_link_forceup(char *ifname)
234 {
235         struct ifreq ifr;
236         int sock;
237
238         if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
239                 error("Can't create socket");
240
241         memset(&ifr, 0, sizeof(ifr));
242         strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
243         if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) {
244                 close(sock);
245                 return (-1);
246         }
247
248         if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
249                 ifr.ifr_flags |= IFF_UP;
250                 if (ioctl(sock, SIOCSIFFLAGS, (caddr_t)&ifr) == -1) {
251                         close(sock);
252                         return (-1);
253                 }
254                 close(sock);
255                 return (0);
256         }
257         close(sock);
258         return (1);
259 }
260
261 void
262 interface_link_forcedown(char *ifname)
263 {
264         struct ifreq ifr;
265         int sock;
266
267         if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
268                 error("Can't create socket");
269
270         memset(&ifr, 0, sizeof(ifr));
271         strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
272         if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) {
273                 close(sock);
274                 return;
275         }
276
277         if ((ifr.ifr_flags & IFF_UP) == IFF_UP) {
278                 ifr.ifr_flags &= ~IFF_UP;
279                 if (ioctl(sock, SIOCSIFFLAGS, (caddr_t)&ifr) == -1) {
280                         close(sock);
281                         return;
282                 }
283         }
284
285         close(sock);
286 }
287
288 int
289 interface_status(char *ifname)
290 {
291         struct ifreq ifr;
292         struct ifmediareq ifmr;
293         int sock;
294
295         if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
296                 error("Can't create socket");
297
298         /* get interface flags */
299         memset(&ifr, 0, sizeof(ifr));
300         strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
301         if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
302                 warning("ioctl(SIOCGIFFLAGS) on %s: %m", ifname);
303                 goto inactive;
304         }
305
306         /*
307          * if one of UP and RUNNING flags is dropped,
308          * the interface is not active.
309          */
310         if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
311                 goto inactive;
312
313         /* Next, check carrier on the interface, if possible */
314         if (ifi->noifmedia)
315                 goto active;
316         memset(&ifmr, 0, sizeof(ifmr));
317         strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
318         if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
319                 /*
320                  * EINVAL or ENOTTY simply means that the interface
321                  * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
322                  */
323                 if (errno != EINVAL && errno != ENOTTY)
324                         debug("ioctl(SIOCGIFMEDIA) on %s: %m", ifname);
325
326                 ifi->noifmedia = 1;
327                 goto active;
328         }
329         if (ifmr.ifm_status & IFM_AVALID) {
330                 if (ifmr.ifm_status & IFM_ACTIVE)
331                         goto active;
332                 else
333                         goto inactive;
334         }
335 inactive:
336         close(sock);
337         return (0);
338 active:
339         close(sock);
340         return (1);
341 }
342
343 void
344 add_timeout(time_t when, void (*where)(void))
345 {
346         struct timeout *t, *q;
347
348         /* See if this timeout supersedes an existing timeout. */
349         t = NULL;
350         for (q = timeouts; q; q = q->next) {
351                 if (q->func == where) {
352                         if (t)
353                                 t->next = q->next;
354                         else
355                                 timeouts = q->next;
356                         break;
357                 }
358                 t = q;
359         }
360
361         /* If we didn't supersede a timeout, allocate a timeout
362            structure now. */
363         if (!q) {
364                 if (free_timeouts) {
365                         q = free_timeouts;
366                         free_timeouts = q->next;
367                         q->func = where;
368                 } else {
369                         q = malloc(sizeof(struct timeout));
370                         if (!q)
371                                 error("Can't allocate timeout structure!");
372                         q->func = where;
373                 }
374         }
375
376         q->when = when;
377
378         /* Now sort this timeout into the timeout list. */
379
380         /* Beginning of list? */
381         if (!timeouts || timeouts->when > q->when) {
382                 q->next = timeouts;
383                 timeouts = q;
384                 return;
385         }
386
387         /* Middle of list? */
388         for (t = timeouts; t->next; t = t->next) {
389                 if (t->next->when > q->when) {
390                         q->next = t->next;
391                         t->next = q;
392                         return;
393                 }
394         }
395
396         /* End of list. */
397         t->next = q;
398         q->next = NULL;
399 }
400
401 void
402 cancel_timeout(void (*where)(void))
403 {
404         struct timeout *t, *q;
405
406         /* Look for this timeout on the list, and unlink it if we find it. */
407         t = NULL;
408         for (q = timeouts; q; q = q->next) {
409                 if (q->func == where) {
410                         if (t)
411                                 t->next = q->next;
412                         else
413                                 timeouts = q->next;
414                         break;
415                 }
416                 t = q;
417         }
418
419         /* If we found the timeout, put it on the free list. */
420         if (q) {
421                 q->next = free_timeouts;
422                 free_timeouts = q;
423         }
424 }
425
426 int
427 interface_link_status(char *ifname)
428 {
429         struct ifmediareq ifmr;
430         int sock;
431
432         if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
433                 error("Can't create socket");
434
435         memset(&ifmr, 0, sizeof(ifmr));
436         strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
437         if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
438                 /* EINVAL/ENOTTY -> link state unknown. treat as active */
439                 if (errno != EINVAL && errno != ENOTTY)
440                         debug("ioctl(SIOCGIFMEDIA) on %s: %m", ifname);
441                 close(sock);
442                 return (1);
443         }
444         close(sock);
445
446         if (ifmr.ifm_status & IFM_AVALID) {
447                 if (ifmr.ifm_status & IFM_ACTIVE)
448                         return (1);
449                 else
450                         return (0);
451         }
452         return (1);
453 }