diff options
Diffstat (limited to 'usr.sbin/dhcpd/bootp.c')
-rw-r--r-- | usr.sbin/dhcpd/bootp.c | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/usr.sbin/dhcpd/bootp.c b/usr.sbin/dhcpd/bootp.c new file mode 100644 index 00000000000..902d2f4f5ef --- /dev/null +++ b/usr.sbin/dhcpd/bootp.c @@ -0,0 +1,369 @@ +/* bootp.c + + BOOTP Protocol support. */ + +/* + * Copyright (c) 1995, 1996, 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 <mellon@fugue.com> 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" + +void bootp (packet) + struct packet *packet; +{ + int result; + struct host_decl *hp; + struct host_decl *host = (struct host_decl *)0; + struct packet outgoing; + struct dhcp_packet raw; + struct sockaddr_in to; + struct in_addr from; + struct hardware hto; + struct tree_cache *options [256]; + struct subnet *subnet; + struct lease *lease; + struct iaddr ip_address; + int i; + + if (packet -> raw -> op != BOOTREQUEST) + return; + + note ("BOOTREQUEST from %s via %s%s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + packet -> raw -> giaddr.s_addr + ? inet_ntoa (packet -> raw -> giaddr) + : packet -> interface -> name, + packet -> options_valid ? "" : " (non-rfc1048)"); + + + + if (!locate_network (packet)) + return; + + hp = find_hosts_by_haddr (packet -> raw -> htype, + packet -> raw -> chaddr, + packet -> raw -> hlen); + + lease = find_lease (packet, packet -> shared_network, 0); + + /* Find an IP address in the host_decl that matches the + specified network. */ + if (hp) + subnet = find_host_for_network (&hp, &ip_address, + packet -> shared_network); + else + subnet = (struct subnet *)0; + + if (!subnet) { + /* We didn't find an applicable host declaration. + Just in case we may be able to dynamically assign + an address, see if there's a host declaration + that doesn't have an ip address associated with it. */ + if (hp) { + for (; hp; hp = hp -> n_ipaddr) { + if (!hp -> fixed_addr) { + host = hp; + break; + } + } + } + + if (host && (!host -> group -> allow_booting)) { + note ("Ignoring excluded BOOTP client %s", + host -> name + ? host -> name + : print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr)); + return; + } + + if (host && (!host -> group -> allow_bootp)) { + note ("Ignoring BOOTP request from client %s", + host -> name + ? host -> name + : print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr)); + return; + } + + /* If we've been told not to boot unknown clients, + and we didn't find any host record for this client, + ignore it. */ + if (!host && !(packet -> shared_network -> + group -> boot_unknown_clients)) { + note ("Ignoring unknown BOOTP client %s via %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + packet -> raw -> giaddr.s_addr + ? inet_ntoa (packet -> raw -> giaddr) + : packet -> interface -> name); + return; + } + + /* If we've been told not to boot with bootp on this + network, ignore it. */ + if (!host && + !(packet -> shared_network -> group -> allow_bootp)) { + note ("Ignoring BOOTP request from client %s via %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + packet -> raw -> giaddr.s_addr + ? inet_ntoa (packet -> raw -> giaddr) + : packet -> interface -> name); + return; + } + + /* If the packet is from a host we don't know and there + are no dynamic bootp addresses on the network it came + in on, drop it on the floor. */ + if (!(packet -> shared_network -> group -> dynamic_bootp)) { + lose: + note ("No applicable record for BOOTP host %s via %s", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + packet -> raw -> giaddr.s_addr + ? inet_ntoa (packet -> raw -> giaddr) + : packet -> interface -> name); + return; + } + + /* If a lease has already been assigned to this client + and it's still okay to use dynamic bootp on + that lease, reassign it. */ + if (lease) { + /* If this lease can be used for dynamic bootp, + do so. */ + if ((lease -> flags & DYNAMIC_BOOTP_OK)) { + + /* If it's not a DYNAMIC_BOOTP lease, + release it before reassigning it + so that we don't get a lease + conflict. */ + if (!(lease -> flags & BOOTP_LEASE)) + release_lease (lease); + + lease -> host = host; + ack_lease (packet, lease, 0, 0); + return; + } + + /* If dynamic BOOTP is no longer allowed for + this lease, set it free. */ + release_lease (lease); + } + + /* If there are dynamic bootp addresses that might be + available, try to snag one. */ + for (lease = packet -> shared_network -> last_lease; + lease && lease -> ends <= cur_time; + lease = lease -> prev) { + if ((lease -> flags & DYNAMIC_BOOTP_OK)) { + lease -> host = host; + ack_lease (packet, lease, 0, 0); + return; + } + } + goto lose; + } + + /* Make sure we're allowed to boot this client. */ + if (hp && (!hp -> group -> allow_booting)) { + note ("Ignoring excluded BOOTP client %s", + hp -> name); + return; + } + + /* Make sure we're allowed to boot this client with bootp. */ + if (hp && (!hp -> group -> allow_bootp)) { + note ("Ignoring BOOTP request from client %s", + hp -> name); + return; + } + + /* Set up the outgoing packet... */ + memset (&outgoing, 0, sizeof outgoing); + memset (&raw, 0, sizeof raw); + outgoing.raw = &raw; + + /* If we didn't get a known vendor magic number on the way in, + just copy the input options to the output. */ + if (!packet -> options_valid && + !subnet -> group -> always_reply_rfc1048 && + (!hp || !hp -> group -> always_reply_rfc1048)) { + memcpy (outgoing.raw -> options, + packet -> raw -> options, DHCP_OPTION_LEN); + outgoing.packet_length = BOOTP_MIN_LEN; + } else { + struct tree_cache netmask_tree; /* -- RBF */ + + /* Come up with a list of options that we want to send + to this client. Start with the per-subnet options, + and then override those with client-specific + options. */ + + memcpy (options, subnet -> group -> options, sizeof options); + + for (i = 0; i < 256; i++) { + if (hp -> group -> options [i]) + options [i] = hp -> group -> options [i]; + } + + /* Use the subnet mask from the subnet declaration if no other + mask has been provided. */ + if (!options [DHO_SUBNET_MASK]) { + options [DHO_SUBNET_MASK] = &netmask_tree; + netmask_tree.flags = TC_TEMPORARY; + netmask_tree.value = lease -> subnet -> netmask.iabuf; + netmask_tree.len = lease -> subnet -> netmask.len; + netmask_tree.buf_size = lease -> subnet -> netmask.len; + netmask_tree.timeout = 0xFFFFFFFF; + netmask_tree.tree = (struct tree *)0; + } + + /* Pack the options into the buffer. Unlike DHCP, we + can't pack options into the filename and server + name buffers. */ + + outgoing.packet_length = + cons_options (packet, outgoing.raw, + 0, options, 0, 0, 1, (u_int8_t *)0, 0); + if (outgoing.packet_length < BOOTP_MIN_LEN) + outgoing.packet_length = BOOTP_MIN_LEN; + } + + /* Take the fields that we care about... */ + raw.op = BOOTREPLY; + raw.htype = packet -> raw -> htype; + raw.hlen = packet -> raw -> hlen; + memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr); + raw.hops = packet -> raw -> hops; + raw.xid = packet -> raw -> xid; + raw.secs = packet -> raw -> secs; + raw.flags = packet -> raw -> flags; + raw.ciaddr = packet -> raw -> ciaddr; + memcpy (&raw.yiaddr, ip_address.iabuf, sizeof raw.yiaddr); + + /* Figure out the address of the next server. */ + if (hp && hp -> group -> next_server.len) + memcpy (&raw.siaddr, hp -> group -> next_server.iabuf, 4); + else if (subnet -> group -> next_server.len) + memcpy (&raw.siaddr, subnet -> group -> next_server.iabuf, 4); + else if (subnet -> interface_address.len) + memcpy (&raw.siaddr, subnet -> interface_address.iabuf, 4); + else + raw.siaddr = packet -> interface -> primary_address; + + raw.giaddr = packet -> raw -> giaddr; + if (hp -> group -> server_name) + strncpy (raw.sname, hp -> group -> server_name, + (sizeof raw.sname)); + else if (subnet -> group -> server_name) + strncpy (raw.sname, subnet -> group -> server_name, + (sizeof raw.sname)); + + if (hp -> group -> filename) + strncpy (raw.file, hp -> group -> filename, + (sizeof raw.file)); + else if (subnet -> group -> filename) + strncpy (raw.file, subnet -> group -> filename, + (sizeof raw.file)); + else + memcpy (raw.file, packet -> raw -> file, sizeof raw.file); + + /* Set up the hardware destination address... */ + hto.htype = packet -> raw -> htype; + hto.hlen = packet -> raw -> hlen; + memcpy (hto.haddr, packet -> raw -> chaddr, hto.hlen); + + from = packet -> interface -> primary_address; + + /* Report what we're doing... */ + note ("BOOTREPLY for %s to %s (%s) via %s", + piaddr (ip_address), hp -> name, + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + packet -> raw -> giaddr.s_addr + ? inet_ntoa (packet -> raw -> giaddr) + : packet -> interface -> name); + + /* Set up the parts of the address that are in common. */ + memset (&to, 0, sizeof to); + to.sin_family = AF_INET; +#ifdef HAVE_SA_LEN + to.sin_len = sizeof to; +#endif + + /* If this was gatewayed, send it back to the gateway... */ + if (raw.giaddr.s_addr) { + to.sin_addr = raw.giaddr; + to.sin_port = local_port; + + if (fallback_interface) { + result = send_packet (fallback_interface, + (struct packet *)0, + &raw, outgoing.packet_length, + from, &to, &hto); + return; + } + + /* If it comes from a client that already knows its address + and is not requesting a broadcast response, and we can + unicast to a client without using the ARP protocol, sent it + directly to that client. */ + } else if (!(raw.flags & htons (BOOTP_BROADCAST))) { + to.sin_addr = raw.yiaddr; + to.sin_port = remote_port; + + /* Otherwise, broadcast it on the local network. */ + } else { + to.sin_addr.s_addr = INADDR_BROADCAST; + to.sin_port = remote_port; /* XXX */ + } + + errno = 0; + result = send_packet (packet -> interface, + packet, &raw, outgoing.packet_length, + from, &to, &hto); +} |