From 9066b3f8349915bcffc1bbec3e08bc088b966f30 Mon Sep 17 00:00:00 2001 From: brooks Date: Tue, 7 Jun 2005 04:05:09 +0000 Subject: Import the OpenBSD dhclient as shipped with OpenBSD-3.7 (the tag OPENBSD_3_7). --- sbin/dhclient/dispatch.c | 495 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 495 insertions(+) create mode 100644 sbin/dhclient/dispatch.c (limited to 'sbin/dhclient/dispatch.c') diff --git a/sbin/dhclient/dispatch.c b/sbin/dhclient/dispatch.c new file mode 100644 index 0000000..a0d7e90 --- /dev/null +++ b/sbin/dhclient/dispatch.c @@ -0,0 +1,495 @@ +/* $OpenBSD: dispatch.c,v 1.31 2004/09/21 04:07:03 david Exp $ */ + +/* + * Copyright 2004 Henning Brauer + * Copyright (c) 1995, 1996, 1997, 1998, 1999 + * The Internet Software Consortium. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#include "dhcpd.h" + +#include + +#include +#include +#include + +struct protocol *protocols; +struct timeout *timeouts; +static struct timeout *free_timeouts; +static int interfaces_invalidated; +void (*bootp_packet_handler)(struct interface_info *, + struct dhcp_packet *, int, unsigned int, + struct iaddr, struct hardware *); + +static int interface_status(struct interface_info *ifinfo); + +/* + * Use getifaddrs() to get a list of all the attached interfaces. For + * each interface that's of type INET and not the loopback interface, + * register that interface with the network I/O software, figure out + * what subnet it's on, and add it to the list of interfaces. + */ +void +discover_interfaces(struct interface_info *iface) +{ + struct ifaddrs *ifap, *ifa; + struct sockaddr_in foo; + struct ifreq *tif; + + if (getifaddrs(&ifap) != 0) + error("getifaddrs failed"); + + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { + if ((ifa->ifa_flags & IFF_LOOPBACK) || + (ifa->ifa_flags & IFF_POINTOPOINT) || + (!(ifa->ifa_flags & IFF_UP))) + continue; + + if (strcmp(iface->name, ifa->ifa_name)) + continue; + + /* + * If we have the capability, extract link information + * and record it in a linked list. + */ + if (ifa->ifa_addr->sa_family == AF_LINK) { + struct sockaddr_dl *foo = + (struct sockaddr_dl *)ifa->ifa_addr; + + iface->index = foo->sdl_index; + iface->hw_address.hlen = foo->sdl_alen; + iface->hw_address.htype = HTYPE_ETHER; /* XXX */ + memcpy(iface->hw_address.haddr, + LLADDR(foo), foo->sdl_alen); + } else if (ifa->ifa_addr->sa_family == AF_INET) { + struct iaddr addr; + + memcpy(&foo, ifa->ifa_addr, sizeof(foo)); + if (foo.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) + continue; + if (!iface->ifp) { + int len = IFNAMSIZ + ifa->ifa_addr->sa_len; + if ((tif = malloc(len)) == NULL) + error("no space to remember ifp"); + strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ); + memcpy(&tif->ifr_addr, ifa->ifa_addr, + ifa->ifa_addr->sa_len); + iface->ifp = tif; + iface->primary_address = foo.sin_addr; + } + addr.len = 4; + memcpy(addr.iabuf, &foo.sin_addr.s_addr, addr.len); + } + } + + if (!iface->ifp) + error("%s: not found", iface->name); + + /* Register the interface... */ + if_register_receive(iface); + if_register_send(iface); + add_protocol(iface->name, iface->rfdesc, got_one, iface); + freeifaddrs(ifap); +} + +void +reinitialize_interfaces(void) +{ + interfaces_invalidated = 1; +} + +/* + * Wait for packets to come in using poll(). When a packet comes in, + * call receive_packet to receive the packet and possibly strip hardware + * addressing information from it, and then call through the + * bootp_packet_handler hook to try to do something with it. + */ +void +dispatch(void) +{ + int count, i, to_msec, nfds = 0; + struct protocol *l; + struct pollfd *fds; + time_t howlong; + + for (l = protocols; l; l = l->next) + nfds++; + + fds = malloc(nfds * sizeof(struct pollfd)); + if (fds == NULL) + error("Can't allocate poll structures."); + + do { + /* + * Call any expired timeouts, and then if there's still + * a timeout registered, time out the select call then. + */ +another: + if (timeouts) { + struct timeout *t; + + if (timeouts->when <= cur_time) { + t = timeouts; + timeouts = timeouts->next; + (*(t->func))(t->what); + t->next = free_timeouts; + free_timeouts = t; + goto another; + } + + /* + * Figure timeout in milliseconds, and check for + * potential overflow, so we can cram into an + * int for poll, while not polling with a + * negative timeout and blocking indefinitely. + */ + howlong = timeouts->when - cur_time; + if (howlong > INT_MAX / 1000) + howlong = INT_MAX / 1000; + to_msec = howlong * 1000; + } else + to_msec = -1; + + /* Set up the descriptors to be polled. */ + for (i = 0, l = protocols; l; l = l->next) { + struct interface_info *ip = l->local; + + if (ip && (l->handler != got_one || !ip->dead)) { + fds[i].fd = l->fd; + fds[i].events = POLLIN; + fds[i].revents = 0; + i++; + } + } + + if (i == 0) + error("No live interfaces to poll on - exiting."); + + /* Wait for a packet or a timeout... XXX */ + count = poll(fds, nfds, to_msec); + + /* Not likely to be transitory... */ + if (count == -1) { + if (errno == EAGAIN || errno == EINTR) { + time(&cur_time); + continue; + } else + error("poll: %m"); + } + + /* Get the current time... */ + time(&cur_time); + + i = 0; + for (l = protocols; l; l = l->next) { + struct interface_info *ip; + ip = l->local; + if ((fds[i].revents & (POLLIN | POLLHUP))) { + fds[i].revents = 0; + if (ip && (l->handler != got_one || + !ip->dead)) + (*(l->handler))(l); + if (interfaces_invalidated) + break; + } + i++; + } + interfaces_invalidated = 0; + } while (1); +} + + +void +got_one(struct protocol *l) +{ + struct sockaddr_in from; + struct hardware hfrom; + struct iaddr ifrom; + ssize_t result; + union { + /* + * Packet input buffer. Must be as large as largest + * possible MTU. + */ + unsigned char packbuf[4095]; + struct dhcp_packet packet; + } u; + struct interface_info *ip = l->local; + + if ((result = receive_packet(ip, u.packbuf, sizeof(u), &from, + &hfrom)) == -1) { + warning("receive_packet failed on %s: %s", ip->name, + strerror(errno)); + ip->errors++; + if ((!interface_status(ip)) || + (ip->noifmedia && ip->errors > 20)) { + /* our interface has gone away. */ + warning("Interface %s no longer appears valid.", + ip->name); + ip->dead = 1; + interfaces_invalidated = 1; + close(l->fd); + remove_protocol(l); + free(ip); + } + return; + } + if (result == 0) + return; + + if (bootp_packet_handler) { + ifrom.len = 4; + memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len); + + (*bootp_packet_handler)(ip, &u.packet, result, + from.sin_port, ifrom, &hfrom); + } +} + +int +interface_status(struct interface_info *ifinfo) +{ + char *ifname = ifinfo->name; + int ifsock = ifinfo->rfdesc; + struct ifreq ifr; + struct ifmediareq ifmr; + + /* get interface flags */ + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) { + syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) on %s: %m", ifname); + goto inactive; + } + + /* + * if one of UP and RUNNING flags is dropped, + * the interface is not active. + */ + if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) + goto inactive; + + /* Next, check carrier on the interface, if possible */ + if (ifinfo->noifmedia) + goto active; + memset(&ifmr, 0, sizeof(ifmr)); + strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); + if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { + if (errno != EINVAL) { + syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m", + ifname); + + ifinfo->noifmedia = 1; + goto active; + } + /* + * EINVAL (or ENOTTY) simply means that the interface + * does not support the SIOCGIFMEDIA ioctl. We regard it alive. + */ + ifinfo->noifmedia = 1; + goto active; + } + if (ifmr.ifm_status & IFM_AVALID) { + switch (ifmr.ifm_active & IFM_NMASK) { + case IFM_ETHER: + if (ifmr.ifm_status & IFM_ACTIVE) + goto active; + else + goto inactive; + break; + default: + goto inactive; + } + } +inactive: + return (0); +active: + return (1); +} + +void +add_timeout(time_t when, void (*where)(void *), void *what) +{ + struct timeout *t, *q; + + /* See if this timeout supersedes an existing timeout. */ + t = NULL; + for (q = timeouts; q; q = q->next) { + if (q->func == where && q->what == what) { + if (t) + t->next = q->next; + else + timeouts = q->next; + break; + } + t = q; + } + + /* If we didn't supersede a timeout, allocate a timeout + structure now. */ + if (!q) { + if (free_timeouts) { + q = free_timeouts; + free_timeouts = q->next; + q->func = where; + q->what = what; + } else { + q = malloc(sizeof(struct timeout)); + if (!q) + error("Can't allocate timeout structure!"); + q->func = where; + q->what = what; + } + } + + q->when = when; + + /* Now sort this timeout into the timeout list. */ + + /* Beginning of list? */ + if (!timeouts || timeouts->when > q->when) { + q->next = timeouts; + timeouts = q; + return; + } + + /* Middle of list? */ + for (t = timeouts; t->next; t = t->next) { + if (t->next->when > q->when) { + q->next = t->next; + t->next = q; + return; + } + } + + /* End of list. */ + t->next = q; + q->next = NULL; +} + +void +cancel_timeout(void (*where)(void *), void *what) +{ + struct timeout *t, *q; + + /* Look for this timeout on the list, and unlink it if we find it. */ + t = NULL; + for (q = timeouts; q; q = q->next) { + if (q->func == where && q->what == what) { + if (t) + t->next = q->next; + else + timeouts = q->next; + break; + } + t = q; + } + + /* If we found the timeout, put it on the free list. */ + if (q) { + q->next = free_timeouts; + free_timeouts = q; + } +} + +/* Add a protocol to the list of protocols... */ +void +add_protocol(char *name, int fd, void (*handler)(struct protocol *), + void *local) +{ + struct protocol *p; + + p = malloc(sizeof(*p)); + if (!p) + error("can't allocate protocol struct for %s", name); + + p->fd = fd; + p->handler = handler; + p->local = local; + p->next = protocols; + protocols = p; +} + +void +remove_protocol(struct protocol *proto) +{ + struct protocol *p, *next, *prev; + + prev = NULL; + for (p = protocols; p; p = next) { + next = p->next; + if (p == proto) { + if (prev) + prev->next = p->next; + else + protocols = p->next; + free(p); + } + } +} + +int +interface_link_status(char *ifname) +{ + struct ifmediareq ifmr; + int sock; + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + error("Can't create socket"); + + memset(&ifmr, 0, sizeof(ifmr)); + strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); + if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) { + /* EINVAL -> link state unknown. treat as active */ + if (errno != EINVAL) + syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m", + ifname); + close(sock); + return (1); + } + close(sock); + + if (ifmr.ifm_status & IFM_AVALID) { + if ((ifmr.ifm_active & IFM_NMASK) == IFM_ETHER) { + if (ifmr.ifm_status & IFM_ACTIVE) + return (1); + else + return (0); + } + } + return (1); +} -- cgit v1.1