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 } 454