BOOTP Protocol support. */
/*
- * Copyright (c) 1995, 1996 The Internet Software Consortium.
- * All rights reserved.
+ * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
- * 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.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
- * 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.
+ * Internet Systems Consortium, Inc.
+ * PO Box 360
+ * Newmarket, NH 03857 USA
+ * <info@isc.org>
+ * https://www.isc.org/
*
- * 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''.
*/
-#ifndef lint
-static char copyright[] =
-"@(#) Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n";
-#endif /* not lint */
-
#include "dhcpd.h"
+#include <errno.h>
+
+#if defined (TRACING)
+# define send_packet trace_packet_send
+#endif
void bootp (packet)
struct packet *packet;
{
int result;
- struct host_decl *hp;
+ struct host_decl *hp = (struct host_decl *)0;
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;
-
- note ("BOOTREQUEST from %s", print_hw_addr (packet -> raw -> htype,
- packet -> raw -> hlen,
- packet -> raw -> chaddr));
-
- locate_network (packet);
-
- hp = find_hosts_by_haddr (packet -> raw -> htype,
- packet -> raw -> chaddr,
- packet -> raw -> hlen);
-
- lease = find_lease (packet);
-
- /* Find an IP address in the host_decl that matches the
- specified network. */
- if (hp && packet -> shared_network)
- 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
+ struct option_state *options = (struct option_state *)0;
+ struct lease *lease = (struct lease *)0;
+ unsigned i;
+ struct data_string d1;
+ struct option_cache *oc;
+ char msgbuf [1024];
+ int ignorep;
+ int peer_has_leases = 0;
+
+ if (packet -> raw -> op != BOOTREQUEST)
+ return;
+
+ /* %Audit% This is log output. %2004.06.17,Safe%
+ * If we truncate we hope the user can get a hint from the log.
+ */
+ snprintf (msgbuf, sizeof msgbuf, "BOOTREQUEST from %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);
+
+ if (!locate_network (packet)) {
+ log_info ("%s: network unknown", msgbuf);
+ return;
+ }
+
+ find_lease (&lease, packet, packet -> shared_network,
+ 0, 0, (struct lease *)0, MDL);
+
+ if (lease && lease->host)
+ host_reference(&hp, lease->host, MDL);
+
+ if (!lease || ((lease->flags & STATIC_LEASE) == 0)) {
+ struct host_decl *h;
+
+ /* We didn't find an applicable fixed-address 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;
- }
+
+ if (!hp)
+ find_hosts_by_haddr(&hp, packet->raw->htype,
+ packet->raw->chaddr,
+ packet->raw->hlen, MDL);
+
+ for (h = hp; h; h = h -> n_ipaddr) {
+ if (!h -> fixed_addr) {
+ host_reference(&host, h, MDL);
+ break;
}
}
- /* 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 &&
- packet -> shared_network -> dynamic_bootp)) {
- lose:
- note ("No applicable record for BOOTP host %s",
- print_hw_addr (packet -> raw -> htype,
- packet -> raw -> hlen,
- packet -> raw -> chaddr));
- return;
+ if (hp)
+ host_dereference(&hp, MDL);
+
+ if (host) {
+ host_reference(&hp, host, MDL);
+ host_dereference(&host, MDL);
}
- /* If a lease has already been assigned through dynamic
- BOOTP, and it's still okay to use dynamic bootp on
- that lease, reassign it. */
- if (lease) {
- if ((lease -> flags & BOOTP_LEASE) &&
- (lease -> flags & DYNAMIC_BOOTP_OK)) {
- lease -> host = host;
- ack_lease (packet, lease, 0, 0);
- return;
- }
- /* If the lease was acquired with DHCP, or
- if dynamic BOOTP is no longer allowed for
- this lease, set it free. */
- release_lease (lease);
+ /* Allocate a lease if we have not yet found one. */
+ if (!lease)
+ allocate_lease (&lease, packet,
+ packet -> shared_network -> pools,
+ &peer_has_leases);
+
+ if (lease == NULL) {
+ log_info("%s: BOOTP from dynamic client and no "
+ "dynamic leases", msgbuf);
+ goto out;
}
- /* At this point, if we don't know the network from which
- the packet came, lose it. */
- if (!packet -> shared_network)
- goto lose;
-
- /* 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;
+#if defined(FAILOVER_PROTOCOL)
+ if ((lease->pool != NULL) &&
+ (lease->pool->failover_peer != NULL)) {
+ dhcp_failover_state_t *peer;
+
+ peer = lease->pool->failover_peer;
+
+ /* If we are in a failover state that bars us from
+ * answering, do not do so.
+ * If we are in a cooperative state, load balance
+ * (all) responses.
+ */
+ if ((peer->service_state == not_responding) ||
+ (peer->service_state == service_startup)) {
+ log_info("%s: not responding%s",
+ msgbuf, peer->nrr);
+ goto out;
+ } else if((peer->service_state == cooperating) &&
+ !load_balance_mine(packet, peer)) {
+ log_info("%s: load balance to peer %s",
+ msgbuf, peer->name);
+ goto out;
}
}
- goto lose;
+#endif
+
+ ack_lease (packet, lease, 0, 0, msgbuf, 0, hp);
+ goto out;
}
- /* If we don't have a fixed address for it, drop it. */
- if (!subnet) {
- note ("No fixed address for BOOTP host %s (%s)",
- print_hw_addr (packet -> raw -> htype,
- packet -> raw -> hlen,
- packet -> raw -> chaddr),
- hp -> name);
- return;
+ /* Run the executable statements to compute the client and server
+ options. */
+ option_state_allocate (&options, MDL);
+
+ /* Execute the subnet statements. */
+ execute_statements_in_scope (NULL, packet, lease, NULL,
+ packet->options, options,
+ &lease->scope, lease->subnet->group,
+ NULL, NULL);
+
+ /* Execute statements from class scopes. */
+ for (i = packet -> class_count; i > 0; i--) {
+ execute_statements_in_scope(NULL, packet, lease, NULL,
+ packet->options, options,
+ &lease->scope,
+ packet->classes[i - 1]->group,
+ lease->subnet->group, NULL);
+ }
+
+ /* Execute the host statements. */
+ if (hp != NULL) {
+ execute_statements_in_scope(NULL, packet, lease, NULL,
+ packet->options, options,
+ &lease->scope, hp->group,
+ lease->subnet->group, NULL);
+ }
+
+ /* Drop the request if it's not allowed for this client. */
+ if ((oc = lookup_option (&server_universe, options, SV_ALLOW_BOOTP)) &&
+ !evaluate_boolean_option_cache(&ignorep, packet, lease,
+ NULL,
+ packet->options, options,
+ &lease->scope, oc, MDL)) {
+ if (!ignorep)
+ log_info ("%s: bootp disallowed", msgbuf);
+ goto out;
+ }
+
+ if ((oc = lookup_option(&server_universe,
+ options, SV_ALLOW_BOOTING)) &&
+ !evaluate_boolean_option_cache(&ignorep, packet, lease,
+ NULL,
+ packet->options, options,
+ &lease->scope, oc, MDL)) {
+ if (!ignorep)
+ log_info ("%s: booting disallowed", msgbuf);
+ goto out;
}
/* Set up the outgoing packet... */
memset (&raw, 0, sizeof raw);
outgoing.raw = &raw;
- /* 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. */
+ /* If we didn't get a known vendor magic number on the way in,
+ just copy the input options to the output. */
+ i = SV_ALWAYS_REPLY_RFC1048;
+ if (!packet->options_valid &&
+ !(evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
+ packet->options, options,
+ &lease->scope,
+ lookup_option (&server_universe,
+ options, i), MDL))) {
+ if (packet->packet_length > DHCP_FIXED_NON_UDP) {
+ memcpy(outgoing.raw->options, packet->raw->options,
+ packet->packet_length - DHCP_FIXED_NON_UDP);
+ }
+
+ outgoing.packet_length =
+ (packet->packet_length < BOOTP_MIN_LEN)
+ ? BOOTP_MIN_LEN
+ : packet->packet_length;
+ } else {
- memcpy (options, subnet -> options, sizeof options);
+ /* Use the subnet mask from the subnet declaration if no other
+ mask has been provided. */
+ oc = (struct option_cache *)0;
+ i = DHO_SUBNET_MASK;
+ if (!lookup_option (&dhcp_universe, options, i)) {
+ if (option_cache_allocate (&oc, MDL)) {
+ if (make_const_data
+ (&oc -> expression,
+ lease -> subnet -> netmask.iabuf,
+ lease -> subnet -> netmask.len,
+ 0, 0, MDL)) {
+ option_code_hash_lookup(&oc->option,
+ dhcp_universe.code_hash,
+ &i, 0, MDL);
+ save_option (&dhcp_universe,
+ options, oc);
+ }
+ option_cache_dereference (&oc, MDL);
+ }
+ }
- for (i = 0; i < 256; i++) {
- if (hp -> options [i])
- options [i] = hp -> options [i];
- }
+ /* If use-host-decl-names is enabled and there is a hostname
+ * defined in the host delcartion, send it back in hostname
+ * option */
+ use_host_decl_name(packet, lease, options);
- /* Pack the options into the buffer. Unlike DHCP, we can't
- pack options into the filename and server name buffers. */
+ /* Pack the options into the buffer. Unlike DHCP, we
+ can't pack options into the filename and server
+ name buffers. */
- cons_options (packet, &outgoing, options, 0);
-
+ outgoing.packet_length =
+ cons_options (packet, outgoing.raw, lease,
+ (struct client_state *)0, 0,
+ packet -> options, options,
+ &lease -> scope,
+ 0, 0, 1, (struct data_string *)0,
+ (const char *)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, raw.hlen);
- memset (&raw.chaddr [raw.hlen], 0,
- (sizeof raw.chaddr) - 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 = 0;
+ raw.flags = packet -> raw -> flags;
raw.ciaddr = packet -> raw -> ciaddr;
- memcpy (&raw.yiaddr, ip_address.iabuf, sizeof raw.yiaddr);
- if (subnet -> interface_address.len)
- memcpy (&raw.siaddr, subnet -> interface_address.iabuf, 4);
- else
- memcpy (&raw.siaddr, server_identifier.iabuf, 4);
+ /* yiaddr is an ipv4 address, it must be 4 octets. */
+ memcpy (&raw.yiaddr, lease->ip_addr.iabuf, 4);
+
+ /* If we're always supposed to broadcast to this client, set
+ the broadcast bit in the bootp flags field. */
+ if ((oc = lookup_option (&server_universe,
+ options, SV_ALWAYS_BROADCAST)) &&
+ evaluate_boolean_option_cache (&ignorep, packet, lease,
+ (struct client_state *)0,
+ packet -> options, options,
+ &lease -> scope, oc, MDL))
+ raw.flags |= htons (BOOTP_BROADCAST);
+
+ /* Figure out the address of the next server. */
+ memset (&d1, 0, sizeof d1);
+ oc = lookup_option (&server_universe, options, SV_NEXT_SERVER);
+ if (oc &&
+ evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options, options,
+ &lease -> scope, oc, MDL)) {
+ /* If there was more than one answer, take the first. */
+ if (d1.len >= 4 && d1.data)
+ memcpy (&raw.siaddr, d1.data, 4);
+ data_string_forget (&d1, MDL);
+ } else {
+ if ((lease->subnet->shared_network->interface != NULL) &&
+ lease->subnet->shared_network->interface->address_count)
+ raw.siaddr =
+ lease->subnet->shared_network->interface->addresses[0];
+ else if (packet->interface->address_count)
+ raw.siaddr = packet->interface->addresses[0];
+ }
raw.giaddr = packet -> raw -> giaddr;
- if (hp -> server_name) {
- strncpy (raw.sname, hp -> server_name,
- (sizeof raw.sname) - 1);
- raw.sname [(sizeof raw.sname) - 1] = 0;
+
+ /* Figure out the filename. */
+ oc = lookup_option (&server_universe, options, SV_FILENAME);
+ if (oc &&
+ evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options, options,
+ &lease -> scope, oc, MDL)) {
+ memcpy (raw.file, d1.data,
+ d1.len > sizeof raw.file ? sizeof raw.file : d1.len);
+ if (sizeof raw.file > d1.len)
+ memset (&raw.file [d1.len],
+ 0, (sizeof raw.file) - d1.len);
+ data_string_forget (&d1, MDL);
+ } else
+ memcpy (raw.file, packet -> raw -> file, sizeof raw.file);
+
+ /* Choose a server name as above. */
+ oc = lookup_option (&server_universe, options, SV_SERVER_NAME);
+ if (oc &&
+ evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options, options,
+ &lease -> scope, oc, MDL)) {
+ memcpy (raw.sname, d1.data,
+ d1.len > sizeof raw.sname ? sizeof raw.sname : d1.len);
+ if (sizeof raw.sname > d1.len)
+ memset (&raw.sname [d1.len],
+ 0, (sizeof raw.sname) - d1.len);
+ data_string_forget (&d1, MDL);
}
- if (hp -> filename) {
- strncpy (raw.file, hp -> filename,
- (sizeof raw.file) - 1);
- raw.file [(sizeof raw.file) - 1] = 0;
+
+ /* Execute the commit statements, if there are any. */
+ execute_statements (NULL, packet, lease, NULL, packet->options,
+ options, &lease->scope, lease->on_star.on_commit,
+ NULL);
+
+ /* We're done with the option state. */
+ option_state_dereference (&options, MDL);
+
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ /* Report what we're doing... */
+ log_info("%s", msgbuf);
+ log_info("DHCP4o6 BOOTREPLY for %s to %s (%s) via %s",
+ piaddr(lease->ip_addr),
+ ((hp != NULL) && (hp->name != NULL)) ?
+ hp -> name : "unknown",
+ print_hw_addr (packet->raw->htype,
+ packet->raw->hlen,
+ packet->raw->chaddr),
+ piaddr(packet->client_addr));
+
+ /* fill dhcp4o6_response */
+ packet->dhcp4o6_response->len = outgoing.packet_length;
+ packet->dhcp4o6_response->buffer = NULL;
+ if (!buffer_allocate(&packet->dhcp4o6_response->buffer,
+ outgoing.packet_length, MDL)) {
+ log_fatal("No memory to store DHCP4o6 reply.");
+ }
+ packet->dhcp4o6_response->data =
+ packet->dhcp4o6_response->buffer->data;
+ memcpy(packet->dhcp4o6_response->buffer->data,
+ outgoing.raw, outgoing.packet_length);
+ goto out;
}
+#endif
/* Set up the hardware destination address... */
- hto.htype = packet -> raw -> htype;
- hto.hlen = packet -> raw -> hlen;
- memcpy (hto.haddr, packet -> raw -> chaddr, hto.hlen);
+ hto.hbuf [0] = packet -> raw -> htype;
+ hto.hlen = packet -> raw -> hlen + 1;
+ memcpy (&hto.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen);
+
+ if (packet->interface->address_count) {
+ from = packet->interface->addresses[0];
+ } else {
+ log_error("%s: Interface %s appears to have no IPv4 "
+ "addresses, and so dhcpd cannot select a source "
+ "address.", msgbuf, packet->interface->name);
+ goto out;
+ }
/* Report what we're doing... */
- note ("BOOTREPLY for %s to %s",
- inet_ntoa (raw.yiaddr),
- print_hw_addr (packet -> raw -> htype,
- packet -> raw -> hlen,
- packet -> raw -> chaddr));
+ log_info("%s", msgbuf);
+ log_info("BOOTREPLY for %s to %s (%s) via %s",
+ piaddr(lease->ip_addr),
+ ((hp != NULL) && (hp->name != NULL)) ? hp -> name : "unknown",
+ 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. */
to.sin_family = AF_INET;
/* If this was gatewayed, send it back to the gateway... */
if (raw.giaddr.s_addr) {
to.sin_addr = raw.giaddr;
- to.sin_port = server_port;
-
-#ifdef USE_FALLBACK
- result = send_fallback (&fallback_interface,
- (struct packet *)0,
- &raw, outgoing.packet_length,
- raw.siaddr, &to, &hto);
- if (result < 0)
- warn ("send_fallback: %m");
- return;
-#endif
+ to.sin_port = local_port;
+
+ if (fallback_interface) {
+ result = send_packet (fallback_interface, NULL, &raw,
+ outgoing.packet_length, from,
+ &to, &hto);
+ if (result < 0) {
+ log_error ("%s:%d: Failed to send %d byte long "
+ "packet over %s interface.", MDL,
+ outgoing.packet_length,
+ fallback_interface->name);
+ }
+
+ goto out;
+ }
+
+ /* 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 (packet -> interface)) {
+ 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 = htons (ntohs (server_port) + 1); /* XXX */
+ to.sin_addr = limited_broadcast;
+ to.sin_port = remote_port; /* XXX */
}
errno = 0;
- result = send_packet (packet -> interface,
- packet, &raw, outgoing.packet_length,
- raw.siaddr, &to, &hto);
- if (result < 0)
- warn ("send_packet: %m");
+ result = send_packet(packet->interface, packet, &raw,
+ outgoing.packet_length, from, &to, &hto);
+ if (result < 0) {
+ log_error ("%s:%d: Failed to send %d byte long packet over %s"
+ " interface.", MDL, outgoing.packet_length,
+ packet->interface->name);
+ }
+
+ out:
+
+ if (options)
+ option_state_dereference (&options, MDL);
+ if (lease)
+ lease_dereference (&lease, MDL);
+ if (hp)
+ host_dereference (&hp, MDL);
+ if (host)
+ host_dereference (&host, MDL);
}