diff options
Diffstat (limited to 'contrib/isc-dhcp/common/discover.c')
-rw-r--r-- | contrib/isc-dhcp/common/discover.c | 1138 |
1 files changed, 1138 insertions, 0 deletions
diff --git a/contrib/isc-dhcp/common/discover.c b/contrib/isc-dhcp/common/discover.c new file mode 100644 index 0000000..953bab0 --- /dev/null +++ b/contrib/isc-dhcp/common/discover.c @@ -0,0 +1,1138 @@ +/* dispatch.c + + Network input dispatcher... */ + +/* + * Copyright (c) 1995-2001 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 and Nominum, Inc. + * To learn more about the Internet Software Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: discover.c,v 1.42.2.8 2001/10/18 20:10:26 mellon Exp $ Copyright (c) 1995-2001 The Internet Software Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#include <sys/ioctl.h> + +struct interface_info *interfaces, *dummy_interfaces, *fallback_interface; +int interfaces_invalidated; +int quiet_interface_discovery; +u_int16_t local_port; +u_int16_t remote_port; +int (*dhcp_interface_setup_hook) (struct interface_info *, struct iaddr *); +int (*dhcp_interface_discovery_hook) (struct interface_info *); +isc_result_t (*dhcp_interface_startup_hook) (struct interface_info *); +int (*dhcp_interface_shutdown_hook) (struct interface_info *); + +struct in_addr limited_broadcast; +struct in_addr local_address; + +void (*bootp_packet_handler) PROTO ((struct interface_info *, + struct dhcp_packet *, unsigned, + unsigned int, + struct iaddr, struct hardware *)); + +omapi_object_type_t *dhcp_type_interface; +#if defined (TRACING) +trace_type_t *interface_trace; +trace_type_t *inpacket_trace; +trace_type_t *outpacket_trace; +#endif +struct interface_info **interface_vector; +int interface_count; +int interface_max; + +OMAPI_OBJECT_ALLOC (interface, struct interface_info, dhcp_type_interface) + +isc_result_t interface_setup () +{ + isc_result_t status; + status = omapi_object_type_register (&dhcp_type_interface, + "interface", + dhcp_interface_set_value, + dhcp_interface_get_value, + dhcp_interface_destroy, + dhcp_interface_signal_handler, + dhcp_interface_stuff_values, + dhcp_interface_lookup, + dhcp_interface_create, + dhcp_interface_remove, + 0, 0, 0, + sizeof (struct interface_info), + interface_initialize, RC_MISC); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't register interface object type: %s", + isc_result_totext (status)); + + return status; +} + +#if defined (TRACING) +void interface_trace_setup () +{ + interface_trace = trace_type_register ("interface", (void *)0, + trace_interface_input, + trace_interface_stop, MDL); + inpacket_trace = trace_type_register ("inpacket", (void *)0, + trace_inpacket_input, + trace_inpacket_stop, MDL); + outpacket_trace = trace_type_register ("outpacket", (void *)0, + trace_outpacket_input, + trace_outpacket_stop, MDL); +} +#endif + +isc_result_t interface_initialize (omapi_object_t *ipo, + const char *file, int line) +{ + struct interface_info *ip = (struct interface_info *)ipo; + ip -> rfdesc = ip -> wfdesc = -1; + return ISC_R_SUCCESS; +} + +/* Use the SIOCGIFCONF ioctl 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 (state) + int state; +{ + struct interface_info *tmp, *ip; + struct interface_info *last, *next; + char buf [2048]; + struct ifconf ic; + struct ifreq ifr; + int i; + int sock; + int address_count = 0; + struct subnet *subnet; + struct shared_network *share; + struct sockaddr_in foo; + int ir; + struct ifreq *tif; +#ifdef ALIAS_NAMES_PERMUTED + char *s; +#endif + isc_result_t status; + static int setup_fallback = 0; + int wifcount = 0; + + /* Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. */ + if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + log_fatal ("Can't create addrlist socket"); + + /* Get the interface configuration information... */ + +#ifdef SIOCGIFCONF_ZERO_PROBE + /* linux will only tell us how long a buffer it wants if we give it + * a null buffer first. So, do a dry run to figure out the length. + * + * XXX this code is duplicated from below because trying to fold + * the logic into the if statement and goto resulted in excesssive + * obfuscation. The intent is that unless you run Linux you shouldn't + * have to deal with this. */ + + ic.ifc_len = 0; + ic.ifc_ifcu.ifcu_buf = (caddr_t)NULL; +#else + /* otherwise, we just feed it a starting size, and it'll tell us if + * it needs more */ + + ic.ifc_len = sizeof buf; + ic.ifc_ifcu.ifcu_buf = (caddr_t)buf; +#endif + + gifconf_again: + i = ioctl(sock, SIOCGIFCONF, &ic); + + if (i < 0) + log_fatal ("ioctl: SIOCGIFCONF: %m"); + +#ifdef SIOCGIFCONF_ZERO_PROBE + /* Workaround for SIOCGIFCONF bug on some Linux versions. */ + if (ic.ifc_ifcu.ifcu_buf == 0 && ic.ifc_len == 0) { + ic.ifc_len = sizeof buf; + ic.ifc_ifcu.ifcu_buf = (caddr_t)buf; + goto gifconf_again; + } +#endif + + /* If the SIOCGIFCONF resulted in more data than would fit in + a buffer, allocate a bigger buffer. */ + if ((ic.ifc_ifcu.ifcu_buf == buf +#ifdef SIOCGIFCONF_ZERO_PROBE + || ic.ifc_ifcu.ifcu_buf == 0 +#endif + ) && ic.ifc_len > sizeof buf) { + ic.ifc_ifcu.ifcu_buf = dmalloc ((size_t)ic.ifc_len, MDL); + if (!ic.ifc_ifcu.ifcu_buf) + log_fatal ("Can't allocate SIOCGIFCONF buffer."); + goto gifconf_again; +#ifdef SIOCGIFCONF_ZERO_PROBE + } else if (ic.ifc_ifcu.ifcu_buf == 0) { + ic.ifc_ifcu.ifcu_buf = (caddr_t)buf; + ic.ifc_len = sizeof buf; + goto gifconf_again; +#endif + } + + + /* If we already have a list of interfaces, and we're running as + a DHCP server, the interfaces were requested. */ + if (interfaces && (state == DISCOVER_SERVER || + state == DISCOVER_RELAY || + state == DISCOVER_REQUESTED)) + ir = 0; + else if (state == DISCOVER_UNCONFIGURED) + ir = INTERFACE_REQUESTED | INTERFACE_AUTOMATIC; + else + ir = INTERFACE_REQUESTED; + + /* Cycle through the list of interfaces looking for IP addresses. */ + for (i = 0; i < ic.ifc_len;) { + struct ifreq *ifp = (struct ifreq *)((caddr_t)ic.ifc_req + i); +#ifdef HAVE_SA_LEN + if (ifp -> ifr_addr.sa_len > sizeof (struct sockaddr)) + i += (sizeof ifp -> ifr_name) + ifp -> ifr_addr.sa_len; + else +#endif + i += sizeof *ifp; + +#ifdef ALIAS_NAMES_PERMUTED + if ((s = strrchr (ifp -> ifr_name, ':'))) { + *s = 0; + } +#endif + +#ifdef SKIP_DUMMY_INTERFACES + if (!strncmp (ifp -> ifr_name, "dummy", 5)) + continue; +#endif + + + /* See if this is the sort of interface we want to + deal with. */ + strcpy (ifr.ifr_name, ifp -> ifr_name); + if (ioctl (sock, SIOCGIFFLAGS, &ifr) < 0) + log_fatal ("Can't get interface flags for %s: %m", + ifr.ifr_name); + + /* See if we've seen an interface that matches this one. */ + for (tmp = interfaces; tmp; tmp = tmp -> next) + if (!strcmp (tmp -> name, ifp -> ifr_name)) + break; + + /* Skip loopback, point-to-point and down interfaces, + except don't skip down interfaces if we're trying to + get a list of configurable interfaces. */ + if (((ifr.ifr_flags & IFF_LOOPBACK || + ifr.ifr_flags & IFF_POINTOPOINT) && !tmp) || + (!(ifr.ifr_flags & IFF_UP) && + state != DISCOVER_UNCONFIGURED)) + continue; + + /* If there isn't already an interface by this name, + allocate one. */ + if (!tmp) { + tmp = (struct interface_info *)0; + status = interface_allocate (&tmp, MDL); + if (status != ISC_R_SUCCESS) + log_fatal ("Error allocating interface %s: %s", + ifp -> ifr_name, + isc_result_totext (status)); + strcpy (tmp -> name, ifp -> ifr_name); + interface_snorf (tmp, ir); + interface_dereference (&tmp, MDL); + tmp = interfaces; /* XXX */ + } + + if (dhcp_interface_discovery_hook) + (*dhcp_interface_discovery_hook) (tmp); + + /* If we have the capability, extract link information + and record it in a linked list. */ +#ifdef HAVE_AF_LINK + if (ifp -> ifr_addr.sa_family == AF_LINK) { + struct sockaddr_dl *foo = ((struct sockaddr_dl *) + (&ifp -> ifr_addr)); +#if defined (HAVE_SIN_LEN) + tmp -> hw_address.hlen = foo -> sdl_alen; +#else + tmp -> hw_address.hlen = 6; /* XXX!!! */ +#endif + tmp -> hw_address.hbuf [0] = HTYPE_ETHER; /* XXX */ + memcpy (&tmp -> hw_address.hbuf [1], + LLADDR (foo), tmp -> hw_address.hlen); + tmp -> hw_address.hlen++; /* for type. */ + } else +#endif /* AF_LINK */ + + if (ifp -> ifr_addr.sa_family == AF_INET) { + struct iaddr addr; + + /* Get a pointer to the address... */ + memcpy (&foo, &ifp -> ifr_addr, + sizeof ifp -> ifr_addr); + + /* We don't want the loopback interface. */ + if (foo.sin_addr.s_addr == htonl (INADDR_LOOPBACK) && + ((tmp -> flags & INTERFACE_AUTOMATIC) && + state == DISCOVER_SERVER)) + continue; + + + /* If this is the first real IP address we've + found, keep a pointer to ifreq structure in + which we found it. */ + if (!tmp -> ifp) { +#ifdef HAVE_SA_LEN + unsigned len = ((sizeof ifp -> ifr_name) + + ifp -> ifr_addr.sa_len); +#else + unsigned len = sizeof *ifp; +#endif + tif = (struct ifreq *)dmalloc (len, MDL); + if (!tif) + log_fatal ("no space for ifp."); + memcpy (tif, ifp, len); + tmp -> ifp = tif; + tmp -> primary_address = foo.sin_addr; + } + + /* Grab the address... */ + addr.len = 4; + memcpy (addr.iabuf, &foo.sin_addr.s_addr, + addr.len); + if (dhcp_interface_setup_hook) + (*dhcp_interface_setup_hook) (tmp, &addr); + } + } + + /* If we allocated a buffer, free it. */ + if (ic.ifc_ifcu.ifcu_buf != buf) + dfree (ic.ifc_ifcu.ifcu_buf, MDL); + +#if defined (LINUX_SLASHPROC_DISCOVERY) + /* On Linux, interfaces that don't have IP addresses don't + show up in the SIOCGIFCONF syscall. This only matters for + the DHCP client, of course - the relay agent and server + should only care about interfaces that are configured with + IP addresses anyway. + + The PROCDEV_DEVICE (/proc/net/dev) is a kernel-supplied file + that, when read, prints a human readable network status. We + extract the names of the network devices by skipping the first + two lines (which are header) and then parsing off everything + up to the colon in each subsequent line - these lines start + with the interface name, then a colon, then a bunch of + statistics. */ + + if (state == DISCOVER_UNCONFIGURED) { + FILE *proc_dev; + char buffer [256]; + int skip = 2; + + proc_dev = fopen (PROCDEV_DEVICE, "r"); + if (!proc_dev) + log_fatal ("%s: %m", PROCDEV_DEVICE); + + while (fgets (buffer, sizeof buffer, proc_dev)) { + char *name = buffer; + char *sep; + + /* Skip the first two blocks, which are header + lines. */ + if (skip) { + --skip; + continue; + } + + sep = strrchr (buffer, ':'); + if (sep) + *sep = '\0'; + while (*name == ' ') + name++; + + /* See if we've seen an interface that matches + this one. */ + for (tmp = interfaces; tmp; tmp = tmp -> next) + if (!strcmp (tmp -> name, name)) + break; + + /* If we found one, nothing more to do.. */ + if (tmp) + continue; + + /* Otherwise, allocate one. */ + tmp = (struct interface_info *)0; + status = interface_allocate (&tmp, MDL); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't allocate interface %s: %s", + name, isc_result_totext (status)); + tmp -> flags = ir; + strncpy (tmp -> name, name, IFNAMSIZ); + interface_reference (&tmp -> next, interfaces, MDL); + interface_dereference (&interfaces, MDL); + interface_reference (&interfaces, tmp, MDL); + interface_dereference (&tmp, MDL); + tmp = interfaces; + + if (dhcp_interface_discovery_hook) + (*dhcp_interface_discovery_hook) (tmp); + + } + fclose (proc_dev); + } +#endif + + /* Now cycle through all the interfaces we found, looking for + hardware addresses. */ +#if defined (HAVE_SIOCGIFHWADDR) && !defined (HAVE_AF_LINK) + for (tmp = interfaces; tmp; tmp = tmp -> next) { + struct ifreq ifr; + struct sockaddr sa; + int b, sk; + + if (!tmp -> ifp) { + /* Make up an ifreq structure. */ + tif = (struct ifreq *)dmalloc (sizeof (struct ifreq), + MDL); + if (!tif) + log_fatal ("no space to remember ifp."); + memset (tif, 0, sizeof (struct ifreq)); + strcpy (tif -> ifr_name, tmp -> name); + tmp -> ifp = tif; + } + + /* Read the hardware address from this interface. */ + ifr = *tmp -> ifp; + if (ioctl (sock, SIOCGIFHWADDR, &ifr) < 0) + continue; + + sa = *(struct sockaddr *)&ifr.ifr_hwaddr; + + switch (sa.sa_family) { +#ifdef HAVE_ARPHRD_TUNNEL + case ARPHRD_TUNNEL: + /* ignore tunnel interfaces. */ +#endif +#ifdef HAVE_ARPHRD_ROSE + case ARPHRD_ROSE: +#endif +#ifdef HAVE_ARPHRD_LOOPBACK + case ARPHRD_LOOPBACK: + /* ignore loopback interface */ + break; +#endif + + case ARPHRD_ETHER: + tmp -> hw_address.hlen = 7; + tmp -> hw_address.hbuf [0] = ARPHRD_ETHER; + memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6); + break; + +#ifndef HAVE_ARPHRD_IEEE802 +# define ARPHRD_IEEE802 HTYPE_IEEE802 +#endif +#if defined (HAVE_ARPHRD_IEEE802_TR) + case ARPHRD_IEEE802_TR: +#endif + case ARPHRD_IEEE802: + tmp -> hw_address.hlen = 7; + tmp -> hw_address.hbuf [0] = ARPHRD_IEEE802; + memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6); + break; + +#ifndef HAVE_ARPHRD_FDDI +# define ARPHRD_FDDI HTYPE_FDDI +#endif + case ARPHRD_FDDI: + tmp -> hw_address.hlen = 17; + tmp -> hw_address.hbuf [0] = HTYPE_FDDI; /* XXX */ + memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 16); + break; + +#ifdef HAVE_ARPHRD_METRICOM + case ARPHRD_METRICOM: + tmp -> hw_address.hlen = 7; + tmp -> hw_address.hbuf [0] = ARPHRD_METRICOM; + memcpy (&tmp -> hw_address.hbuf [0], sa.sa_data, 6); + break; +#endif + +#ifdef HAVE_ARPHRD_AX25 + case ARPHRD_AX25: + tmp -> hw_address.hlen = 7; + tmp -> hw_address.hbuf [0] = ARPHRD_AX25; + memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6); + break; +#endif + +#ifdef HAVE_ARPHRD_NETROM + case ARPHRD_NETROM: + tmp -> hw_address.hlen = 7; + tmp -> hw_address.hbuf [0] = ARPHRD_NETROM; + memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6); + break; +#endif + + default: + log_error ("%s: unknown hardware address type %d", + ifr.ifr_name, sa.sa_family); + break; + } + } +#endif /* defined (HAVE_SIOCGIFHWADDR) && !defined (HAVE_AF_LINK) */ + + /* If we're just trying to get a list of interfaces that we might + be able to configure, we can quit now. */ + if (state == DISCOVER_UNCONFIGURED) { + close (sock); + return; + } + + /* Weed out the interfaces that did not have IP addresses. */ + tmp = last = next = (struct interface_info *)0; + if (interfaces) + interface_reference (&tmp, interfaces, MDL); + while (tmp) { + if (next) + interface_dereference (&next, MDL); + if (tmp -> next) + interface_reference (&next, tmp -> next, MDL); + /* skip interfaces that are running already */ + if (tmp -> flags & INTERFACE_RUNNING) + continue; + if ((tmp -> flags & INTERFACE_AUTOMATIC) && + state == DISCOVER_REQUESTED) + tmp -> flags &= ~(INTERFACE_AUTOMATIC | + INTERFACE_REQUESTED); + if (!tmp -> ifp || !(tmp -> flags & INTERFACE_REQUESTED)) { + if ((tmp -> flags & INTERFACE_REQUESTED) != ir) + log_fatal ("%s: not found", tmp -> name); + if (!last) { + if (interfaces) + interface_dereference (&interfaces, + MDL); + if (next) + interface_reference (&interfaces, next, MDL); + } else { + interface_dereference (&last -> next, MDL); + if (next) + interface_reference (&last -> next, + next, MDL); + } + if (tmp -> next) + interface_dereference (&tmp -> next, MDL); + + /* Remember the interface in case we need to know + about it later. */ + if (dummy_interfaces) { + interface_reference (&tmp -> next, + dummy_interfaces, MDL); + interface_dereference (&dummy_interfaces, MDL); + } + interface_reference (&dummy_interfaces, tmp, MDL); + interface_dereference (&tmp, MDL); + if (next) + interface_reference (&tmp, next, MDL); + continue; + } + last = tmp; + + memcpy (&foo, &tmp -> ifp -> ifr_addr, + sizeof tmp -> ifp -> ifr_addr); + + /* We must have a subnet declaration for each interface. */ + if (!tmp -> shared_network && (state == DISCOVER_SERVER)) { + log_error ("%s", ""); + log_error ("No subnet declaration for %s (%s).", + tmp -> name, inet_ntoa (foo.sin_addr)); + if (supports_multiple_interfaces (tmp)) { + log_error ("** Ignoring requests on %s. %s", + tmp -> name, "If this is not what"); + log_error (" you want, please write %s", + "a subnet declaration"); + log_error (" in your dhcpd.conf file %s", + "for the network segment"); + log_error (" to %s %s %s", + "which interface", + tmp -> name, "is attached. **"); + log_error ("%s", ""); + goto next; + } else { + log_error ("You must write a subnet %s", + " declaration for this"); + log_error ("subnet. You cannot prevent %s", + "the DHCP server"); + log_error ("from listening on this subnet %s", + "because your"); + log_fatal ("operating system does not %s.", + "support this capability"); + } + } + + /* Find subnets that don't have valid interface + addresses... */ + for (subnet = (tmp -> shared_network + ? tmp -> shared_network -> subnets + : (struct subnet *)0); + subnet; subnet = subnet -> next_sibling) { + if (!subnet -> interface_address.len) { + /* Set the interface address for this subnet + to the first address we found. */ + subnet -> interface_address.len = 4; + memcpy (subnet -> interface_address.iabuf, + &foo.sin_addr.s_addr, 4); + } + } + + /* Flag the index as not having been set, so that the + interface registerer can set it or not as it chooses. */ + tmp -> index = -1; + + /* Register the interface... */ + if_register_receive (tmp); + if_register_send (tmp); + + interface_stash (tmp); + wifcount++; +#if defined (HAVE_SETFD) + if (fcntl (tmp -> rfdesc, F_SETFD, 1) < 0) + log_error ("Can't set close-on-exec on %s: %m", + tmp -> name); + if (tmp -> rfdesc != tmp -> wfdesc) { + if (fcntl (tmp -> wfdesc, F_SETFD, 1) < 0) + log_error ("Can't set close-on-exec on %s: %m", + tmp -> name); + } +#endif + next: + interface_dereference (&tmp, MDL); + if (next) + interface_reference (&tmp, next, MDL); + } + + /* Now register all the remaining interfaces as protocols. */ + for (tmp = interfaces; tmp; tmp = tmp -> next) { + /* not if it's been registered before */ + if (tmp -> flags & INTERFACE_RUNNING) + continue; + if (tmp -> rfdesc == -1) + continue; + status = omapi_register_io_object ((omapi_object_t *)tmp, + if_readsocket, 0, + got_one, 0, 0); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't register I/O handle for %s: %s", + tmp -> name, isc_result_totext (status)); + } + + close (sock); + + if (state == DISCOVER_SERVER && wifcount == 0) { + log_info ("%s", ""); + log_fatal ("Not configured to listen on any interfaces!"); + } + + if (!setup_fallback) { + setup_fallback = 1; + maybe_setup_fallback (); + } + +#if defined (HAVE_SETFD) + if (fallback_interface) { + if (fcntl (fallback_interface -> rfdesc, F_SETFD, 1) < 0) + log_error ("Can't set close-on-exec on fallback: %m"); + if (fallback_interface -> rfdesc != fallback_interface -> wfdesc) { + if (fcntl (fallback_interface -> wfdesc, F_SETFD, 1) < 0) + log_error ("Can't set close-on-exec on fallback: %m"); + } + } +#endif +} + +int if_readsocket (h) + omapi_object_t *h; +{ + struct interface_info *ip; + + if (h -> type != dhcp_type_interface) + return -1; + ip = (struct interface_info *)h; + return ip -> rfdesc; +} + +int setup_fallback (struct interface_info **fp, const char *file, int line) +{ + isc_result_t status; + + status = interface_allocate (&fallback_interface, file, line); + if (status != ISC_R_SUCCESS) + log_fatal ("Error allocating fallback interface: %s", + isc_result_totext (status)); + strcpy (fallback_interface -> name, "fallback"); + if (dhcp_interface_setup_hook) + (*dhcp_interface_setup_hook) (fallback_interface, + (struct iaddr *)0); + status = interface_reference (fp, fallback_interface, file, line); + + fallback_interface -> index = -1; + interface_stash (fallback_interface); + return status == ISC_R_SUCCESS; +} + +void reinitialize_interfaces () +{ + struct interface_info *ip; + + for (ip = interfaces; ip; ip = ip -> next) { + if_reinitialize_receive (ip); + if_reinitialize_send (ip); + } + + if (fallback_interface) + if_reinitialize_send (fallback_interface); + + interfaces_invalidated = 1; +} + +isc_result_t got_one (h) + omapi_object_t *h; +{ + struct sockaddr_in from; + struct hardware hfrom; + struct iaddr ifrom; + int result; + union { + unsigned char packbuf [4095]; /* Packet input buffer. + Must be as large as largest + possible MTU. */ + struct dhcp_packet packet; + } u; + struct interface_info *ip; + + if (h -> type != dhcp_type_interface) + return ISC_R_INVALIDARG; + ip = (struct interface_info *)h; + + again: + if ((result = + receive_packet (ip, u.packbuf, sizeof u, &from, &hfrom)) < 0) { + log_error ("receive_packet failed on %s: %m", ip -> name); + return ISC_R_UNEXPECTED; + } + if (result == 0) + return ISC_R_UNEXPECTED; + + /* If we didn't at least get the fixed portion of the BOOTP + packet, drop the packet. We're allowing packets with no + sname or filename, because we're aware of at least one + client that sends such packets, but this definitely falls + into the category of being forgiving. */ + if (result < DHCP_FIXED_NON_UDP - DHCP_SNAME_LEN - DHCP_FILE_LEN) + return ISC_R_UNEXPECTED; + + if (bootp_packet_handler) { + ifrom.len = 4; + memcpy (ifrom.iabuf, &from.sin_addr, ifrom.len); + + (*bootp_packet_handler) (ip, &u.packet, (unsigned)result, + from.sin_port, ifrom, &hfrom); + } + + /* If there is buffered data, read again. This is for, e.g., + bpf, which may return two packets at once. */ + if (ip -> rbuf_offset != ip -> rbuf_len) + goto again; + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_interface_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + struct interface_info *interface; + isc_result_t status; + int foo; + + if (h -> type != dhcp_type_interface) + return ISC_R_INVALIDARG; + interface = (struct interface_info *)h; + + if (!omapi_ds_strcmp (name, "name")) { + if ((value -> type == omapi_datatype_data || + value -> type == omapi_datatype_string) && + value -> u.buffer.len < sizeof interface -> name) { + memcpy (interface -> name, + value -> u.buffer.value, + value -> u.buffer.len); + interface -> name [value -> u.buffer.len] = 0; + } else + return ISC_R_INVALIDARG; + return ISC_R_SUCCESS; + } + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> set_value) { + status = ((*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value)); + if (status == ISC_R_SUCCESS || status == ISC_R_UNCHANGED) + return status; + } + + return ISC_R_NOTFOUND; +} + + +isc_result_t dhcp_interface_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + return ISC_R_NOTIMPLEMENTED; +} + +isc_result_t dhcp_interface_destroy (omapi_object_t *h, + const char *file, int line) +{ + struct interface_info *interface; + isc_result_t status; + + if (h -> type != dhcp_type_interface) + return ISC_R_INVALIDARG; + interface = (struct interface_info *)h; + + if (interface -> ifp) { + dfree (interface -> ifp, file, line); + interface -> ifp = 0; + } + if (interface -> next) + interface_dereference (&interface -> next, file, line); + if (interface -> rbuf) { + dfree (interface -> rbuf, file, line); + interface -> rbuf = (unsigned char *)0; + } + if (interface -> client) + interface -> client = (struct client_state *)0; + + if (interface -> shared_network) + omapi_object_dereference ((omapi_object_t **) + &interface -> shared_network, MDL); + + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_interface_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + struct interface_info *ip, *interface; + struct client_config *config; + struct client_state *client; + isc_result_t status; + + if (h -> type != dhcp_type_interface) + return ISC_R_INVALIDARG; + interface = (struct interface_info *)h; + + /* If it's an update signal, see if the interface is dead right + now, or isn't known at all, and if that's the case, revive it. */ + if (!strcmp (name, "update")) { + for (ip = dummy_interfaces; ip; ip = ip -> next) + if (ip == interface) + break; + if (ip && dhcp_interface_startup_hook) + return (*dhcp_interface_startup_hook) (ip); + + for (ip = interfaces; ip; ip = ip -> next) + if (ip == interface) + break; + if (!ip && dhcp_interface_startup_hook) + return (*dhcp_interface_startup_hook) (ip); + } + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> get_value) { + status = ((*(h -> inner -> type -> signal_handler)) + (h -> inner, name, ap)); + if (status == ISC_R_SUCCESS) + return status; + } + return ISC_R_NOTFOUND; +} + +isc_result_t dhcp_interface_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *h) +{ + struct interface_info *interface; + isc_result_t status; + + if (h -> type != dhcp_type_interface) + return ISC_R_INVALIDARG; + interface = (struct interface_info *)h; + + /* Write out all the values. */ + + status = omapi_connection_put_name (c, "state"); + if (status != ISC_R_SUCCESS) + return status; + if (interface -> flags && INTERFACE_REQUESTED) + status = omapi_connection_put_string (c, "up"); + else + status = omapi_connection_put_string (c, "down"); + if (status != ISC_R_SUCCESS) + return status; + + /* Write out the inner object, if any. */ + if (h -> inner && h -> inner -> type -> stuff_values) { + status = ((*(h -> inner -> type -> stuff_values)) + (c, id, h -> inner)); + if (status == ISC_R_SUCCESS) + return status; + } + + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_interface_lookup (omapi_object_t **ip, + omapi_object_t *id, + omapi_object_t *ref) +{ + omapi_value_t *tv = (omapi_value_t *)0; + isc_result_t status; + struct interface_info *interface; + + if (!ref) + return ISC_R_NOKEYS; + + /* First see if we were sent a handle. */ + status = omapi_get_value_str (ref, id, "handle", &tv); + if (status == ISC_R_SUCCESS) { + status = omapi_handle_td_lookup (ip, tv -> value); + + omapi_value_dereference (&tv, MDL); + if (status != ISC_R_SUCCESS) + return status; + + /* Don't return the object if the type is wrong. */ + if ((*ip) -> type != dhcp_type_interface) { + omapi_object_dereference (ip, MDL); + return ISC_R_INVALIDARG; + } + } + + /* Now look for an interface name. */ + status = omapi_get_value_str (ref, id, "name", &tv); + if (status == ISC_R_SUCCESS) { + char *s; + unsigned len; + for (interface = interfaces; interface; + interface = interface -> next) { + s = memchr (interface -> name, 0, IFNAMSIZ); + if (s) + len = s - &interface -> name [0]; + else + len = IFNAMSIZ; + if ((tv -> value -> u.buffer.len == len && + !memcmp (interface -> name, + (char *)tv -> value -> u.buffer.value, + len))) + break; + } + if (!interface) { + for (interface = dummy_interfaces; + interface; interface = interface -> next) { + s = memchr (interface -> name, 0, IFNAMSIZ); + if (s) + len = s - &interface -> name [0]; + else + len = IFNAMSIZ; + if ((tv -> value -> u.buffer.len == len && + !memcmp (interface -> name, + (char *) + tv -> value -> u.buffer.value, + len))) + break; + } + } + + omapi_value_dereference (&tv, MDL); + if (*ip && *ip != (omapi_object_t *)interface) { + omapi_object_dereference (ip, MDL); + return ISC_R_KEYCONFLICT; + } else if (!interface) { + if (*ip) + omapi_object_dereference (ip, MDL); + return ISC_R_NOTFOUND; + } else if (!*ip) + omapi_object_reference (ip, + (omapi_object_t *)interface, + MDL); + } + + /* If we get to here without finding an interface, no valid key was + specified. */ + if (!*ip) + return ISC_R_NOKEYS; + return ISC_R_SUCCESS; +} + +/* actually just go discover the interface */ +isc_result_t dhcp_interface_create (omapi_object_t **lp, + omapi_object_t *id) +{ + struct interface_info *hp; + isc_result_t status; + + hp = (struct interface_info *)0; + status = interface_allocate (&hp, MDL); + if (status != ISC_R_SUCCESS) + return status; + hp -> flags = INTERFACE_REQUESTED; + status = interface_reference ((struct interface_info **)lp, hp, MDL); + interface_dereference (&hp, MDL); + return status; +} + +isc_result_t dhcp_interface_remove (omapi_object_t *lp, + omapi_object_t *id) +{ + struct interface_info *interface, *ip, *last; + + interface = (struct interface_info *)lp; + + /* remove from interfaces */ + last = 0; + for (ip = interfaces; ip; ip = ip -> next) { + if (ip == interface) { + if (last) { + interface_dereference (&last -> next, MDL); + if (ip -> next) + interface_reference (&last -> next, + ip -> next, MDL); + } else { + interface_dereference (&interfaces, MDL); + if (ip -> next) + interface_reference (&interfaces, + ip -> next, MDL); + } + if (ip -> next) + interface_dereference (&ip -> next, MDL); + break; + } + last = ip; + } + if (!ip) + return ISC_R_NOTFOUND; + + /* add the interface to the dummy_interface list */ + if (dummy_interfaces) { + interface_reference (&interface -> next, + dummy_interfaces, MDL); + interface_dereference (&dummy_interfaces, MDL); + } + interface_reference (&dummy_interfaces, interface, MDL); + + /* do a DHCPRELEASE */ + if (dhcp_interface_shutdown_hook) + (*dhcp_interface_shutdown_hook) (interface); + + /* remove the io object */ + omapi_unregister_io_object ((omapi_object_t *)interface); + + if_deregister_send (interface); + if_deregister_receive (interface); + + return ISC_R_SUCCESS; +} + +void interface_stash (struct interface_info *tptr) +{ + struct interface_info **vec; + int delta; + + /* If the registerer didn't assign an index, assign one now. */ + if (tptr -> index == -1) { + tptr -> index = interface_count++; + while (tptr -> index < interface_max && + interface_vector [tptr -> index]) + tptr -> index = interface_count++; + } + + if (interface_max <= tptr -> index) { + delta = tptr -> index - interface_max + 10; + vec = dmalloc ((interface_max + delta) * + sizeof (struct interface_info *), MDL); + if (!vec) + return; + memset (&vec [interface_max], 0, + (sizeof (struct interface_info *)) * delta); + interface_max += delta; + if (interface_vector) { + memcpy (vec, interface_vector, + (interface_count * + sizeof (struct interface_info *))); + dfree (interface_vector, MDL); + } + interface_vector = vec; + } + interface_reference (&interface_vector [tptr -> index], tptr, MDL); + if (tptr -> index >= interface_count) + interface_count = tptr -> index + 1; +#if defined (TRACING) + trace_interface_register (interface_trace, tptr); +#endif +} + +void interface_snorf (struct interface_info *tmp, int ir) +{ + tmp -> circuit_id = (u_int8_t *)tmp -> name; + tmp -> circuit_id_len = strlen (tmp -> name); + tmp -> remote_id = 0; + tmp -> remote_id_len = 0; + tmp -> flags = ir; + if (interfaces) { + interface_reference (&tmp -> next, + interfaces, MDL); + interface_dereference (&interfaces, MDL); + } + interface_reference (&interfaces, tmp, MDL); +} |