1 /* $OpenBSD: src/sbin/dhclient/dispatch.c,v 1.59 2012/10/11 08:05:05 sthen 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 <sys/ioctl.h> 43 44 #include <net/if_media.h> 45 46 #include <ifaddrs.h> 47 #include <poll.h> 48 49 #include "dhcpd.h" 50 51 struct timeout timeout; 52 53 /* 54 * Use getifaddrs() to get a list of all the attached interfaces. Find 55 * our interface on the list and store the interesting information about it. 56 */ 57 void 58 discover_interface(void) 59 { 60 struct ifaddrs *ifap, *ifa; 61 struct ifreq *tif; 62 int len = IFNAMSIZ + sizeof(struct sockaddr_storage); 63 64 if (getifaddrs(&ifap) != 0) 65 error("getifaddrs failed"); 66 67 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { 68 if ((ifa->ifa_flags & IFF_LOOPBACK) || 69 (ifa->ifa_flags & IFF_POINTOPOINT) || 70 (!(ifa->ifa_flags & IFF_UP))) 71 continue; 72 73 if (strcmp(ifi->name, ifa->ifa_name)) 74 continue; 75 76 /* 77 * If we have the capability, extract & save link information. 78 */ 79 if (ifa->ifa_addr->sa_family == AF_LINK) { 80 struct sockaddr_dl *foo = 81 (struct sockaddr_dl *)ifa->ifa_addr; 82 83 ifi->index = foo->sdl_index; 84 ifi->hw_address.hlen = foo->sdl_alen; 85 ifi->hw_address.htype = HTYPE_ETHER; /* XXX */ 86 memcpy(ifi->hw_address.haddr, 87 LLADDR(foo), foo->sdl_alen); 88 } 89 if (!ifi->ifp) { 90 if ((tif = malloc(len)) == NULL) 91 error("no space to remember ifp"); 92 strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ); 93 ifi->ifp = tif; 94 } 95 } 96 97 if (!ifi->ifp) 98 error("%s: not found", ifi->name); 99 100 /* Register the interface... */ 101 if_register_receive(); 102 if_register_send(); 103 freeifaddrs(ifap); 104 } 105 106 /* 107 * Loop waiting for packets, timeouts or routing messages. 108 */ 109 void 110 dispatch(void) 111 { 112 int count, to_msec; 113 struct pollfd fds[2]; 114 time_t cur_time, howlong; 115 void (*func)(void); 116 117 do { 118 /* 119 * Call expired timeout, and then if there's still 120 * a timeout registered, time out the select call then. 121 */ 122 another: 123 if (!ifi) 124 error("No interfaces available"); 125 126 if (timeout.func) { 127 time(&cur_time); 128 if (timeout.when <= cur_time) { 129 func = timeout.func; 130 cancel_timeout(); 131 (*(func))(); 132 goto another; 133 } 134 /* 135 * Figure timeout in milliseconds, and check for 136 * potential overflow, so we can cram into an 137 * int for poll, while not polling with a 138 * negative timeout and blocking indefinitely. 139 */ 140 howlong = timeout.when - cur_time; 141 if (howlong > INT_MAX / 1000) 142 howlong = INT_MAX / 1000; 143 to_msec = howlong * 1000; 144 } else 145 to_msec = -1; 146 147 /* Set up the descriptors to be polled. */ 148 if (!ifi || ifi->rfdesc == -1) 149 error("No live interface to poll on"); 150 151 fds[0].fd = ifi->rfdesc; 152 fds[1].fd = routefd; /* Could be -1, which will be ignored. */ 153 fds[0].events = fds[1].events = POLLIN; 154 155 /* Wait for a packet or a timeout... XXX */ 156 count = poll(fds, 2, to_msec); 157 158 /* Not likely to be transitory... */ 159 if (count == -1) { 160 if (errno == EAGAIN || errno == EINTR) { 161 continue; 162 } else 163 error("poll: %m"); 164 } 165 166 if ((fds[0].revents & (POLLIN | POLLHUP))) { 167 /* XXX profmakx: I am not sure whether updating the linkstate 168 here is the best idea, but it being not up to date leads 169 to a busy loop. 170 Alternatively we can just remove the link state check since 171 the link state is checked in got_one() later on 172 */ 173 ifi->linkstat = interface_status(ifi->name); 174 175 if (ifi && ifi->linkstat && ifi->rfdesc != -1) 176 got_one(); 177 } 178 if ((fds[1].revents & (POLLIN | POLLHUP))) { 179 if (ifi) 180 routehandler(); 181 } 182 } while (1); 183 } 184 185 void 186 got_one(void) 187 { 188 struct sockaddr_in from; 189 struct hardware hfrom; 190 struct iaddr ifrom; 191 ssize_t result; 192 193 if ((result = receive_packet(&from, &hfrom)) == -1) { 194 warning("receive_packet failed on %s: %s", ifi->name, 195 strerror(errno)); 196 ifi->errors++; 197 if ((!interface_status(ifi->name)) || 198 (ifi->noifmedia && ifi->errors > 20)) { 199 /* our interface has gone away. */ 200 error("Interface %s no longer appears valid.", 201 ifi->name); 202 } 203 return; 204 } 205 if (result == 0) 206 return; 207 208 ifrom.len = 4; 209 memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len); 210 211 do_packet(result, from.sin_port, ifrom, &hfrom); 212 } 213 214 /* 215 * Normally its ok to force the interface link up, but don't do it 216 * if it is an 80211 interface. 217 */ 218 int 219 interface_link_forceup(char *ifname) 220 { 221 struct ifreq ifr; 222 struct ifmediareq ifmr; 223 int sock; 224 225 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 226 error("Can't create socket"); 227 228 memset(&ifr, 0, sizeof(ifr)); 229 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 230 if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) { 231 close(sock); 232 return (-1); 233 } 234 memset(&ifmr, 0, sizeof(ifmr)); 235 strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); 236 if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) != -1) { 237 if (IFM_TYPE(ifmr.ifm_active) == IFM_IEEE80211) { 238 return 0; 239 } 240 } 241 242 if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 243 ifr.ifr_flags |= IFF_UP; 244 if (ioctl(sock, SIOCSIFFLAGS, (caddr_t)&ifr) == -1) { 245 close(sock); 246 return (-1); 247 } 248 close(sock); 249 return (0); 250 } 251 close(sock); 252 return (1); 253 } 254 255 int 256 interface_status(char *ifname) 257 { 258 struct ifreq ifr; 259 struct ifmediareq ifmr; 260 int sock; 261 262 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 263 error("Can't create socket"); 264 265 /* get interface flags */ 266 memset(&ifr, 0, sizeof(ifr)); 267 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 268 if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) { 269 error("ioctl(SIOCGIFFLAGS) on %s: %m", ifname); 270 } 271 272 /* 273 * if one of UP and RUNNING flags is dropped, 274 * the interface is not active. 275 */ 276 if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) 277 goto inactive; 278 279 /* Next, check carrier on the interface, if possible */ 280 if (ifi->noifmedia) 281 goto active; 282 memset(&ifmr, 0, sizeof(ifmr)); 283 strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); 284 if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) { 285 /* 286 * EINVAL or ENOTTY simply means that the interface 287 * does not support the SIOCGIFMEDIA ioctl. We regard it alive. 288 */ 289 #ifdef DEBUG 290 if (errno != EINVAL && errno != ENOTTY) 291 debug("ioctl(SIOCGIFMEDIA) on %s: %m", ifname); 292 #endif 293 ifi->noifmedia = 1; 294 goto active; 295 } 296 if (ifmr.ifm_status & IFM_AVALID) { 297 if (ifmr.ifm_status & IFM_ACTIVE) 298 goto active; 299 else 300 goto inactive; 301 } 302 303 /* Assume 'active' if IFM_AVALID is not set. */ 304 305 active: 306 close(sock); 307 return (1); 308 inactive: 309 close(sock); 310 return (0); 311 } 312 313 void 314 set_timeout(time_t when, void (*where)(void)) 315 { 316 timeout.when = when; 317 timeout.func = where; 318 } 319 320 void 321 set_timeout_interval(time_t secs, void (*where)(void)) 322 { 323 timeout.when = time(NULL) + secs; 324 timeout.func = where; 325 } 326 327 void 328 cancel_timeout(void) 329 { 330 timeout.when = 0; 331 timeout.func = NULL; 332 } 333 334 int 335 subnet_exists(struct client_lease *l) 336 { 337 struct ifaddrs *ifap, *ifa; 338 in_addr_t mymask, myaddr, mynet, hismask, hisaddr, hisnet; 339 340 bcopy(l->options[DHO_SUBNET_MASK].data, &mymask, 4); 341 bcopy(l->address.iabuf, &myaddr, 4); 342 mynet = mymask & myaddr; 343 344 if (getifaddrs(&ifap) != 0) 345 error("getifaddrs failed"); 346 347 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { 348 if (strcmp(ifi->name, ifa->ifa_name) == 0) 349 continue; 350 351 if (ifa->ifa_addr->sa_family != AF_INET) 352 continue; 353 354 hismask = ((struct sockaddr_in *)ifa->ifa_netmask)-> 355 sin_addr.s_addr; 356 hisaddr = ((struct sockaddr_in *)ifa->ifa_addr)-> 357 sin_addr.s_addr; 358 hisnet = hisaddr & hismask; 359 360 if (hisnet == 0) 361 continue; 362 363 /* Would his packets go out *my* interface? */ 364 if (mynet == (hisaddr & mymask)) { 365 note("interface %s already has the offered subnet!", 366 ifa->ifa_name); 367 return (1); 368 } 369 370 /* Would my packets go out *his* interface? */ 371 if (hisnet == (myaddr & hismask)) { 372 note("interface %s already has the offered subnet!", 373 ifa->ifa_name); 374 return (1); 375 } 376 } 377 378 freeifaddrs(ifap); 379 380 return (0); 381 } 382