/* 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 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)) && can_unicast_without_arp ()) { 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); }