1 /* $OpenBSD: dispatch.c,v 1.7 2007/09/02 15:19:38 deraadt 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 "dhcpd.h" 43 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; 51 struct timeout *timeouts; 52 static struct timeout *free_timeouts; 53 static int interfaces_invalidated; 54 55 void (*bootp_packet_handler)(struct interface_info *, 56 struct dhcp_packet *, int, unsigned int, 57 struct iaddr, struct hardware *); 58 59 static int interface_status(struct interface_info *ifinfo); 60 61 /* 62 * Use getifaddrs() to get a list of all the attached interfaces. For 63 * each interface that's of type INET and not the loopback interface, 64 * register that interface with the network I/O software, figure out 65 * what subnet it's on, and add it to the list of interfaces. 66 */ 67 void 68 discover_interfaces(struct interface_info *iface) 69 { 70 struct sockaddr_in foo; 71 struct ifaddrs *ifap, *ifa; 72 struct ifreq *tif; 73 74 if (getifaddrs(&ifap) != 0) 75 error("getifaddrs failed"); 76 77 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { 78 if ((ifa->ifa_flags & IFF_LOOPBACK) || 79 (ifa->ifa_flags & IFF_POINTOPOINT) || 80 (!(ifa->ifa_flags & IFF_UP))) 81 continue; 82 83 if (strcmp(iface->name, ifa->ifa_name)) 84 continue; 85 86 /* 87 * If we have the capability, extract link information 88 * and record it in a linked list. 89 */ 90 if (ifa->ifa_addr->sa_family == AF_LINK) { 91 struct sockaddr_dl *foo = 92 (struct sockaddr_dl *)ifa->ifa_addr; 93 94 iface->index = foo->sdl_index; 95 iface->hw_address.hlen = foo->sdl_alen; 96 iface->hw_address.htype = HTYPE_ETHER; /* XXX */ 97 memcpy(iface->hw_address.haddr, 98 LLADDR(foo), foo->sdl_alen); 99 } else if (ifa->ifa_addr->sa_family == AF_INET) { 100 struct iaddr addr; 101 102 memcpy(&foo, ifa->ifa_addr, sizeof(foo)); 103 if (foo.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) 104 continue; 105 if (!iface->ifp) { 106 int len = IFNAMSIZ + ifa->ifa_addr->sa_len; 107 108 if ((tif = malloc(len)) == NULL) 109 error("no space to remember ifp"); 110 strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ); 111 memcpy(&tif->ifr_addr, ifa->ifa_addr, 112 ifa->ifa_addr->sa_len); 113 iface->ifp = tif; 114 iface->primary_address = foo.sin_addr; 115 } 116 addr.len = 4; 117 memcpy(addr.iabuf, &foo.sin_addr.s_addr, addr.len); 118 } 119 } 120 121 if (!iface->ifp) 122 error("%s: not found", iface->name); 123 124 /* Register the interface... */ 125 if_register_receive(iface); 126 if_register_send(iface); 127 add_protocol(iface->name, iface->rfdesc, got_one, iface); 128 freeifaddrs(ifap); 129 } 130 131 /* 132 * Wait for packets to come in using poll(). When a packet comes in, 133 * call receive_packet to receive the packet and possibly strip hardware 134 * addressing information from it, and then call through the 135 * bootp_packet_handler hook to try to do something with it. 136 */ 137 void 138 dispatch(void) 139 { 140 int count, i, to_msec, nfds = 0; 141 struct protocol *l; 142 struct pollfd *fds; 143 time_t howlong; 144 145 nfds = 0; 146 for (l = protocols; l; l = l->next) 147 nfds++; 148 149 fds = calloc(nfds, sizeof(struct pollfd)); 150 if (fds == NULL) 151 error("Can't allocate poll structures."); 152 153 do { 154 /* 155 * Call any expired timeouts, and then if there's still 156 * a timeout registered, time out the select call then. 157 */ 158 another: 159 if (timeouts) { 160 if (timeouts->when <= cur_time) { 161 struct timeout *t = timeouts; 162 163 timeouts = timeouts->next; 164 (*(t->func))(t->what); 165 t->next = free_timeouts; 166 free_timeouts = t; 167 goto another; 168 } 169 170 /* 171 * Figure timeout in milliseconds, and check for 172 * potential overflow, so we can cram into an 173 * int for poll, while not polling with a 174 * negative timeout and blocking indefinitely. 175 */ 176 howlong = timeouts->when - cur_time; 177 if (howlong > INT_MAX / 1000) 178 howlong = INT_MAX / 1000; 179 to_msec = howlong * 1000; 180 } else 181 to_msec = -1; 182 183 /* Set up the descriptors to be polled. */ 184 i = 0; 185 186 for (l = protocols; l; l = l->next) { 187 struct interface_info *ip = l->local; 188 189 if (ip && (l->handler != got_one || !ip->dead)) { 190 fds[i].fd = l->fd; 191 fds[i].events = POLLIN; 192 fds[i].revents = 0; 193 i++; 194 } 195 } 196 197 if (i == 0) 198 error("No live interfaces to poll on - exiting."); 199 200 /* Wait for a packet or a timeout... XXX */ 201 count = poll(fds, nfds, to_msec); 202 203 /* Not likely to be transitory... */ 204 if (count == -1) { 205 if (errno == EAGAIN || errno == EINTR) { 206 time(&cur_time); 207 continue; 208 } 209 else 210 error("poll: %m"); 211 } 212 213 /* Get the current time... */ 214 time(&cur_time); 215 216 i = 0; 217 for (l = protocols; l; l = l->next) { 218 struct interface_info *ip = l->local; 219 220 if ((fds[i].revents & (POLLIN | POLLHUP))) { 221 fds[i].revents = 0; 222 if (ip && (l->handler != got_one || 223 !ip->dead)) 224 (*(l->handler))(l); 225 if (interfaces_invalidated) 226 break; 227 } 228 i++; 229 } 230 interfaces_invalidated = 0; 231 } while (1); 232 } 233 234 235 void 236 got_one(struct protocol *l) 237 { 238 struct sockaddr_in from; 239 struct hardware hfrom; 240 struct iaddr ifrom; 241 size_t result; 242 union { 243 /* 244 * Packet input buffer. Must be as large as largest 245 * possible MTU. 246 */ 247 unsigned char packbuf[4095]; 248 struct dhcp_packet packet; 249 } u; 250 struct interface_info *ip = l->local; 251 252 if ((result = receive_packet(ip, u.packbuf, sizeof(u), &from, 253 &hfrom)) == -1) { 254 warning("receive_packet failed on %s: %s", ip->name, 255 strerror(errno)); 256 ip->errors++; 257 if ((!interface_status(ip)) || 258 (ip->noifmedia && ip->errors > 20)) { 259 /* our interface has gone away. */ 260 warning("Interface %s no longer appears valid.", 261 ip->name); 262 ip->dead = 1; 263 interfaces_invalidated = 1; 264 close(l->fd); 265 remove_protocol(l); 266 free(ip); 267 } 268 return; 269 } 270 if (result == 0) 271 return; 272 273 if (bootp_packet_handler) { 274 ifrom.len = 4; 275 memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len); 276 277 (*bootp_packet_handler)(ip, &u.packet, result, 278 from.sin_port, ifrom, &hfrom); 279 } 280 } 281 282 int 283 interface_status(struct interface_info *ifinfo) 284 { 285 char *ifname = ifinfo->name; 286 int ifsock = ifinfo->rfdesc; 287 struct ifreq ifr; 288 struct ifmediareq ifmr; 289 290 /* get interface flags */ 291 memset(&ifr, 0, sizeof(ifr)); 292 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 293 if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) { 294 syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) on %s: %m", ifname); 295 goto inactive; 296 } 297 /* 298 * if one of UP and RUNNING flags is dropped, 299 * the interface is not active. 300 */ 301 if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 302 goto inactive; 303 } 304 /* Next, check carrier on the interface, if possible */ 305 if (ifinfo->noifmedia) 306 goto active; 307 memset(&ifmr, 0, sizeof(ifmr)); 308 strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); 309 if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { 310 if (errno != EINVAL) { 311 syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m", 312 ifname); 313 314 ifinfo->noifmedia = 1; 315 goto active; 316 } 317 /* 318 * EINVAL (or ENOTTY) simply means that the interface 319 * does not support the SIOCGIFMEDIA ioctl. We regard it alive. 320 */ 321 ifinfo->noifmedia = 1; 322 goto active; 323 } 324 if (ifmr.ifm_status & IFM_AVALID) { 325 switch (ifmr.ifm_active & IFM_NMASK) { 326 case IFM_ETHER: 327 if (ifmr.ifm_status & IFM_ACTIVE) 328 goto active; 329 else 330 goto inactive; 331 break; 332 default: 333 goto inactive; 334 } 335 } 336 inactive: 337 return (0); 338 active: 339 return (1); 340 } 341 342 /* Add a protocol to the list of protocols... */ 343 void 344 add_protocol(char *name, int fd, void (*handler)(struct protocol *), 345 void *local) 346 { 347 struct protocol *p; 348 349 p = malloc(sizeof(*p)); 350 if (!p) 351 error("can't allocate protocol struct for %s", name); 352 353 p->fd = fd; 354 p->handler = handler; 355 p->local = local; 356 p->next = protocols; 357 protocols = p; 358 } 359 360 void 361 remove_protocol(struct protocol *proto) 362 { 363 struct protocol *p, *next, *prev; 364 365 prev = NULL; 366 for (p = protocols; p; p = next) { 367 next = p->next; 368 if (p == proto) { 369 if (prev) 370 prev->next = p->next; 371 else 372 protocols = p->next; 373 free(p); 374 } 375 } 376 } 377