1 /* $OpenBSD: dispatch.c,v 1.3 2023/07/14 07:09:00 gerhard 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/types.h> 43 #include <sys/ioctl.h> 44 #include <sys/socket.h> 45 46 #include <net/if.h> 47 #include <net/if_dl.h> 48 #include <net/if_media.h> 49 #include <net/if_types.h> 50 51 #include <netinet/in.h> 52 #include <netinet/if_ether.h> 53 54 #include <errno.h> 55 #include <ifaddrs.h> 56 #include <limits.h> 57 #include <poll.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <syslog.h> 61 #include <time.h> 62 #include <unistd.h> 63 64 #include "dhcp.h" 65 #include "dhcpd.h" 66 #include "log.h" 67 68 /* 69 * Macros implementation used to generate link-local addresses. This 70 * code was copied from: sys/netinet6/in6_ifattach.c. 71 */ 72 #define EUI64_UBIT 0x02 73 #define EUI64_TO_IFID(in6) \ 74 do { (in6)->s6_addr[8] ^= EUI64_UBIT; } while (0) 75 76 struct protocol *protocols; 77 struct timeout *timeouts; 78 static struct timeout *free_timeouts; 79 static int interfaces_invalidated; 80 81 void (*bootp_packet_handler)(struct interface_info *, 82 void *, size_t, struct packet_ctx *); 83 84 static int interface_status(struct interface_info *ifinfo); 85 86 struct interface_info * 87 iflist_getbyindex(unsigned int index) 88 { 89 struct interface_info *intf; 90 91 TAILQ_FOREACH(intf, &intflist, entry) { 92 if (intf->index != index) 93 continue; 94 95 return intf; 96 } 97 98 return NULL; 99 } 100 101 struct interface_info * 102 iflist_getbyname(const char *name) 103 { 104 struct interface_info *intf; 105 106 TAILQ_FOREACH(intf, &intflist, entry) { 107 if (strcmp(intf->name, name) != 0) 108 continue; 109 110 return intf; 111 } 112 113 return NULL; 114 } 115 116 struct interface_info * 117 iflist_getbyaddr6(struct in6_addr *addr) 118 { 119 struct interface_info *intf; 120 121 TAILQ_FOREACH(intf, &intflist, entry) { 122 /* Look for link-layer address. */ 123 if (memcmp(&intf->linklocal, addr, sizeof(*addr)) == 0) 124 return intf; 125 } 126 127 return NULL; 128 } 129 130 void 131 setup_iflist(void) 132 { 133 struct interface_info *intf; 134 struct sockaddr_dl *sdl; 135 struct ifaddrs *ifap, *ifa; 136 struct if_data *ifi; 137 struct sockaddr_in *sin; 138 struct sockaddr_in6 *sin6; 139 140 TAILQ_INIT(&intflist); 141 if (getifaddrs(&ifap)) 142 fatalx("getifaddrs failed"); 143 144 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { 145 if ((ifa->ifa_flags & IFF_LOOPBACK) || 146 (ifa->ifa_flags & IFF_POINTOPOINT)) 147 continue; 148 149 /* Find interface or create it. */ 150 intf = iflist_getbyname(ifa->ifa_name); 151 if (intf == NULL) { 152 intf = calloc(1, sizeof(*intf)); 153 if (intf == NULL) 154 fatal("calloc"); 155 156 strlcpy(intf->name, ifa->ifa_name, 157 sizeof(intf->name)); 158 TAILQ_INSERT_HEAD(&intflist, intf, entry); 159 } 160 161 /* Signal disabled interface. */ 162 if ((ifa->ifa_flags & IFF_UP) == 0) 163 intf->dead = 1; 164 165 if (ifa->ifa_addr->sa_family == AF_LINK) { 166 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 167 ifi = (struct if_data *)ifa->ifa_data; 168 169 /* Skip non ethernet interfaces. */ 170 if (ifi->ifi_type != IFT_ETHER && 171 ifi->ifi_type != IFT_ENC && 172 ifi->ifi_type != IFT_CARP) { 173 TAILQ_REMOVE(&intflist, intf, entry); 174 free(intf); 175 continue; 176 } 177 178 intf->index = sdl->sdl_index; 179 intf->hw_address.hlen = sdl->sdl_alen; 180 memcpy(intf->hw_address.haddr, 181 LLADDR(sdl), sdl->sdl_alen); 182 } else if (ifa->ifa_addr->sa_family == AF_INET) { 183 sin = (struct sockaddr_in *)ifa->ifa_addr; 184 if (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK) || 185 intf->primary_address.s_addr != INADDR_ANY) 186 continue; 187 188 intf->primary_address = sin->sin_addr; 189 } else if (ifa->ifa_addr->sa_family == AF_INET6) { 190 sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; 191 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { 192 intf->linklocal = sin6->sin6_addr; 193 #ifdef __KAME__ 194 /* 195 * Remove possible scope from address if 196 * link-local. 197 */ 198 intf->linklocal.s6_addr[2] = 0; 199 intf->linklocal.s6_addr[3] = 0; 200 #endif 201 } else 202 intf->gipv6 = 1; 203 204 /* At least one IPv6 address was found. */ 205 intf->ipv6 = 1; 206 } 207 } 208 209 freeifaddrs(ifap); 210 211 /* 212 * Generate link-local IPv6 address for interfaces without it. 213 * 214 * For IPv6 DHCP Relay it doesn't matter what is used for 215 * link-addr field, so let's generate an address that won't 216 * change during execution so we can always find the interface 217 * to relay packets back. This is only used for layer 2 relaying 218 * when the interface might not have an address. 219 */ 220 TAILQ_FOREACH(intf, &intflist, entry) { 221 if (memcmp(&intf->linklocal, &in6addr_any, 222 sizeof(in6addr_any)) != 0) 223 continue; 224 225 intf->linklocal.s6_addr[0] = 0xfe; 226 intf->linklocal.s6_addr[1] = 0x80; 227 intf->linklocal.s6_addr[8] = intf->hw_address.haddr[0]; 228 intf->linklocal.s6_addr[9] = intf->hw_address.haddr[1]; 229 intf->linklocal.s6_addr[10] = intf->hw_address.haddr[2]; 230 intf->linklocal.s6_addr[11] = 0xff; 231 intf->linklocal.s6_addr[12] = 0xfe; 232 intf->linklocal.s6_addr[13] = intf->hw_address.haddr[3]; 233 intf->linklocal.s6_addr[14] = intf->hw_address.haddr[4]; 234 intf->linklocal.s6_addr[15] = intf->hw_address.haddr[5]; 235 EUI64_TO_IFID(&intf->linklocal); 236 } 237 } 238 239 struct interface_info * 240 register_interface(const char *ifname, void (*handler)(struct protocol *)) 241 { 242 struct interface_info *intf; 243 244 if ((intf = iflist_getbyname(ifname)) == NULL) 245 return NULL; 246 247 /* Don't register disabled interfaces. */ 248 if (intf->dead) 249 return NULL; 250 251 /* Check if we already registered the interface. */ 252 if (intf->ifr.ifr_name[0] != 0) 253 return intf; 254 255 if (strlcpy(intf->ifr.ifr_name, ifname, 256 sizeof(intf->ifr.ifr_name)) >= sizeof(intf->ifr.ifr_name)) 257 fatalx("interface name '%s' too long", ifname); 258 259 if_register_receive(intf); 260 if_register_send(intf); 261 add_protocol(intf->name, intf->rfdesc, handler, intf); 262 263 return intf; 264 } 265 266 /* 267 * Wait for packets to come in using poll(). When a packet comes in, 268 * call receive_packet to receive the packet and possibly strip hardware 269 * addressing information from it, and then call through the 270 * bootp_packet_handler hook to try to do something with it. 271 */ 272 void 273 dispatch(void) 274 { 275 int count, i, to_msec, nfds = 0; 276 struct protocol *l; 277 struct pollfd *fds; 278 time_t howlong; 279 280 nfds = 0; 281 for (l = protocols; l; l = l->next) 282 nfds++; 283 284 fds = calloc(nfds, sizeof(struct pollfd)); 285 if (fds == NULL) 286 fatalx("Can't allocate poll structures."); 287 288 do { 289 /* 290 * Call any expired timeouts, and then if there's still 291 * a timeout registered, time out the select call then. 292 */ 293 another: 294 if (timeouts) { 295 if (timeouts->when <= cur_time) { 296 struct timeout *t = timeouts; 297 298 timeouts = timeouts->next; 299 (*(t->func))(t->what); 300 t->next = free_timeouts; 301 free_timeouts = t; 302 goto another; 303 } 304 305 /* 306 * Figure timeout in milliseconds, and check for 307 * potential overflow, so we can cram into an 308 * int for poll, while not polling with a 309 * negative timeout and blocking indefinitely. 310 */ 311 howlong = timeouts->when - cur_time; 312 if (howlong > INT_MAX / 1000) 313 howlong = INT_MAX / 1000; 314 to_msec = howlong * 1000; 315 } else 316 to_msec = -1; 317 318 /* Set up the descriptors to be polled. */ 319 i = 0; 320 321 for (l = protocols; l; l = l->next) { 322 struct interface_info *ip = l->local; 323 324 if (ip && (l->handler != got_one || !ip->dead)) { 325 fds[i].fd = l->fd; 326 fds[i].events = POLLIN; 327 fds[i].revents = 0; 328 i++; 329 } 330 } 331 332 if (i == 0) 333 fatalx("No live interfaces to poll on - exiting."); 334 335 /* Wait for a packet or a timeout... XXX */ 336 count = poll(fds, nfds, to_msec); 337 338 /* Not likely to be transitory... */ 339 if (count == -1) { 340 if (errno == EAGAIN || errno == EINTR) { 341 time(&cur_time); 342 continue; 343 } 344 else 345 fatal("poll"); 346 } 347 348 /* Get the current time... */ 349 time(&cur_time); 350 351 i = 0; 352 for (l = protocols; l; l = l->next) { 353 struct interface_info *ip = l->local; 354 355 if ((fds[i].revents & (POLLIN | POLLHUP))) { 356 fds[i].revents = 0; 357 if (ip && (l->handler != got_one || 358 !ip->dead)) 359 (*(l->handler))(l); 360 if (interfaces_invalidated) 361 break; 362 } 363 i++; 364 } 365 interfaces_invalidated = 0; 366 } while (1); 367 } 368 369 370 void 371 got_one(struct protocol *l) 372 { 373 struct packet_ctx pc; 374 ssize_t result; 375 uint8_t buf[4096]; 376 struct interface_info *ip = l->local; 377 378 memset(&pc, 0, sizeof(pc)); 379 380 if ((result = receive_packet(ip, buf, sizeof(buf), &pc)) == -1) { 381 log_warn("receive_packet failed on %s", ip->name); 382 ip->errors++; 383 if ((!interface_status(ip)) || 384 (ip->noifmedia && ip->errors > 20)) { 385 /* our interface has gone away. */ 386 log_warnx("Interface %s no longer appears valid.", 387 ip->name); 388 ip->dead = 1; 389 interfaces_invalidated = 1; 390 close(l->fd); 391 remove_protocol(l); 392 free(ip); 393 } 394 return; 395 } 396 if (result == 0) 397 return; 398 399 if (bootp_packet_handler) 400 (*bootp_packet_handler)(ip, buf, result, &pc); 401 } 402 403 int 404 interface_status(struct interface_info *ifinfo) 405 { 406 char *ifname = ifinfo->name; 407 int ifsock = ifinfo->rfdesc; 408 struct ifreq ifr; 409 struct ifmediareq ifmr; 410 411 /* get interface flags */ 412 memset(&ifr, 0, sizeof(ifr)); 413 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 414 if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) == -1) { 415 log_warn("ioctl(SIOCGIFFLAGS) on %s", ifname); 416 goto inactive; 417 } 418 /* 419 * if one of UP and RUNNING flags is dropped, 420 * the interface is not active. 421 */ 422 if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 423 goto inactive; 424 } 425 /* Next, check carrier on the interface, if possible */ 426 if (ifinfo->noifmedia) 427 goto active; 428 memset(&ifmr, 0, sizeof(ifmr)); 429 strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); 430 if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) { 431 if (errno != EINVAL) { 432 log_debug("ioctl(SIOCGIFMEDIA) on %s", ifname); 433 ifinfo->noifmedia = 1; 434 goto active; 435 } 436 /* 437 * EINVAL (or ENOTTY) simply means that the interface 438 * does not support the SIOCGIFMEDIA ioctl. We regard it alive. 439 */ 440 ifinfo->noifmedia = 1; 441 goto active; 442 } 443 if (ifmr.ifm_status & IFM_AVALID) { 444 switch (ifmr.ifm_active & IFM_NMASK) { 445 case IFM_ETHER: 446 if (ifmr.ifm_status & IFM_ACTIVE) 447 goto active; 448 else 449 goto inactive; 450 break; 451 default: 452 goto inactive; 453 } 454 } 455 inactive: 456 return (0); 457 active: 458 return (1); 459 } 460 461 /* Add a protocol to the list of protocols... */ 462 void 463 add_protocol(char *name, int fd, void (*handler)(struct protocol *), 464 void *local) 465 { 466 struct protocol *p; 467 468 p = malloc(sizeof(*p)); 469 if (!p) 470 fatalx("can't allocate protocol struct for %s", name); 471 472 p->fd = fd; 473 p->handler = handler; 474 p->local = local; 475 p->next = protocols; 476 protocols = p; 477 } 478 479 void 480 remove_protocol(struct protocol *proto) 481 { 482 struct protocol *p, *next, *prev; 483 484 prev = NULL; 485 for (p = protocols; p; p = next) { 486 next = p->next; 487 if (p == proto) { 488 if (prev) 489 prev->next = p->next; 490 else 491 protocols = p->next; 492 free(p); 493 } 494 } 495 } 496