xref: /openbsd-src/usr.sbin/dhcpd/dhcp.c (revision 8415e52b1598f273c11974fc6d2b8ce98710b063)
1*8415e52bSreyk /*	$OpenBSD: dhcp.c,v 1.57 2017/07/11 10:28:24 reyk Exp $ */
2e853bc5dShenning 
3e853bc5dShenning /*
4e853bc5dShenning  * Copyright (c) 1995, 1996, 1997, 1998, 1999
5e853bc5dShenning  * The Internet Software Consortium.   All rights reserved.
6e853bc5dShenning  *
7e853bc5dShenning  * Redistribution and use in source and binary forms, with or without
8e853bc5dShenning  * modification, are permitted provided that the following conditions
9e853bc5dShenning  * are met:
10e853bc5dShenning  *
11e853bc5dShenning  * 1. Redistributions of source code must retain the above copyright
12e853bc5dShenning  *    notice, this list of conditions and the following disclaimer.
13e853bc5dShenning  * 2. Redistributions in binary form must reproduce the above copyright
14e853bc5dShenning  *    notice, this list of conditions and the following disclaimer in the
15e853bc5dShenning  *    documentation and/or other materials provided with the distribution.
16e853bc5dShenning  * 3. Neither the name of The Internet Software Consortium nor the names
17e853bc5dShenning  *    of its contributors may be used to endorse or promote products derived
18e853bc5dShenning  *    from this software without specific prior written permission.
19e853bc5dShenning  *
20e853bc5dShenning  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
21e853bc5dShenning  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
22e853bc5dShenning  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23e853bc5dShenning  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24e853bc5dShenning  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
25e853bc5dShenning  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26e853bc5dShenning  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27e853bc5dShenning  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28e853bc5dShenning  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29e853bc5dShenning  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30e853bc5dShenning  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31e853bc5dShenning  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32e853bc5dShenning  * SUCH DAMAGE.
33e853bc5dShenning  *
34e853bc5dShenning  * This software has been written for the Internet Software Consortium
35e853bc5dShenning  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
36e853bc5dShenning  * Enterprises.  To learn more about the Internet Software Consortium,
37e853bc5dShenning  * see ``http://www.vix.com/isc''.  To learn more about Vixie
38e853bc5dShenning  * Enterprises, see ``http://www.vix.com''.
39e853bc5dShenning  */
40e853bc5dShenning 
41837cddffSkrw #include <sys/types.h>
42837cddffSkrw #include <sys/socket.h>
43837cddffSkrw 
44837cddffSkrw #include <arpa/inet.h>
45837cddffSkrw 
46837cddffSkrw #include <net/if.h>
47837cddffSkrw 
48837cddffSkrw #include <netinet/in.h>
49837cddffSkrw 
50837cddffSkrw #include <errno.h>
51837cddffSkrw #include <stdio.h>
52837cddffSkrw #include <stdlib.h>
53837cddffSkrw #include <string.h>
54837cddffSkrw 
55837cddffSkrw #include "dhcp.h"
56837cddffSkrw #include "tree.h"
57e853bc5dShenning #include "dhcpd.h"
58c525a185Skrw #include "log.h"
595f515bebSbeck #include "sync.h"
60e853bc5dShenning 
61e853bc5dShenning int outstanding_pings;
62e853bc5dShenning 
63e853bc5dShenning static char dhcp_message[256];
64e853bc5dShenning 
65ea507cabSderaadt void
dhcp(struct packet * packet,int is_udpsock)6694bf53e6Skrw dhcp(struct packet *packet, int is_udpsock)
67e853bc5dShenning {
68e853bc5dShenning 	if (!locate_network(packet) && packet->packet_type != DHCPREQUEST)
69e853bc5dShenning 		return;
70e853bc5dShenning 
7194bf53e6Skrw 	if (is_udpsock && packet->packet_type != DHCPINFORM) {
72c525a185Skrw 		log_info("Unable to handle a DHCP message type=%d on UDP "
7394bf53e6Skrw 		    "socket", packet->packet_type);
7494bf53e6Skrw 		return;
7594bf53e6Skrw 	}
7694bf53e6Skrw 
77e853bc5dShenning 	switch (packet->packet_type) {
78e853bc5dShenning 	case DHCPDISCOVER:
79e853bc5dShenning 		dhcpdiscover(packet);
80e853bc5dShenning 		break;
81e853bc5dShenning 
82e853bc5dShenning 	case DHCPREQUEST:
83e853bc5dShenning 		dhcprequest(packet);
84e853bc5dShenning 		break;
85e853bc5dShenning 
86e853bc5dShenning 	case DHCPRELEASE:
87e853bc5dShenning 		dhcprelease(packet);
88e853bc5dShenning 		break;
89e853bc5dShenning 
90e853bc5dShenning 	case DHCPDECLINE:
91e853bc5dShenning 		dhcpdecline(packet);
92e853bc5dShenning 		break;
93e853bc5dShenning 
94e853bc5dShenning 	case DHCPINFORM:
95e853bc5dShenning 		dhcpinform(packet);
96e853bc5dShenning 		break;
97e853bc5dShenning 
98e853bc5dShenning 	default:
99e853bc5dShenning 		break;
100e853bc5dShenning 	}
101e853bc5dShenning }
102e853bc5dShenning 
1038e1c2028Sderaadt void
dhcpdiscover(struct packet * packet)1048e1c2028Sderaadt dhcpdiscover(struct packet *packet)
105e853bc5dShenning {
106e853bc5dShenning 	struct lease *lease = find_lease(packet, packet->shared_network, 0);
107e853bc5dShenning 	struct host_decl *hp;
108e853bc5dShenning 
109c525a185Skrw 	log_info("DHCPDISCOVER from %s via %s",
1108e1c2028Sderaadt 	    print_hw_addr(packet->raw->htype, packet->raw->hlen,
111e853bc5dShenning 	    packet->raw->chaddr),
1128e1c2028Sderaadt 	    packet->raw->giaddr.s_addr ? inet_ntoa(packet->raw->giaddr) :
1138e1c2028Sderaadt 	    packet->interface->name);
114e853bc5dShenning 
115e853bc5dShenning 	/* Sourceless packets don't make sense here. */
116e853bc5dShenning 	if (!packet->shared_network) {
117c525a185Skrw 		log_info("Packet from unknown subnet: %s",
118e853bc5dShenning 		    inet_ntoa(packet->raw->giaddr));
119e853bc5dShenning 		return;
120e853bc5dShenning 	}
121e853bc5dShenning 
122e853bc5dShenning 	/* If we didn't find a lease, try to allocate one... */
123e853bc5dShenning 	if (!lease) {
124e853bc5dShenning 		lease = packet->shared_network->last_lease;
125e853bc5dShenning 
1268e1c2028Sderaadt 		/*
1278e1c2028Sderaadt 		 * If there are no leases in that subnet that have
1288e1c2028Sderaadt 		 * expired, we have nothing to offer this client.
1298e1c2028Sderaadt 		 */
130e853bc5dShenning 		if (!lease || lease->ends > cur_time) {
131c525a185Skrw 			log_info("no free leases on subnet %s",
132e853bc5dShenning 			    packet->shared_network->name);
133e853bc5dShenning 			return;
134e853bc5dShenning 		}
135e853bc5dShenning 
1368e1c2028Sderaadt 		/*
1378e1c2028Sderaadt 		 * If we find an abandoned lease, take it, but print a
1388e1c2028Sderaadt 		 * warning message, so that if it continues to lose,
1398e1c2028Sderaadt 		 * the administrator will eventually investigate.
1408e1c2028Sderaadt 		 */
141e853bc5dShenning 		if ((lease->flags & ABANDONED_LEASE)) {
142e853bc5dShenning 			struct lease *lp;
143e853bc5dShenning 
144e853bc5dShenning 			/* See if we can find an unabandoned lease first. */
145e853bc5dShenning 			for (lp = lease; lp; lp = lp->prev) {
146e853bc5dShenning 				if (lp->ends > cur_time)
147e853bc5dShenning 					break;
148e853bc5dShenning 				if (!(lp->flags & ABANDONED_LEASE)) {
149e853bc5dShenning 					lease = lp;
150e853bc5dShenning 					break;
151e853bc5dShenning 				}
152e853bc5dShenning 			}
153e853bc5dShenning 
1548e1c2028Sderaadt 			/*
1558e1c2028Sderaadt 			 * If we can't find an unabandoned lease,
1568e1c2028Sderaadt 			 * reclaim the abandoned lease.
1578e1c2028Sderaadt 			 */
158e853bc5dShenning 			if ((lease->flags & ABANDONED_LEASE)) {
159c525a185Skrw 				log_warnx("Reclaiming abandoned IP address %s.",
160e853bc5dShenning 				    piaddr(lease->ip_addr));
161e853bc5dShenning 				lease->flags &= ~ABANDONED_LEASE;
1626f4dfa88Sckuethe 
163dfafa184Sckuethe 				pfmsg('L', lease); /* unabandon address */
164e853bc5dShenning 			}
165e853bc5dShenning 		}
166e853bc5dShenning 
167e853bc5dShenning 		/* Try to find a host_decl that matches the client
168e853bc5dShenning 		   identifier or hardware address on the packet, and
169e853bc5dShenning 		   has no fixed IP address.   If there is one, hang
170e853bc5dShenning 		   it off the lease so that its option definitions
171e853bc5dShenning 		   can be used. */
1728e1c2028Sderaadt 		if (((packet->options[DHO_DHCP_CLIENT_IDENTIFIER].len != 0) &&
1738e1c2028Sderaadt 		    ((hp = find_hosts_by_uid(
1748e1c2028Sderaadt 		    packet->options[DHO_DHCP_CLIENT_IDENTIFIER].data,
17535318e8fSkrw 		    packet->options[DHO_DHCP_CLIENT_IDENTIFIER].len)) !=
17635318e8fSkrw 		    NULL)) ||
177e853bc5dShenning 		    ((hp = find_hosts_by_haddr(packet->raw->htype,
1788e1c2028Sderaadt 		    packet->raw->chaddr, packet->raw->hlen)) != NULL)) {
179e853bc5dShenning 			for (; hp; hp = hp->n_ipaddr) {
180e853bc5dShenning 				if (!hp->fixed_addr) {
181e853bc5dShenning 					lease->host = hp;
182e853bc5dShenning 					break;
183e853bc5dShenning 				}
184e853bc5dShenning 			}
1858e1c2028Sderaadt 		} else
1868e1c2028Sderaadt 			lease->host = NULL;
187e853bc5dShenning 	}
188e853bc5dShenning 
189e853bc5dShenning 	/* If this subnet won't boot unknown clients, ignore the
190e853bc5dShenning 	   request. */
191e853bc5dShenning 	if (!lease->host &&
192e853bc5dShenning 	    !lease->subnet->group->boot_unknown_clients) {
193c525a185Skrw 		log_info("Ignoring unknown client %s",
1948e1c2028Sderaadt 		    print_hw_addr(packet->raw->htype, packet->raw->hlen,
195e853bc5dShenning 		    packet->raw->chaddr));
1968e1c2028Sderaadt 	} else if (lease->host && !lease->host->group->allow_booting) {
197c525a185Skrw 		log_info("Declining to boot client %s",
1988e1c2028Sderaadt 		    lease->host->name ? lease->host->name :
1998e1c2028Sderaadt 		    print_hw_addr(packet->raw->htype, packet->raw->hlen,
200e853bc5dShenning 		    packet->raw->chaddr));
201e853bc5dShenning 	} else
202e853bc5dShenning 		ack_lease(packet, lease, DHCPOFFER, cur_time + 120);
203e853bc5dShenning }
204e853bc5dShenning 
2058e1c2028Sderaadt void
dhcprequest(struct packet * packet)2068e1c2028Sderaadt dhcprequest(struct packet *packet)
207e853bc5dShenning {
208e853bc5dShenning 	struct lease *lease;
209e853bc5dShenning 	struct iaddr cip;
210e853bc5dShenning 	struct subnet *subnet;
211e853bc5dShenning 	int ours = 0;
212e853bc5dShenning 
213e853bc5dShenning 	cip.len = 4;
21441b6b05bStobias 	if (packet->options[DHO_DHCP_REQUESTED_ADDRESS].len == 4)
215e853bc5dShenning 		memcpy(cip.iabuf,
2168e1c2028Sderaadt 		    packet->options[DHO_DHCP_REQUESTED_ADDRESS].data, 4);
2178e1c2028Sderaadt 	else
218e853bc5dShenning 		memcpy(cip.iabuf, &packet->raw->ciaddr.s_addr, 4);
219e853bc5dShenning 	subnet = find_subnet(cip);
220e853bc5dShenning 
2218e1c2028Sderaadt 	/* Find the lease that matches the address requested by the client. */
222e853bc5dShenning 
223e853bc5dShenning 	if (subnet)
224e853bc5dShenning 		lease = find_lease(packet, subnet->shared_network, &ours);
225e853bc5dShenning 	else
226ea507cabSderaadt 		lease = NULL;
227e853bc5dShenning 
228c525a185Skrw 	log_info("DHCPREQUEST for %s from %s via %s", piaddr(cip),
2298e1c2028Sderaadt 	    print_hw_addr(packet->raw->htype, packet->raw->hlen,
230e853bc5dShenning 	    packet->raw->chaddr),
2318e1c2028Sderaadt 	    packet->raw->giaddr.s_addr ? inet_ntoa(packet->raw->giaddr) :
2328e1c2028Sderaadt 	    packet->interface->name);
233e853bc5dShenning 
234e853bc5dShenning 	/* If a client on a given network REQUESTs a lease on an
2358e1c2028Sderaadt 	 * address on a different network, NAK it.  If the Requested
2368e1c2028Sderaadt 	 * Address option was used, the protocol says that it must
2378e1c2028Sderaadt 	 * have been broadcast, so we can trust the source network
2388e1c2028Sderaadt 	 * information.
2398e1c2028Sderaadt 	 *
2408e1c2028Sderaadt 	 * If ciaddr was specified and Requested Address was not, then
2418e1c2028Sderaadt 	 * we really only know for sure what network a packet came from
2428e1c2028Sderaadt 	 * if it came through a BOOTP gateway - if it came through an
2438e1c2028Sderaadt 	 * IP router, we'll just have to assume that it's cool.
2448e1c2028Sderaadt 	 *
2458e1c2028Sderaadt 	 * If we don't think we know where the packet came from, it
2468e1c2028Sderaadt 	 * came through a gateway from an unknown network, so it's not
2478e1c2028Sderaadt 	 * from a RENEWING client.  If we recognize the network it
2488e1c2028Sderaadt 	 * *thinks* it's on, we can NAK it even though we don't
2498e1c2028Sderaadt 	 * recognize the network it's *actually* on; otherwise we just
2508e1c2028Sderaadt 	 * have to ignore it.
2518e1c2028Sderaadt 	 *
2528e1c2028Sderaadt 	 * We don't currently try to take advantage of access to the
2538e1c2028Sderaadt 	 * raw packet, because it's not available on all platforms.
2548e1c2028Sderaadt 	 * So a packet that was unicast to us through a router from a
2558e1c2028Sderaadt 	 * RENEWING client is going to look exactly like a packet that
2568e1c2028Sderaadt 	 * was broadcast to us from an INIT-REBOOT client.
2578e1c2028Sderaadt 	 *
2588e1c2028Sderaadt 	 * Since we can't tell the difference between these two kinds
2598e1c2028Sderaadt 	 * of packets, if the packet appears to have come in off the
2608e1c2028Sderaadt 	 * local wire, we have to treat it as if it's a RENEWING
2618e1c2028Sderaadt 	 * client.  This means that we can't NAK a RENEWING client on
2628e1c2028Sderaadt 	 * the local wire that has a bogus address.  The good news is
2638e1c2028Sderaadt 	 * that we won't ACK it either, so it should revert to INIT
2648e1c2028Sderaadt 	 * state and send us a DHCPDISCOVER, which we *can* work with.
2658e1c2028Sderaadt 	 *
2668e1c2028Sderaadt 	 * Because we can't detect that a RENEWING client is on the
2678e1c2028Sderaadt 	 * wrong wire, it's going to sit there trying to renew until
2688e1c2028Sderaadt 	 * it gets to the REBIND state, when we *can* NAK it because
2698e1c2028Sderaadt 	 * the packet will get to us through a BOOTP gateway.  We
2708e1c2028Sderaadt 	 * shouldn't actually see DHCPREQUEST packets from RENEWING
2718e1c2028Sderaadt 	 * clients on the wrong wire anyway, since their idea of their
2728e1c2028Sderaadt 	 * local router will be wrong.  In any case, the protocol
2738e1c2028Sderaadt 	 * doesn't really allow us to NAK a DHCPREQUEST from a
2748e1c2028Sderaadt 	 * RENEWING client, so we can punt on this issue.
2758e1c2028Sderaadt 	 */
276e853bc5dShenning 	if (!packet->shared_network ||
2778e1c2028Sderaadt 	    (packet->raw->ciaddr.s_addr && packet->raw->giaddr.s_addr) ||
27841b6b05bStobias 	    (packet->options[DHO_DHCP_REQUESTED_ADDRESS].len == 4 &&
279e853bc5dShenning 	    !packet->raw->ciaddr.s_addr)) {
280e853bc5dShenning 
2818e1c2028Sderaadt 		/*
2828e1c2028Sderaadt 		 * If we don't know where it came from but we do know
2838e1c2028Sderaadt 		 * where it claims to have come from, it didn't come
2848e1c2028Sderaadt 		 * from there.   Fry it.
2858e1c2028Sderaadt 		 */
286e853bc5dShenning 		if (!packet->shared_network) {
287e853bc5dShenning 			if (subnet &&
2888e1c2028Sderaadt 			    subnet->shared_network->group->authoritative) {
289e853bc5dShenning 				nak_lease(packet, &cip);
290e853bc5dShenning 				return;
291e853bc5dShenning 			}
292e853bc5dShenning 			/* Otherwise, ignore it. */
293e853bc5dShenning 			return;
294e853bc5dShenning 		}
295e853bc5dShenning 
2968e1c2028Sderaadt 		/*
2978e1c2028Sderaadt 		 * If we do know where it came from and it asked for an
2988e1c2028Sderaadt 		 * address that is not on that shared network, nak it.
2998e1c2028Sderaadt 		 */
300e853bc5dShenning 		subnet = find_grouped_subnet(packet->shared_network, cip);
301e853bc5dShenning 		if (!subnet) {
302e853bc5dShenning 			if (packet->shared_network->group->authoritative)
303e853bc5dShenning 				nak_lease(packet, &cip);
304e853bc5dShenning 			return;
305e853bc5dShenning 		}
306e853bc5dShenning 	}
307e853bc5dShenning 
3088e1c2028Sderaadt 	/*
3098e1c2028Sderaadt 	 * If we found a lease for the client but it's not the one the
3108e1c2028Sderaadt 	 * client asked for, don't send it - some other server probably
3118e1c2028Sderaadt 	 * made the cut.
3128e1c2028Sderaadt 	 */
313e853bc5dShenning 	if (lease && !addr_eq(lease->ip_addr, cip)) {
3148e1c2028Sderaadt 		/*
3158e1c2028Sderaadt 		 * If we found the address the client asked for, but
3168e1c2028Sderaadt 		 * it wasn't what got picked, the lease belongs to us,
3178e1c2028Sderaadt 		 * so we should NAK it.
3188e1c2028Sderaadt 		 */
319e853bc5dShenning 		if (ours)
320e853bc5dShenning 			nak_lease(packet, &cip);
321e853bc5dShenning 		return;
322e853bc5dShenning 	}
323e853bc5dShenning 
3248e1c2028Sderaadt 	/*
3258e1c2028Sderaadt 	 * If the address the client asked for is ours, but it wasn't
3268e1c2028Sderaadt 	 * available for the client, NAK it.
3278e1c2028Sderaadt 	 */
328e853bc5dShenning 	if (!lease && ours) {
329e853bc5dShenning 		nak_lease(packet, &cip);
330e853bc5dShenning 		return;
331e853bc5dShenning 	}
332e853bc5dShenning 
333e853bc5dShenning 	/* If we're not allowed to serve this client anymore, don't. */
3348e1c2028Sderaadt 	if (lease && !lease->host &&
335e853bc5dShenning 	    !lease->subnet->group->boot_unknown_clients) {
336c525a185Skrw 		log_info("Ignoring unknown client %s",
3378e1c2028Sderaadt 		    print_hw_addr(packet->raw->htype, packet->raw->hlen,
338e853bc5dShenning 		    packet->raw->chaddr));
339e853bc5dShenning 		return;
34035318e8fSkrw 	} else if (lease && lease->host && !lease->host->group->allow_booting)
34135318e8fSkrw 		{
342c525a185Skrw 		log_info("Declining to renew client %s",
3438e1c2028Sderaadt 		    lease->host->name ? lease->host->name :
3448e1c2028Sderaadt 		    print_hw_addr(packet->raw->htype, packet->raw->hlen,
345e853bc5dShenning 		    packet->raw->chaddr));
346e853bc5dShenning 		return;
347e853bc5dShenning 	}
348e853bc5dShenning 
3498e1c2028Sderaadt 	/*
3508e1c2028Sderaadt 	 * If we own the lease that the client is asking for,
3518e1c2028Sderaadt 	 * and it's already been assigned to the client, ack it.
3528e1c2028Sderaadt 	 */
353e853bc5dShenning 	if (lease &&
354e853bc5dShenning 	    ((lease->uid_len && lease->uid_len ==
355e853bc5dShenning 	    packet->options[DHO_DHCP_CLIENT_IDENTIFIER].len &&
3568e1c2028Sderaadt 	    !memcmp(packet->options[DHO_DHCP_CLIENT_IDENTIFIER].data,
357e853bc5dShenning 	    lease->uid, lease->uid_len)) ||
358e853bc5dShenning 	    (lease->hardware_addr.hlen == packet->raw->hlen &&
359e853bc5dShenning 	    lease->hardware_addr.htype == packet->raw->htype &&
3608e1c2028Sderaadt 	    !memcmp(lease->hardware_addr.haddr, packet->raw->chaddr,
361e853bc5dShenning 	    packet->raw->hlen)))) {
362e853bc5dShenning 		ack_lease(packet, lease, DHCPACK, 0);
3635f515bebSbeck 		sync_lease(lease);
364e853bc5dShenning 		return;
365e853bc5dShenning 	}
366e853bc5dShenning 
3678e1c2028Sderaadt 	/*
3688e1c2028Sderaadt 	 * At this point, the client has requested a lease, and it's
3698e1c2028Sderaadt 	 * available, but it wasn't assigned to the client, which
3708e1c2028Sderaadt 	 * means that the client probably hasn't gone through the
3718e1c2028Sderaadt 	 * DHCPDISCOVER part of the protocol.  We are within our
3728e1c2028Sderaadt 	 * rights to send a DHCPNAK.   We can also send a DHCPACK.
3738e1c2028Sderaadt 	 * The thing we probably should not do is to remain silent.
3748e1c2028Sderaadt 	 * For now, we'll just assign the lease to the client anyway.
3758e1c2028Sderaadt 	 */
3765f515bebSbeck 	if (lease) {
377e853bc5dShenning 		ack_lease(packet, lease, DHCPACK, 0);
3785f515bebSbeck 		sync_lease(lease);
3795f515bebSbeck 	}
380e853bc5dShenning }
381e853bc5dShenning 
3828e1c2028Sderaadt void
dhcprelease(struct packet * packet)3838e1c2028Sderaadt dhcprelease(struct packet *packet)
384e853bc5dShenning {
385d7503dcfSkrw 	char ciaddrbuf[INET_ADDRSTRLEN];
386e853bc5dShenning 	struct lease *lease;
387e853bc5dShenning 	struct iaddr cip;
388e853bc5dShenning 	int i;
389e853bc5dShenning 
3908e1c2028Sderaadt 	/*
3918e1c2028Sderaadt 	 * DHCPRELEASE must not specify address in requested-address
3928e1c2028Sderaadt 	 * option, but old protocol specs weren't explicit about this,
3938e1c2028Sderaadt 	 * so let it go.
3948e1c2028Sderaadt 	 */
395e853bc5dShenning 	if (packet->options[DHO_DHCP_REQUESTED_ADDRESS].len) {
396c525a185Skrw 		log_info("DHCPRELEASE from %s specified requested-address.",
3978e1c2028Sderaadt 		    print_hw_addr(packet->raw->htype, packet->raw->hlen,
398e853bc5dShenning 		    packet->raw->chaddr));
399e853bc5dShenning 	}
400e853bc5dShenning 
401e853bc5dShenning 	i = DHO_DHCP_CLIENT_IDENTIFIER;
402e853bc5dShenning 	if (packet->options[i].len) {
403e853bc5dShenning 		lease = find_lease_by_uid(packet->options[i].data,
404e853bc5dShenning 		    packet->options[i].len);
405e853bc5dShenning 
4068e1c2028Sderaadt 		/*
4078e1c2028Sderaadt 		 * See if we can find a lease that matches the
4088e1c2028Sderaadt 		 * IP address the client is claiming.
4098e1c2028Sderaadt 		 */
410e853bc5dShenning 		for (; lease; lease = lease->n_uid) {
411e853bc5dShenning 			if (!memcmp(&packet->raw->ciaddr,
412e853bc5dShenning 			    lease->ip_addr.iabuf, 4)) {
413e853bc5dShenning 				break;
414e853bc5dShenning 			}
415e853bc5dShenning 		}
416e853bc5dShenning 	} else {
4178e1c2028Sderaadt 		/*
4188e1c2028Sderaadt 		* The client is supposed to pass a valid client-identifier,
4198e1c2028Sderaadt 		 * but the spec on this has changed historically, so try the
4208e1c2028Sderaadt 		 * IP address in ciaddr if the client-identifier fails.
4218e1c2028Sderaadt 		 */
422e853bc5dShenning 		cip.len = 4;
423e853bc5dShenning 		memcpy(cip.iabuf, &packet->raw->ciaddr, 4);
424e853bc5dShenning 		lease = find_lease_by_ip_addr(cip);
425e853bc5dShenning 	}
426e853bc5dShenning 
427d7503dcfSkrw 	/* Can't do >1 inet_ntoa() in a printf()! */
428d7503dcfSkrw 	strlcpy(ciaddrbuf, inet_ntoa(packet->raw->ciaddr), sizeof(ciaddrbuf));
429d7503dcfSkrw 
430c525a185Skrw 	log_info("DHCPRELEASE of %s from %s via %s (%sfound)",
431d7503dcfSkrw 	    ciaddrbuf,
4328e1c2028Sderaadt 	    print_hw_addr(packet->raw->htype, packet->raw->hlen,
433e853bc5dShenning 	    packet->raw->chaddr),
4348e1c2028Sderaadt 	    packet->raw->giaddr.s_addr ? inet_ntoa(packet->raw->giaddr) :
4358e1c2028Sderaadt 	    packet->interface->name,
436e853bc5dShenning 	    lease ? "" : "not ");
437e853bc5dShenning 
438fbccfd36Sclaudio 	/* If we're already acking this lease, don't do it again. */
439fbccfd36Sclaudio 	if (lease && lease->state) {
440c525a185Skrw 		log_info("DHCPRELEASE already acking lease %s",
441fbccfd36Sclaudio 		    piaddr(lease->ip_addr));
442fbccfd36Sclaudio 		return;
443fbccfd36Sclaudio 	}
444fbccfd36Sclaudio 
445e853bc5dShenning 	/* If we found a lease, release it. */
446e853bc5dShenning 	if (lease && lease->ends > cur_time) {
4478e1c2028Sderaadt 		/*
4488e1c2028Sderaadt 		 * First, we ping this lease to see if it's still
4498e1c2028Sderaadt 		 * there. if it is, we don't release it. This avoids
4508e1c2028Sderaadt 		 * the problem of spoofed releases being used to liberate
4518e1c2028Sderaadt 		 * addresses from the server.
452e853bc5dShenning 		 */
453e853bc5dShenning 		if (!lease->releasing) {
454c525a185Skrw 			log_info("DHCPRELEASE of %s from %s via %s (found)",
455d7503dcfSkrw 			    ciaddrbuf,
456e853bc5dShenning 			    print_hw_addr(packet->raw->htype,
4578e1c2028Sderaadt 			    packet->raw->hlen, packet->raw->chaddr),
4588e1c2028Sderaadt 			    packet->raw->giaddr.s_addr ?
4598e1c2028Sderaadt 			    inet_ntoa(packet->raw->giaddr) :
4608e1c2028Sderaadt 			    packet->interface->name);
461e853bc5dShenning 
462e853bc5dShenning 			lease->releasing = 1;
463e853bc5dShenning 			add_timeout(cur_time + 1, lease_ping_timeout, lease);
464e853bc5dShenning 			icmp_echorequest(&(lease->ip_addr));
465e853bc5dShenning 			++outstanding_pings;
4668e1c2028Sderaadt 		} else {
467c525a185Skrw 			log_info("DHCPRELEASE of %s from %s via %s ignored "
4688e1c2028Sderaadt 			    "(release already pending)",
469d7503dcfSkrw 			    ciaddrbuf,
470e853bc5dShenning 			    print_hw_addr(packet->raw->htype,
4718e1c2028Sderaadt 			    packet->raw->hlen, packet->raw->chaddr),
4728e1c2028Sderaadt 			    packet->raw->giaddr.s_addr ?
4738e1c2028Sderaadt 			    inet_ntoa(packet->raw->giaddr) :
4748e1c2028Sderaadt 			    packet->interface->name);
475e853bc5dShenning 		}
4768e1c2028Sderaadt 	} else {
47735318e8fSkrw 		log_info("DHCPRELEASE of %s from %s via %s for nonexistent "
47835318e8fSkrw 		    "lease", ciaddrbuf, print_hw_addr(packet->raw->htype,
47935318e8fSkrw 		    packet->raw->hlen, packet->raw->chaddr),
4808e1c2028Sderaadt 		    packet->raw->giaddr.s_addr ?
48135318e8fSkrw 		    inet_ntoa(packet->raw->giaddr) : packet->interface->name);
482e853bc5dShenning 	}
483e853bc5dShenning }
484e853bc5dShenning 
4858e1c2028Sderaadt void
dhcpdecline(struct packet * packet)4868e1c2028Sderaadt dhcpdecline(struct packet *packet)
487e853bc5dShenning {
488e853bc5dShenning 	struct lease *lease;
489e853bc5dShenning 	struct iaddr cip;
490e853bc5dShenning 
491e853bc5dShenning 	/* DHCPDECLINE must specify address. */
49241b6b05bStobias 	if (packet->options[DHO_DHCP_REQUESTED_ADDRESS].len != 4)
493e853bc5dShenning 		return;
494e853bc5dShenning 
495e853bc5dShenning 	cip.len = 4;
496e853bc5dShenning 	memcpy(cip.iabuf,
497e853bc5dShenning 	    packet->options[DHO_DHCP_REQUESTED_ADDRESS].data, 4);
498e853bc5dShenning 	lease = find_lease_by_ip_addr(cip);
499e853bc5dShenning 
500c525a185Skrw 	log_info("DHCPDECLINE on %s from %s via %s",
5018e1c2028Sderaadt 	    piaddr(cip), print_hw_addr(packet->raw->htype,
5028e1c2028Sderaadt 	    packet->raw->hlen, packet->raw->chaddr),
5038e1c2028Sderaadt 	    packet->raw->giaddr.s_addr ? inet_ntoa(packet->raw->giaddr) :
5048e1c2028Sderaadt 	    packet->interface->name);
505e853bc5dShenning 
506fbccfd36Sclaudio 	/* If we're already acking this lease, don't do it again. */
507fbccfd36Sclaudio 	if (lease && lease->state) {
508c525a185Skrw 		log_info("DHCPDECLINE already acking lease %s",
509fbccfd36Sclaudio 		    piaddr(lease->ip_addr));
510fbccfd36Sclaudio 		return;
511fbccfd36Sclaudio 	}
512fbccfd36Sclaudio 
513e853bc5dShenning 	/* If we found a lease, mark it as unusable and complain. */
5148e1c2028Sderaadt 	if (lease)
515e853bc5dShenning 		abandon_lease(lease, "declined.");
516e853bc5dShenning }
517e853bc5dShenning 
5188e1c2028Sderaadt void
dhcpinform(struct packet * packet)5198e1c2028Sderaadt dhcpinform(struct packet *packet)
520e853bc5dShenning {
521fc9106f9Syasuoka 	struct lease lease;
52227efec96Smillert 	struct iaddr cip;
52327efec96Smillert 	struct subnet *subnet;
52427efec96Smillert 
52527efec96Smillert 	/*
52627efec96Smillert 	 * ciaddr should be set to client's IP address but
52727efec96Smillert 	 * not all clients are standards compliant.
52827efec96Smillert 	 */
52927efec96Smillert 	cip.len = 4;
530*8415e52bSreyk 	if (packet->raw->ciaddr.s_addr && !packet->raw->giaddr.s_addr) {
531fc9106f9Syasuoka 		if (memcmp(&packet->raw->ciaddr.s_addr,
532fc9106f9Syasuoka 		    packet->client_addr.iabuf, 4) != 0) {
533c525a185Skrw 			log_info("DHCPINFORM from %s but ciaddr %s is not "
5346b03e9c2Skrw 			    "consistent with actual address",
535fc9106f9Syasuoka 			    piaddr(packet->client_addr),
536fc9106f9Syasuoka 			    inet_ntoa(packet->raw->ciaddr));
537fc9106f9Syasuoka 			return;
538fc9106f9Syasuoka 		}
53927efec96Smillert 		memcpy(cip.iabuf, &packet->raw->ciaddr.s_addr, 4);
540fc9106f9Syasuoka 	} else
54127efec96Smillert 		memcpy(cip.iabuf, &packet->client_addr.iabuf, 4);
54227efec96Smillert 
543c525a185Skrw 	log_info("DHCPINFORM from %s", piaddr(cip));
54427efec96Smillert 
54527efec96Smillert 	/* Find the lease that matches the address requested by the client. */
54627efec96Smillert 	subnet = find_subnet(cip);
54727efec96Smillert 	if (!subnet)
54827efec96Smillert 		return;
54927efec96Smillert 
55027efec96Smillert 	/* Sourceless packets don't make sense here. */
55127efec96Smillert 	if (!subnet->shared_network) {
552c525a185Skrw 		log_info("Packet from unknown subnet: %s",
55327efec96Smillert 		    inet_ntoa(packet->raw->giaddr));
55427efec96Smillert 		return;
55527efec96Smillert 	}
55627efec96Smillert 
557fc9106f9Syasuoka 	/* Use a fake lease entry */
558fc9106f9Syasuoka 	memset(&lease, 0, sizeof(lease));
559fc9106f9Syasuoka 	lease.subnet = subnet;
560fc9106f9Syasuoka 	lease.shared_network = subnet->shared_network;
56127efec96Smillert 
562fc9106f9Syasuoka 	if (packet->options[DHO_DHCP_CLIENT_IDENTIFIER].len)
563fc9106f9Syasuoka 		lease.host = find_hosts_by_uid(
564fc9106f9Syasuoka 		    packet->options[DHO_DHCP_CLIENT_IDENTIFIER].data,
565fc9106f9Syasuoka 		    packet->options[DHO_DHCP_CLIENT_IDENTIFIER].len);
566fc9106f9Syasuoka 
567fc9106f9Syasuoka 	lease.starts = lease.timestamp = lease.ends = MIN_TIME;
568fc9106f9Syasuoka 	lease.flags = INFORM_NOLEASE;
569fc9106f9Syasuoka 	ack_lease(packet, &lease, DHCPACK, 0);
570fc9106f9Syasuoka 	if (lease.state != NULL)
571fc9106f9Syasuoka 		free_lease_state(lease.state, "ack_lease");
572e853bc5dShenning }
573e853bc5dShenning 
5748e1c2028Sderaadt void
nak_lease(struct packet * packet,struct iaddr * cip)5758e1c2028Sderaadt nak_lease(struct packet *packet, struct iaddr *cip)
576e853bc5dShenning {
577e853bc5dShenning 	struct sockaddr_in to;
578e853bc5dShenning 	struct in_addr from;
5791a4d4b6bSkrw 	ssize_t result;
5801a4d4b6bSkrw 	int i;
581e853bc5dShenning 	struct dhcp_packet raw;
582e853bc5dShenning 	unsigned char nak = DHCPNAK;
583e853bc5dShenning 	struct packet outgoing;
5842c9b957aSkrw 	struct tree_cache *options[256];
5852c9b957aSkrw 	struct tree_cache dhcpnak_tree, dhcpmsg_tree;
5862c9b957aSkrw 	struct tree_cache client_tree, server_tree;
587e853bc5dShenning 
588e853bc5dShenning 	memset(options, 0, sizeof options);
589e853bc5dShenning 	memset(&outgoing, 0, sizeof outgoing);
590e853bc5dShenning 	memset(&raw, 0, sizeof raw);
591e853bc5dShenning 	outgoing.raw = &raw;
592e853bc5dShenning 
593e853bc5dShenning 	/* Set DHCP_MESSAGE_TYPE to DHCPNAK */
5949e601734Skrw 	i = DHO_DHCP_MESSAGE_TYPE;
5959e601734Skrw 	options[i] = &dhcpnak_tree;
5969e601734Skrw 	options[i]->value = &nak;
5979e601734Skrw 	options[i]->len = sizeof nak;
5989e601734Skrw 	options[i]->buf_size = sizeof nak;
5999e601734Skrw 	options[i]->timeout = -1;
6009e601734Skrw 	options[i]->tree = NULL;
6019e601734Skrw 	options[i]->flags = 0;
602e853bc5dShenning 
603e853bc5dShenning 	/* Set DHCP_MESSAGE to whatever the message is */
6049e601734Skrw 	i = DHO_DHCP_MESSAGE;
6059e601734Skrw 	options[i] = &dhcpmsg_tree;
6069e601734Skrw 	options[i]->value = (unsigned char *)dhcp_message;
6079e601734Skrw 	options[i]->len = strlen(dhcp_message);
6089e601734Skrw 	options[i]->buf_size = strlen(dhcp_message);
6099e601734Skrw 	options[i]->timeout = -1;
6109e601734Skrw 	options[i]->tree = NULL;
6119e601734Skrw 	options[i]->flags = 0;
6122c9b957aSkrw 
6139e20d503Skrw 	/* Include server identifier in the NAK. At least one
6149e20d503Skrw 	 * router vendor depends on it when using dhcp relay proxy mode.
6159e20d503Skrw 	 */
6169e601734Skrw 	i = DHO_DHCP_SERVER_IDENTIFIER;
6179e601734Skrw 	if (packet->options[i].len) {
6189e601734Skrw 		options[i] = &server_tree;
6199e601734Skrw 		options[i]->value = packet->options[i].data,
6209e601734Skrw 		options[i]->len = packet->options[i].len;
6219e601734Skrw 		options[i]->buf_size = packet->options[i].len;
6229e601734Skrw 		options[i]->timeout = -1;
6239e601734Skrw 		options[i]->tree = NULL;
6249e601734Skrw 		options[i]->flags = 0;
6259e20d503Skrw 	}
6269e20d503Skrw 
6272c9b957aSkrw 	/* Echo back the client-identifier as RFC 6842 mandates. */
6282c9b957aSkrw 	i = DHO_DHCP_CLIENT_IDENTIFIER;
6292c9b957aSkrw 	if (packet->options[i].len) {
6302c9b957aSkrw 		options[i] = &client_tree;
6312c9b957aSkrw 		options[i]->value = packet->options[i].data,
6322c9b957aSkrw 		options[i]->len = packet->options[i].len;
6332c9b957aSkrw 		options[i]->buf_size = packet->options[i].len;
6342c9b957aSkrw 		options[i]->timeout = -1;
6352c9b957aSkrw 		options[i]->tree = NULL;
6362c9b957aSkrw 		options[i]->flags = 0;
6372c9b957aSkrw 	}
6382c9b957aSkrw 
639e853bc5dShenning 	/* Do not use the client's requested parameter list. */
640e853bc5dShenning 	i = DHO_DHCP_PARAMETER_REQUEST_LIST;
641e853bc5dShenning 	if (packet->options[i].data) {
642e853bc5dShenning 		packet->options[i].len = 0;
64392b98df2Skrw 		free(packet->options[i].data);
6448e1c2028Sderaadt 		packet->options[i].data = NULL;
645e853bc5dShenning 	}
646e853bc5dShenning 
647e853bc5dShenning 	/* Set up the option buffer... */
6488e1c2028Sderaadt 	outgoing.packet_length = cons_options(packet, outgoing.raw,
6498e1c2028Sderaadt 	    0, options, 0, 0, 0, NULL, 0);
650e853bc5dShenning 
651e853bc5dShenning /*	memset(&raw.ciaddr, 0, sizeof raw.ciaddr);*/
652e853bc5dShenning 	raw.siaddr = packet->interface->primary_address;
653e853bc5dShenning 	raw.giaddr = packet->raw->giaddr;
654e853bc5dShenning 	memcpy(raw.chaddr, packet->raw->chaddr, sizeof raw.chaddr);
655e853bc5dShenning 	raw.hlen = packet->raw->hlen;
656e853bc5dShenning 	raw.htype = packet->raw->htype;
657e853bc5dShenning 	raw.xid = packet->raw->xid;
658e853bc5dShenning 	raw.secs = packet->raw->secs;
659e853bc5dShenning 	raw.flags = packet->raw->flags | htons(BOOTP_BROADCAST);
660e853bc5dShenning 	raw.hops = packet->raw->hops;
661e853bc5dShenning 	raw.op = BOOTREPLY;
662e853bc5dShenning 
663e853bc5dShenning 	/* Report what we're sending... */
664c525a185Skrw 	log_info("DHCPNAK on %s to %s via %s", piaddr(*cip),
665ea507cabSderaadt 	    print_hw_addr(packet->raw->htype, packet->raw->hlen,
666ea507cabSderaadt 	    packet->raw->chaddr), packet->raw->giaddr.s_addr ?
667ea507cabSderaadt 	    inet_ntoa(packet->raw->giaddr) : packet->interface->name);
668e853bc5dShenning 
669e853bc5dShenning 	/* Set up the common stuff... */
670e853bc5dShenning 	memset(&to, 0, sizeof to);
671e853bc5dShenning 	to.sin_family = AF_INET;
672e853bc5dShenning 	to.sin_len = sizeof to;
673e853bc5dShenning 
674e853bc5dShenning 	from = packet->interface->primary_address;
675e853bc5dShenning 
676e853bc5dShenning 	/* Make sure that the packet is at least as big as a BOOTP packet. */
677e853bc5dShenning 	if (outgoing.packet_length < BOOTP_MIN_LEN)
678e853bc5dShenning 		outgoing.packet_length = BOOTP_MIN_LEN;
679e853bc5dShenning 
6808e1c2028Sderaadt 	/*
6818e1c2028Sderaadt 	 * If this was gatewayed, send it back to the gateway.
6828e1c2028Sderaadt 	 * Otherwise, broadcast it on the local network.
6838e1c2028Sderaadt 	 */
684e853bc5dShenning 	if (raw.giaddr.s_addr) {
685e853bc5dShenning 		to.sin_addr = raw.giaddr;
686390956b7Scanacar 		to.sin_port = server_port;
687e853bc5dShenning 
68884d8c049Syasuoka 		result = packet->interface->send_packet(packet->interface, &raw,
689cc3e8043Sclaudio 		    outgoing.packet_length, from, &to, packet->haddr);
690e853bc5dShenning 		if (result == -1)
6910438cf0aSkrw 			log_warn("send_fallback");
692e853bc5dShenning 		return;
693e853bc5dShenning 	} else {
694e853bc5dShenning 		to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
695390956b7Scanacar 		to.sin_port = client_port;
696e853bc5dShenning 	}
697e853bc5dShenning 
698e853bc5dShenning 	errno = 0;
69984d8c049Syasuoka 	result = packet->interface->send_packet(packet->interface, &raw,
7008e1c2028Sderaadt 	    outgoing.packet_length, from, &to, NULL);
701e853bc5dShenning }
702e853bc5dShenning 
7038e1c2028Sderaadt void
ack_lease(struct packet * packet,struct lease * lease,unsigned int offer,time_t when)7048e1c2028Sderaadt ack_lease(struct packet *packet, struct lease *lease, unsigned int offer,
7058e1c2028Sderaadt     time_t when)
706e853bc5dShenning {
707e853bc5dShenning 	struct lease lt;
708e853bc5dShenning 	struct lease_state *state;
7098e1c2028Sderaadt 	time_t lease_time, offered_lease_time, max_lease_time, default_lease_time;
710e853bc5dShenning 	struct class *vendor_class, *user_class;
711a51e9872Skrw 	int ulafdr, echo_client_id, i;
712e853bc5dShenning 
713e853bc5dShenning 	/* If we're already acking this lease, don't do it again. */
714e853bc5dShenning 	if (lease->state) {
715b450f114Sbeck 		if ((lease->flags & STATIC_LEASE) ||
716b450f114Sbeck 		    cur_time - lease->timestamp < 60) {
71735318e8fSkrw 			log_info("already acking lease %s",
71835318e8fSkrw 			    piaddr(lease->ip_addr));
719e853bc5dShenning 			return;
720e853bc5dShenning 		}
721b450f114Sbeck 		free_lease_state(lease->state, "ACK timed out");
722b450f114Sbeck 		lease->state = NULL;
723b450f114Sbeck 	}
724e853bc5dShenning 
7259e601734Skrw 	i = DHO_DHCP_CLASS_IDENTIFIER;
7269e601734Skrw 	if (packet->options[i].len) {
7279e601734Skrw 		vendor_class = find_class(0, packet->options[i].data,
7289e601734Skrw 		    packet->options[i].len);
7298e1c2028Sderaadt 	} else
7308e1c2028Sderaadt 		vendor_class = NULL;
731e853bc5dShenning 
7329e601734Skrw 	i = DHO_DHCP_USER_CLASS_ID;
7339e601734Skrw 	if (packet->options[i].len) {
7349e601734Skrw 		user_class = find_class(1, packet->options[i].data,
7359e601734Skrw 		    packet->options[i].len);
7368e1c2028Sderaadt 	} else
7378e1c2028Sderaadt 		user_class = NULL;
738e853bc5dShenning 
739e853bc5dShenning 	/*
740e853bc5dShenning 	 * If there is not a specific host entry, and either the
741e853bc5dShenning 	 * vendor class or user class (if they exist) deny booting,
742e853bc5dShenning 	 * then bug out.
743e853bc5dShenning 	 */
744e853bc5dShenning 	if (!lease->host) {
745e853bc5dShenning 		if (vendor_class && !vendor_class->group->allow_booting) {
746c525a185Skrw 			log_debug("Booting denied by vendor class");
747e853bc5dShenning 			return;
748e853bc5dShenning 		}
749e853bc5dShenning 
750e853bc5dShenning 		if (user_class && !user_class->group->allow_booting) {
751c525a185Skrw 			log_debug("Booting denied by user class");
752e853bc5dShenning 			return;
753e853bc5dShenning 		}
754e853bc5dShenning 	}
755e853bc5dShenning 
756e853bc5dShenning 	/* Allocate a lease state structure... */
757e853bc5dShenning 	state = new_lease_state("ack_lease");
758e853bc5dShenning 	if (!state)
759c525a185Skrw 		fatalx("unable to allocate lease state!");
760e853bc5dShenning 	memset(state, 0, sizeof *state);
761e853bc5dShenning 	state->got_requested_address = packet->got_requested_address;
762e853bc5dShenning 	state->shared_network = packet->interface->shared_network;
763e853bc5dShenning 
764e853bc5dShenning 	/* Remember if we got a server identifier option. */
7659e601734Skrw 	i = DHO_DHCP_SERVER_IDENTIFIER;
7669e601734Skrw 	if (packet->options[i].len)
767e853bc5dShenning 		state->got_server_identifier = 1;
768e853bc5dShenning 
769e853bc5dShenning 	/* Replace the old lease hostname with the new one, if it's changed. */
7709e601734Skrw 	i = DHO_HOST_NAME;
7719e601734Skrw 	if (packet->options[i].len && lease->client_hostname &&
7729e601734Skrw 	    (strlen(lease->client_hostname) == packet->options[i].len) &&
7739e601734Skrw 	    !memcmp(lease->client_hostname, packet->options[i].data,
7749e601734Skrw 	    packet->options[i].len)) {
7759e601734Skrw 	} else if (packet->options[i].len) {
776e853bc5dShenning 		free(lease->client_hostname);
7779e601734Skrw 		lease->client_hostname = malloc( packet->options[i].len + 1);
778e853bc5dShenning 		if (!lease->client_hostname)
779c525a185Skrw 			fatalx("no memory for client hostname.\n");
7809e601734Skrw 		memcpy(lease->client_hostname, packet->options[i].data,
7819e601734Skrw 		    packet->options[i].len);
7829e601734Skrw 		lease->client_hostname[packet->options[i].len] = 0;
783e853bc5dShenning 	} else if (lease->client_hostname) {
784e853bc5dShenning 		free(lease->client_hostname);
785e853bc5dShenning 		lease->client_hostname = 0;
786e853bc5dShenning 	}
787e853bc5dShenning 
7882c9b957aSkrw 	/* Replace the lease client identifier with a new one. */
7892c9b957aSkrw 	i = DHO_DHCP_CLIENT_IDENTIFIER;
7902c9b957aSkrw 	if (packet->options[i].len && lease->client_identifier &&
7918d148acdSkrw 	    lease->client_identifier_len == packet->options[i].len &&
7922c9b957aSkrw 	    !memcmp(lease->client_identifier, packet->options[i].data,
7932c9b957aSkrw 	    packet->options[i].len)) {
7942c9b957aSkrw 		/* Same client identifier. */
7952c9b957aSkrw 	} else if (packet->options[i].len) {
7962c9b957aSkrw 		free(lease->client_identifier);
7972c9b957aSkrw 		lease->client_identifier = malloc(packet->options[i].len);
7982c9b957aSkrw 		if (!lease->client_identifier)
799c525a185Skrw 			fatalx("no memory for client identifier.\n");
8002c9b957aSkrw 		lease->client_identifier_len = packet->options[i].len;
8012c9b957aSkrw 		memcpy(lease->client_identifier, packet->options[i].data,
8022c9b957aSkrw 		    packet->options[i].len);
8032c9b957aSkrw 	} else if (lease->client_identifier) {
8042c9b957aSkrw 		free(lease->client_identifier);
8052c9b957aSkrw 		lease->client_identifier = NULL;
8062c9b957aSkrw 		lease->client_identifier_len = 0;
8072c9b957aSkrw 	}
8082c9b957aSkrw 
8098e1c2028Sderaadt 	/*
8108e1c2028Sderaadt 	 * Choose a filename; first from the host_decl, if any, then from
8118e1c2028Sderaadt 	 * the user class, then from the vendor class.
8128e1c2028Sderaadt 	 */
813e853bc5dShenning 	if (lease->host && lease->host->group->filename)
814e853bc5dShenning 		strlcpy(state->filename, lease->host->group->filename,
815e853bc5dShenning 		    sizeof state->filename);
816e853bc5dShenning 	else if (user_class && user_class->group->filename)
817e853bc5dShenning 		strlcpy(state->filename, user_class->group->filename,
818e853bc5dShenning 		    sizeof state->filename);
819e853bc5dShenning 	else if (vendor_class && vendor_class->group->filename)
820e853bc5dShenning 		strlcpy(state->filename, vendor_class->group->filename,
821e853bc5dShenning 		    sizeof state->filename);
822e853bc5dShenning 	else if (packet->raw->file[0])
823e853bc5dShenning 		strlcpy(state->filename, packet->raw->file,
824e853bc5dShenning 		    sizeof state->filename);
825e853bc5dShenning 	else if (lease->subnet->group->filename)
8268e1c2028Sderaadt 		strlcpy(state->filename, lease->subnet->group->filename,
827e853bc5dShenning 		    sizeof state->filename);
828e853bc5dShenning 	else
829e853bc5dShenning 		strlcpy(state->filename, "", sizeof state->filename);
830e853bc5dShenning 
831e853bc5dShenning 	/* Choose a server name as above. */
832e853bc5dShenning 	if (lease->host && lease->host->group->server_name)
833e853bc5dShenning 		state->server_name = lease->host->group->server_name;
834e853bc5dShenning 	else if (user_class && user_class->group->server_name)
835e853bc5dShenning 		state->server_name = user_class->group->server_name;
836e853bc5dShenning 	else if (vendor_class && vendor_class->group->server_name)
837e853bc5dShenning 		state->server_name = vendor_class->group->server_name;
838e853bc5dShenning 	else if (lease->subnet->group->server_name)
8398e1c2028Sderaadt 		state->server_name = lease->subnet->group->server_name;
8408e1c2028Sderaadt 	else state->server_name = NULL;
841e853bc5dShenning 
8428e1c2028Sderaadt 	/*
8438e1c2028Sderaadt 	 * At this point, we have a lease that we can offer the client.
8448e1c2028Sderaadt 	 * Now we construct a lease structure that contains what we want,
8458e1c2028Sderaadt 	 * and call supersede_lease to do the right thing with it.
8468e1c2028Sderaadt 	 */
847e853bc5dShenning 	memset(&lt, 0, sizeof lt);
848e853bc5dShenning 
8498e1c2028Sderaadt 	/*
8508e1c2028Sderaadt 	 * Use the ip address of the lease that we finally found in
8518e1c2028Sderaadt 	 * the database.
8528e1c2028Sderaadt 	 */
853e853bc5dShenning 	lt.ip_addr = lease->ip_addr;
854e853bc5dShenning 
855e853bc5dShenning 	/* Start now. */
856e853bc5dShenning 	lt.starts = cur_time;
857e853bc5dShenning 
858e853bc5dShenning 	/* Figure out maximum lease time. */
8598e1c2028Sderaadt 	if (lease->host && lease->host->group->max_lease_time)
860e853bc5dShenning 		max_lease_time = lease->host->group->max_lease_time;
861e853bc5dShenning 	else
862e853bc5dShenning 		max_lease_time = lease->subnet->group->max_lease_time;
863e853bc5dShenning 
864e853bc5dShenning 	/* Figure out default lease time. */
8658e1c2028Sderaadt 	if (lease->host && lease->host->group->default_lease_time)
8668e1c2028Sderaadt 		default_lease_time = lease->host->group->default_lease_time;
867e853bc5dShenning 	else
8688e1c2028Sderaadt 		default_lease_time = lease->subnet->group->default_lease_time;
869e853bc5dShenning 
8708e1c2028Sderaadt 	/*
8718e1c2028Sderaadt 	 * Figure out how long a lease to assign.    If this is a
8728e1c2028Sderaadt 	 * dynamic BOOTP lease, its duration must be infinite.
8738e1c2028Sderaadt 	 */
874e853bc5dShenning 	if (offer) {
8759e601734Skrw 		i = DHO_DHCP_LEASE_TIME;
8769e601734Skrw 		if (packet->options[i].len == 4) {
8779e601734Skrw 			lease_time = getULong( packet->options[i].data);
878e853bc5dShenning 
8798e1c2028Sderaadt 			/*
8808e1c2028Sderaadt 			 * Don't let the client ask for a longer lease than
8818e1c2028Sderaadt 			 * is supported for this subnet or host.
8825c9a6b01Stom 			 *
8835c9a6b01Stom 			 * time_t is signed, so really large numbers come
8845c9a6b01Stom 			 * back as negative.  Don't allow lease_time of 0,
8855c9a6b01Stom 			 * either.
8868e1c2028Sderaadt 			 */
8875c9a6b01Stom 			if (lease_time < 1 || lease_time > max_lease_time)
888e853bc5dShenning 				lease_time = max_lease_time;
889e853bc5dShenning 		} else
890e853bc5dShenning 			lease_time = default_lease_time;
891e853bc5dShenning 
892e853bc5dShenning 		state->offered_expiry = cur_time + lease_time;
893e853bc5dShenning 		if (when)
894e853bc5dShenning 			lt.ends = when;
895e853bc5dShenning 		else
896e853bc5dShenning 			lt.ends = state->offered_expiry;
897e853bc5dShenning 	} else {
898e853bc5dShenning 		if (lease->host &&
899e853bc5dShenning 		    lease->host->group->bootp_lease_length)
900e853bc5dShenning 			lt.ends = (cur_time +
9018e1c2028Sderaadt 			    lease->host->group->bootp_lease_length);
902e853bc5dShenning 		else if (lease->subnet->group->bootp_lease_length)
903e853bc5dShenning 			lt.ends = (cur_time +
9048e1c2028Sderaadt 			    lease->subnet->group->bootp_lease_length);
905e853bc5dShenning 		else if (lease->host &&
906e853bc5dShenning 		    lease->host->group->bootp_lease_cutoff)
907e853bc5dShenning 			lt.ends = lease->host->group->bootp_lease_cutoff;
908e853bc5dShenning 		else
9098e1c2028Sderaadt 			lt.ends = lease->subnet->group->bootp_lease_cutoff;
910e853bc5dShenning 		state->offered_expiry = lt.ends;
911e853bc5dShenning 		lt.flags = BOOTP_LEASE;
912e853bc5dShenning 	}
913e853bc5dShenning 
914e853bc5dShenning 	/* Record the uid, if given... */
915e853bc5dShenning 	i = DHO_DHCP_CLIENT_IDENTIFIER;
916e853bc5dShenning 	if (packet->options[i].len) {
917e853bc5dShenning 		if (packet->options[i].len <= sizeof lt.uid_buf) {
918e853bc5dShenning 			memcpy(lt.uid_buf, packet->options[i].data,
919e853bc5dShenning 			    packet->options[i].len);
920e853bc5dShenning 			lt.uid = lt.uid_buf;
921e853bc5dShenning 			lt.uid_max = sizeof lt.uid_buf;
922e853bc5dShenning 			lt.uid_len = packet->options[i].len;
923e853bc5dShenning 		} else {
924e853bc5dShenning 			lt.uid_max = lt.uid_len = packet->options[i].len;
92535de856eSderaadt 			lt.uid = malloc(lt.uid_max);
926e853bc5dShenning 			if (!lt.uid)
927c525a185Skrw 				fatalx("can't allocate memory for large uid.");
9288e1c2028Sderaadt 			memcpy(lt.uid, packet->options[i].data, lt.uid_len);
929e853bc5dShenning 		}
930e853bc5dShenning 	}
931e853bc5dShenning 
932e853bc5dShenning 	lt.host = lease->host;
933e853bc5dShenning 	lt.subnet = lease->subnet;
934e853bc5dShenning 	lt.shared_network = lease->shared_network;
935e853bc5dShenning 
936e853bc5dShenning 	/* Don't call supersede_lease on a mocked-up lease. */
937fc9106f9Syasuoka 	if (lease->flags & (STATIC_LEASE | INFORM_NOLEASE)) {
938e853bc5dShenning 		/* Copy the hardware address into the static lease
939e853bc5dShenning 		   structure. */
940e853bc5dShenning 		lease->hardware_addr.hlen = packet->raw->hlen;
941e853bc5dShenning 		lease->hardware_addr.htype = packet->raw->htype;
942e853bc5dShenning 		memcpy(lease->hardware_addr.haddr, packet->raw->chaddr,
943e853bc5dShenning 		    sizeof packet->raw->chaddr); /* XXX */
944e853bc5dShenning 	} else {
945e853bc5dShenning 		/* Record the hardware address, if given... */
946e853bc5dShenning 		lt.hardware_addr.hlen = packet->raw->hlen;
947e853bc5dShenning 		lt.hardware_addr.htype = packet->raw->htype;
948e853bc5dShenning 		memcpy(lt.hardware_addr.haddr, packet->raw->chaddr,
949e853bc5dShenning 		    sizeof packet->raw->chaddr);
950e853bc5dShenning 
951e853bc5dShenning 		/* Install the new information about this lease in the
952e853bc5dShenning 		   database.  If this is a DHCPACK or a dynamic BOOTREPLY
953e853bc5dShenning 		   and we can't write the lease, don't ACK it (or BOOTREPLY
954e853bc5dShenning 		   it) either. */
955e853bc5dShenning 
95635318e8fSkrw 		if (!(supersede_lease(lease, &lt, !offer ||
95735318e8fSkrw 		    offer == DHCPACK) || (offer && offer != DHCPACK))) {
95804fac749Smillert 			free_lease_state(state, "ack_lease: !supersede_lease");
959e853bc5dShenning 			return;
960e853bc5dShenning 		}
96104fac749Smillert 	}
962e853bc5dShenning 
963e853bc5dShenning 	/* Remember the interface on which the packet arrived. */
964e853bc5dShenning 	state->ip = packet->interface;
965e853bc5dShenning 
966e853bc5dShenning 	/* Set a flag if this client is a lame Microsoft client that NUL
967e853bc5dShenning 	   terminates string options and expects us to do likewise. */
9689e601734Skrw 	i = DHO_HOST_NAME;
9699e601734Skrw 	if (packet->options[i].len &&
9709e601734Skrw 	    packet->options[i].data[packet->options[i].len - 1] == '\0')
971e853bc5dShenning 		lease->flags |= MS_NULL_TERMINATION;
972e853bc5dShenning 	else
973e853bc5dShenning 		lease->flags &= ~MS_NULL_TERMINATION;
974e853bc5dShenning 
975e853bc5dShenning 	/* Remember the giaddr, xid, secs, flags and hops. */
976e853bc5dShenning 	state->giaddr = packet->raw->giaddr;
977e853bc5dShenning 	state->ciaddr = packet->raw->ciaddr;
978e853bc5dShenning 	state->xid = packet->raw->xid;
979e853bc5dShenning 	state->secs = packet->raw->secs;
980e853bc5dShenning 	state->bootp_flags = packet->raw->flags;
981e853bc5dShenning 	state->hops = packet->raw->hops;
982e853bc5dShenning 	state->offer = offer;
983cc3e8043Sclaudio 	memcpy(&state->haddr, packet->haddr, sizeof state->haddr);
984e853bc5dShenning 
985e853bc5dShenning 	/* Figure out what options to send to the client: */
986e853bc5dShenning 
987e853bc5dShenning 	/* Start out with the subnet options... */
9888e1c2028Sderaadt 	memcpy(state->options, lease->subnet->group->options,
989e853bc5dShenning 	    sizeof state->options);
990e853bc5dShenning 
991e853bc5dShenning 	/* Vendor and user classes are only supported for DHCP clients. */
992e853bc5dShenning 	if (state->offer) {
993e853bc5dShenning 		/* If we have a vendor class, install those options,
994e853bc5dShenning 		   superseding any subnet options. */
995e853bc5dShenning 		if (vendor_class) {
996e853bc5dShenning 			for (i = 0; i < 256; i++)
997e853bc5dShenning 				if (vendor_class->group->options[i])
998e853bc5dShenning 					state->options[i] =
9998e1c2028Sderaadt 					    vendor_class->group->options[i];
1000e853bc5dShenning 		}
1001e853bc5dShenning 
1002e853bc5dShenning 		/* If we have a user class, install those options,
1003e853bc5dShenning 		   superseding any subnet and vendor class options. */
1004e853bc5dShenning 		if (user_class) {
1005e853bc5dShenning 			for (i = 0; i < 256; i++)
1006e853bc5dShenning 				if (user_class->group->options[i])
1007e853bc5dShenning 					state->options[i] =
10088e1c2028Sderaadt 					    user_class->group->options[i];
1009e853bc5dShenning 		}
1010e853bc5dShenning 
1011e853bc5dShenning 	}
1012e853bc5dShenning 
1013e853bc5dShenning 	/* If we have a host_decl structure, install the associated
1014e853bc5dShenning 	   options, superseding anything that's in the way. */
1015e853bc5dShenning 	if (lease->host) {
1016e853bc5dShenning 		for (i = 0; i < 256; i++)
1017e853bc5dShenning 			if (lease->host->group->options[i])
10188e1c2028Sderaadt 				state->options[i] =
10198e1c2028Sderaadt 				    lease->host->group->options[i];
1020e853bc5dShenning 	}
1021e853bc5dShenning 
1022e853bc5dShenning 	/* Get the Maximum Message Size option from the packet, if one
1023e853bc5dShenning 	   was sent. */
1024e853bc5dShenning 	i = DHO_DHCP_MAX_MESSAGE_SIZE;
1025e853bc5dShenning 	if (packet->options[i].data &&
10268e1c2028Sderaadt 	    packet->options[i].len == sizeof(u_int16_t))
10278e1c2028Sderaadt 		state->max_message_size = getUShort(packet->options[i].data);
1028e853bc5dShenning 	/* Otherwise, if a maximum message size was specified, use that. */
1029e853bc5dShenning 	else if (state->options[i] && state->options[i]->value)
10308e1c2028Sderaadt 		state->max_message_size = getUShort(state->options[i]->value);
1031e853bc5dShenning 
1032e853bc5dShenning 	/* Save the parameter request list if there is one. */
1033e853bc5dShenning 	i = DHO_DHCP_PARAMETER_REQUEST_LIST;
1034e853bc5dShenning 	if (packet->options[i].data) {
1035d60fc4a4Skrw 		state->prl = calloc(1, packet->options[i].len);
1036e853bc5dShenning 		if (!state->prl)
1037c525a185Skrw 			log_warnx("no memory for parameter request list");
1038e853bc5dShenning 		else {
10398e1c2028Sderaadt 			memcpy(state->prl, packet->options[i].data,
1040e853bc5dShenning 			    packet->options[i].len);
1041e853bc5dShenning 			state->prl_len = packet->options[i].len;
1042e853bc5dShenning 		}
1043e853bc5dShenning 	}
1044e853bc5dShenning 
1045e853bc5dShenning 	/* If we didn't get a hostname from an option somewhere, see if
1046e853bc5dShenning 	   we can get one from the lease. */
1047e853bc5dShenning 	i = DHO_HOST_NAME;
1048e853bc5dShenning 	if (!state->options[i] && lease->hostname) {
1049e853bc5dShenning 		state->options[i] = new_tree_cache("hostname");
1050e853bc5dShenning 		state->options[i]->flags = TC_TEMPORARY;
10518e1c2028Sderaadt 		state->options[i]->value = (unsigned char *)lease->hostname;
1052e853bc5dShenning 		state->options[i]->len = strlen(lease->hostname);
1053e853bc5dShenning 		state->options[i]->buf_size = state->options[i]->len;
10545dee4edfSotto 		state->options[i]->timeout = -1;
10558e1c2028Sderaadt 		state->options[i]->tree = NULL;
1056e853bc5dShenning 	}
1057e853bc5dShenning 
10588e1c2028Sderaadt 	/*
10598e1c2028Sderaadt 	 * Now, if appropriate, put in DHCP-specific options that
10608e1c2028Sderaadt 	 * override those.
10618e1c2028Sderaadt 	 */
1062e853bc5dShenning 	if (state->offer) {
1063e853bc5dShenning 		i = DHO_DHCP_MESSAGE_TYPE;
1064e853bc5dShenning 		state->options[i] = new_tree_cache("message-type");
1065e853bc5dShenning 		state->options[i]->flags = TC_TEMPORARY;
1066e853bc5dShenning 		state->options[i]->value = &state->offer;
1067e853bc5dShenning 		state->options[i]->len = sizeof state->offer;
1068e853bc5dShenning 		state->options[i]->buf_size = sizeof state->offer;
10695dee4edfSotto 		state->options[i]->timeout = -1;
10708e1c2028Sderaadt 		state->options[i]->tree = NULL;
1071e853bc5dShenning 
1072e853bc5dShenning 		i = DHO_DHCP_SERVER_IDENTIFIER;
1073e853bc5dShenning 		if (!state->options[i]) {
1074e853bc5dShenning 		 use_primary:
1075e853bc5dShenning 			state->options[i] = new_tree_cache("server-id");
1076e853bc5dShenning 			state->options[i]->value =
10778e1c2028Sderaadt 			    (unsigned char *)&state->ip->primary_address;
1078e853bc5dShenning 			state->options[i]->len =
1079e853bc5dShenning 			    sizeof state->ip->primary_address;
10809e601734Skrw 			state->options[i]->buf_size = state->options[i]->len;
10815dee4edfSotto 			state->options[i]->timeout = -1;
10828e1c2028Sderaadt 			state->options[i]->tree = NULL;
10838e1c2028Sderaadt 			state->from.len = sizeof state->ip->primary_address;
10848e1c2028Sderaadt 			memcpy(state->from.iabuf, &state->ip->primary_address,
1085e853bc5dShenning 			    state->from.len);
1086e853bc5dShenning 		} else {
1087e853bc5dShenning 			/* Find the value of the server identifier... */
1088e853bc5dShenning 			if (!tree_evaluate(state->options[i]))
1089e853bc5dShenning 				goto use_primary;
1090e853bc5dShenning 			if (!state->options[i]->value ||
109135318e8fSkrw 			    (state->options[i]->len >
109235318e8fSkrw 			    sizeof state->from.iabuf))
1093e853bc5dShenning 				goto use_primary;
1094e853bc5dShenning 
1095e853bc5dShenning 			state->from.len = state->options[i]->len;
10968e1c2028Sderaadt 			memcpy(state->from.iabuf, state->options[i]->value,
1097e853bc5dShenning 			    state->from.len);
1098e853bc5dShenning 		}
10997e041c99Skrw 		/*
11007e041c99Skrw 		 * Do not ACK a REQUEST intended for another server.
11017e041c99Skrw 		 */
11027e041c99Skrw 		if (packet->options[i].len == 4) {
11037e041c99Skrw 			if (state->options[i]->len != 4 ||
11047e041c99Skrw 			    memcmp(packet->options[i].data,
11057e041c99Skrw 			    state->options[i]->value, 4) != 0) {
11067e041c99Skrw 				free_lease_state(state, "ack_lease: "
11077e041c99Skrw 				    "server identifier");
11087e041c99Skrw 				return;
11097e041c99Skrw 			}
11107e041c99Skrw 		}
11117e041c99Skrw 
1112fc9106f9Syasuoka 		/* If we used the vendor class the client specified, we
1113fc9106f9Syasuoka 		   have to return it. */
1114fc9106f9Syasuoka 		if (vendor_class) {
1115fc9106f9Syasuoka 			i = DHO_DHCP_CLASS_IDENTIFIER;
11169e601734Skrw 			state->options[i] = new_tree_cache("class-identifier");
1117fc9106f9Syasuoka 			state->options[i]->flags = TC_TEMPORARY;
1118fc9106f9Syasuoka 			state->options[i]->value =
1119fc9106f9Syasuoka 				(unsigned char *)vendor_class->name;
11209e601734Skrw 			state->options[i]->len = strlen(vendor_class->name);
11219e601734Skrw 			state->options[i]->buf_size = state->options[i]->len;
1122fc9106f9Syasuoka 			state->options[i]->timeout = -1;
1123fc9106f9Syasuoka 			state->options[i]->tree = NULL;
1124fc9106f9Syasuoka 		}
1125fc9106f9Syasuoka 
1126fc9106f9Syasuoka 		/* If we used the user class the client specified, we
1127fc9106f9Syasuoka 		   have to return it. */
1128fc9106f9Syasuoka 		if (user_class) {
1129fc9106f9Syasuoka 			i = DHO_DHCP_USER_CLASS_ID;
1130fc9106f9Syasuoka 			state->options[i] = new_tree_cache("user-class");
1131fc9106f9Syasuoka 			state->options[i]->flags = TC_TEMPORARY;
1132fc9106f9Syasuoka 			state->options[i]->value =
1133fc9106f9Syasuoka 				(unsigned char *)user_class->name;
11349e601734Skrw 			state->options[i]->len = strlen(user_class->name);
11359e601734Skrw 			state->options[i]->buf_size = state->options[i]->len;
1136fc9106f9Syasuoka 			state->options[i]->timeout = -1;
1137fc9106f9Syasuoka 			state->options[i]->tree = NULL;
1138fc9106f9Syasuoka 		}
1139fc9106f9Syasuoka 	}
1140fc9106f9Syasuoka 
1141fc9106f9Syasuoka 	/* for DHCPINFORM, don't include lease time parameters */
1142fc9106f9Syasuoka 	if (state->offer && (lease->flags & INFORM_NOLEASE) == 0) {
1143e853bc5dShenning 
1144e853bc5dShenning 		/* Sanity check the lease time. */
1145e853bc5dShenning 		if ((state->offered_expiry - cur_time) < 15)
1146e853bc5dShenning 			offered_lease_time = default_lease_time;
1147e853bc5dShenning 		else if (state->offered_expiry - cur_time > max_lease_time)
1148e853bc5dShenning 			offered_lease_time = max_lease_time;
1149e853bc5dShenning 		else
1150e853bc5dShenning 			offered_lease_time =
1151e853bc5dShenning 			    state->offered_expiry - cur_time;
1152e853bc5dShenning 
11539e601734Skrw 		putULong((unsigned char *)&state->expiry, offered_lease_time);
1154e853bc5dShenning 		i = DHO_DHCP_LEASE_TIME;
1155e853bc5dShenning 		state->options[i] = new_tree_cache("lease-expiry");
1156e853bc5dShenning 		state->options[i]->flags = TC_TEMPORARY;
11578e1c2028Sderaadt 		state->options[i]->value = (unsigned char *)&state->expiry;
1158e853bc5dShenning 		state->options[i]->len = sizeof state->expiry;
1159e853bc5dShenning 		state->options[i]->buf_size = sizeof state->expiry;
11605dee4edfSotto 		state->options[i]->timeout = -1;
11618e1c2028Sderaadt 		state->options[i]->tree = NULL;
1162e853bc5dShenning 
1163e853bc5dShenning 		/* Renewal time is lease time * 0.5. */
1164e853bc5dShenning 		offered_lease_time /= 2;
11659e601734Skrw 		putULong((unsigned char *)&state->renewal, offered_lease_time);
1166e853bc5dShenning 		i = DHO_DHCP_RENEWAL_TIME;
1167e853bc5dShenning 		state->options[i] = new_tree_cache("renewal-time");
1168e853bc5dShenning 		state->options[i]->flags = TC_TEMPORARY;
1169e853bc5dShenning 		state->options[i]->value =
1170e853bc5dShenning 			(unsigned char *)&state->renewal;
1171e853bc5dShenning 		state->options[i]->len = sizeof state->renewal;
1172e853bc5dShenning 		state->options[i]->buf_size = sizeof state->renewal;
11735dee4edfSotto 		state->options[i]->timeout = -1;
11748e1c2028Sderaadt 		state->options[i]->tree = NULL;
1175e853bc5dShenning 
1176e853bc5dShenning 
1177e853bc5dShenning 		/* Rebinding time is lease time * 0.875. */
11788e1c2028Sderaadt 		offered_lease_time += (offered_lease_time / 2 +
11798e1c2028Sderaadt 		    offered_lease_time / 4);
11809e601734Skrw 		putULong((unsigned char *)&state->rebind, offered_lease_time);
1181e853bc5dShenning 		i = DHO_DHCP_REBINDING_TIME;
1182e853bc5dShenning 		state->options[i] = new_tree_cache("rebind-time");
1183e853bc5dShenning 		state->options[i]->flags = TC_TEMPORARY;
11849e601734Skrw 		state->options[i]->value = (unsigned char *)&state->rebind;
1185e853bc5dShenning 		state->options[i]->len = sizeof state->rebind;
1186e853bc5dShenning 		state->options[i]->buf_size = sizeof state->rebind;
11875dee4edfSotto 		state->options[i]->timeout = -1;
11888e1c2028Sderaadt 		state->options[i]->tree = NULL;
1189e853bc5dShenning 	}
1190e853bc5dShenning 
1191e853bc5dShenning 	/* Use the subnet mask from the subnet declaration if no other
1192e853bc5dShenning 	   mask has been provided. */
1193e853bc5dShenning 	i = DHO_SUBNET_MASK;
1194e853bc5dShenning 	if (!state->options[i]) {
1195e853bc5dShenning 		state->options[i] = new_tree_cache("subnet-mask");
1196e853bc5dShenning 		state->options[i]->flags = TC_TEMPORARY;
11979e601734Skrw 		state->options[i]->value = lease->subnet->netmask.iabuf;
1198e853bc5dShenning 		state->options[i]->len = lease->subnet->netmask.len;
11999e601734Skrw 		state->options[i]->buf_size = lease->subnet->netmask.len;
12005dee4edfSotto 		state->options[i]->timeout = -1;
12018e1c2028Sderaadt 		state->options[i]->tree = NULL;
1202e853bc5dShenning 	}
1203e853bc5dShenning 
1204e853bc5dShenning 	/* If so directed, use the leased IP address as the router address.
1205e853bc5dShenning 	   This supposedly makes Win95 machines ARP for all IP addresses,
1206e853bc5dShenning 	   so if the local router does proxy arp, you win. */
1207e853bc5dShenning 
1208e853bc5dShenning 	ulafdr = 0;
1209e853bc5dShenning 	if (lease->host) {
1210e853bc5dShenning 		if (lease->host->group->use_lease_addr_for_default_route)
1211e853bc5dShenning 			ulafdr = 1;
1212e853bc5dShenning 	} else if (user_class) {
1213e853bc5dShenning 		if (user_class->group->use_lease_addr_for_default_route)
1214e853bc5dShenning 			ulafdr = 1;
1215e853bc5dShenning 	} else if (vendor_class) {
1216e853bc5dShenning 		if (vendor_class->group->use_lease_addr_for_default_route)
1217e853bc5dShenning 			ulafdr = 1;
12188e1c2028Sderaadt 	} else if (lease->subnet->group->use_lease_addr_for_default_route)
1219e853bc5dShenning 		ulafdr = 1;
1220e853bc5dShenning 	else
1221e853bc5dShenning 		ulafdr = 0;
1222e853bc5dShenning 
1223e853bc5dShenning 	i = DHO_ROUTERS;
1224e853bc5dShenning 	if (ulafdr && !state->options[i]) {
1225e853bc5dShenning 		state->options[i] = new_tree_cache("routers");
1226e853bc5dShenning 		state->options[i]->flags = TC_TEMPORARY;
12278e1c2028Sderaadt 		state->options[i]->value = lease->ip_addr.iabuf;
12288e1c2028Sderaadt 		state->options[i]->len = lease->ip_addr.len;
12298e1c2028Sderaadt 		state->options[i]->buf_size = lease->ip_addr.len;
12305dee4edfSotto 		state->options[i]->timeout = -1;
12318e1c2028Sderaadt 		state->options[i]->tree = NULL;
1232e853bc5dShenning 	}
1233e853bc5dShenning 
1234a82310c0Skrw 	/*
1235a82310c0Skrw 	 * RFC 3046: MUST NOT echo relay agent information if the server
1236a82310c0Skrw 	 * does not understand/use the data. We don't.
1237a82310c0Skrw 	 */
12385b44f46dSreyk 	i = DHO_RELAY_AGENT_INFORMATION;
1239a82310c0Skrw 	memset(&state->options[i], 0, sizeof(state->options[i]));
12405b44f46dSreyk 
12412c9b957aSkrw 	/* Echo back the client-identifier as RFC 6842 mandates. */
1242a51e9872Skrw 	if (lease->host)
1243a51e9872Skrw 		echo_client_id = lease->host->group->echo_client_id;
1244a51e9872Skrw 	else if (user_class)
1245a51e9872Skrw 		echo_client_id = user_class->group->echo_client_id;
1246a51e9872Skrw 	else if (vendor_class)
1247a51e9872Skrw 		echo_client_id = vendor_class->group->echo_client_id;
1248a51e9872Skrw 	else
1249a51e9872Skrw 		echo_client_id = lease->subnet->group->echo_client_id;
12503fd9b174Skrw 	i = DHO_DHCP_CLIENT_IDENTIFIER;
1251a51e9872Skrw 	if (lease->client_identifier && echo_client_id) {
12522c9b957aSkrw 		state->options[i] = new_tree_cache("dhcp-client-identifier");
12532c9b957aSkrw 		state->options[i]->flags = TC_TEMPORARY;
12542c9b957aSkrw 		state->options[i]->value = lease->client_identifier;
12552c9b957aSkrw 		state->options[i]->len = lease->client_identifier_len;
12562c9b957aSkrw 		state->options[i]->buf_size = lease->client_identifier_len;
12572c9b957aSkrw 		state->options[i]->timeout = -1;
12582c9b957aSkrw 		state->options[i]->tree = NULL;
12592c9b957aSkrw 	} else
12603fd9b174Skrw 		memset(&state->options[i], 0, sizeof(state->options[i]));
12613fd9b174Skrw 
1262e853bc5dShenning 	lease->state = state;
1263e853bc5dShenning 
1264e853bc5dShenning 	/* If this is a DHCPOFFER, ping the lease address before actually
1265e853bc5dShenning 	   sending the offer. */
1266e853bc5dShenning 	if (offer == DHCPOFFER && !(lease->flags & STATIC_LEASE) &&
1267e853bc5dShenning 	    cur_time - lease->timestamp > 60) {
1268e853bc5dShenning 		lease->timestamp = cur_time;
1269e853bc5dShenning 		icmp_echorequest(&lease->ip_addr);
1270e853bc5dShenning 		add_timeout(cur_time + 1, lease_ping_timeout, lease);
1271e853bc5dShenning 		++outstanding_pings;
1272e853bc5dShenning 	} else {
1273e853bc5dShenning 		lease->timestamp = cur_time;
1274e853bc5dShenning 		dhcp_reply(lease);
1275e853bc5dShenning 	}
1276e853bc5dShenning }
1277e853bc5dShenning 
12788e1c2028Sderaadt void
dhcp_reply(struct lease * lease)12798e1c2028Sderaadt dhcp_reply(struct lease *lease)
1280e853bc5dShenning {
1281d7503dcfSkrw 	char ciaddrbuf[INET_ADDRSTRLEN];
12820795b389Sderaadt 	int bufs = 0, packet_length, i;
1283e853bc5dShenning 	struct dhcp_packet raw;
1284e853bc5dShenning 	struct sockaddr_in to;
1285e853bc5dShenning 	struct in_addr from;
1286e853bc5dShenning 	struct lease_state *state = lease->state;
1287e853bc5dShenning 	int nulltp, bootpp;
1288e853bc5dShenning 	u_int8_t *prl;
1289e853bc5dShenning 	int prl_len;
1290e853bc5dShenning 
1291e853bc5dShenning 	if (!state)
1292c525a185Skrw 		fatalx("dhcp_reply was supplied lease with no state!");
1293e853bc5dShenning 
1294e853bc5dShenning 	/* Compose a response for the client... */
1295e853bc5dShenning 	memset(&raw, 0, sizeof raw);
1296e853bc5dShenning 
1297e853bc5dShenning 	/* Copy in the filename if given; otherwise, flag the filename
1298e853bc5dShenning 	   buffer as available for options. */
1299e853bc5dShenning 	if (state->filename[0])
1300e853bc5dShenning 		strlcpy(raw.file, state->filename, sizeof raw.file);
1301e853bc5dShenning 	else
1302e853bc5dShenning 		bufs |= 1;
1303e853bc5dShenning 
1304e853bc5dShenning 	/* Copy in the server name if given; otherwise, flag the
1305e853bc5dShenning 	   server_name buffer as available for options. */
1306e853bc5dShenning 	if (state->server_name)
1307e853bc5dShenning 		strlcpy(raw.sname, state->server_name, sizeof raw.sname);
1308e853bc5dShenning 	else
1309e853bc5dShenning 		bufs |= 2; /* XXX */
1310e853bc5dShenning 
1311e853bc5dShenning 	memcpy(raw.chaddr, lease->hardware_addr.haddr, sizeof raw.chaddr);
1312e853bc5dShenning 	raw.hlen = lease->hardware_addr.hlen;
1313e853bc5dShenning 	raw.htype = lease->hardware_addr.htype;
1314e853bc5dShenning 
1315e853bc5dShenning 	/* See if this is a Microsoft client that NUL-terminates its
1316e853bc5dShenning 	   strings and expects us to do likewise... */
1317e853bc5dShenning 	if (lease->flags & MS_NULL_TERMINATION)
1318e853bc5dShenning 		nulltp = 1;
1319e853bc5dShenning 	else
1320e853bc5dShenning 		nulltp = 0;
1321e853bc5dShenning 
1322e853bc5dShenning 	/* See if this is a bootp client... */
1323e853bc5dShenning 	if (state->offer)
1324e853bc5dShenning 		bootpp = 0;
1325e853bc5dShenning 	else
1326e853bc5dShenning 		bootpp = 1;
1327e853bc5dShenning 
1328e853bc5dShenning 	if (state->options[DHO_DHCP_PARAMETER_REQUEST_LIST] &&
1329e853bc5dShenning 	    state->options[DHO_DHCP_PARAMETER_REQUEST_LIST]->value) {
13308e1c2028Sderaadt 		prl = state->options[DHO_DHCP_PARAMETER_REQUEST_LIST]->value;
13318e1c2028Sderaadt 		prl_len = state->options[DHO_DHCP_PARAMETER_REQUEST_LIST]->len;
1332e853bc5dShenning 	} else if (state->prl) {
1333e853bc5dShenning 		prl = state->prl;
1334e853bc5dShenning 		prl_len = state->prl_len;
1335e853bc5dShenning 	} else {
13368e1c2028Sderaadt 		prl = NULL;
1337e853bc5dShenning 		prl_len = 0;
1338e853bc5dShenning 	}
1339e853bc5dShenning 
1340e853bc5dShenning 	/* Insert such options as will fit into the buffer. */
13418e1c2028Sderaadt 	packet_length = cons_options(NULL, &raw, state->max_message_size,
13428e1c2028Sderaadt 	    state->options, bufs, nulltp, bootpp, prl, prl_len);
1343e853bc5dShenning 
1344e853bc5dShenning 	/* Having done the cons_options(), we can release the tree_cache
1345e853bc5dShenning 	   entries. */
1346e853bc5dShenning 	for (i = 0; i < 256; i++) {
1347e853bc5dShenning 		if (state->options[i] &&
1348e853bc5dShenning 		    state->options[i]->flags & TC_TEMPORARY)
1349285f06efSderaadt 			free_tree_cache(state->options[i]);
1350e853bc5dShenning 	}
1351e853bc5dShenning 
1352e853bc5dShenning 	memcpy(&raw.ciaddr, &state->ciaddr, sizeof raw.ciaddr);
1353fc9106f9Syasuoka 	if ((lease->flags & INFORM_NOLEASE) == 0)
1354e853bc5dShenning 		memcpy(&raw.yiaddr, lease->ip_addr.iabuf, 4);
1355e853bc5dShenning 
1356e853bc5dShenning 	/* Figure out the address of the next server. */
1357e853bc5dShenning 	if (lease->host && lease->host->group->next_server.len)
13588e1c2028Sderaadt 		memcpy(&raw.siaddr, lease->host->group->next_server.iabuf, 4);
1359e853bc5dShenning 	else if (lease->subnet->group->next_server.len)
136035318e8fSkrw 		memcpy(&raw.siaddr, lease->subnet->group->next_server.iabuf,
136135318e8fSkrw 		    4);
1362e853bc5dShenning 	else if (lease->subnet->interface_address.len)
13638e1c2028Sderaadt 		memcpy(&raw.siaddr, lease->subnet->interface_address.iabuf, 4);
1364e853bc5dShenning 	else
1365e853bc5dShenning 		raw.siaddr = state->ip->primary_address;
1366e853bc5dShenning 
1367e853bc5dShenning 	raw.giaddr = state->giaddr;
1368e853bc5dShenning 
1369e853bc5dShenning 	raw.xid = state->xid;
1370e853bc5dShenning 	raw.secs = state->secs;
1371e853bc5dShenning 	raw.flags = state->bootp_flags;
1372e853bc5dShenning 	raw.hops = state->hops;
1373e853bc5dShenning 	raw.op = BOOTREPLY;
1374e853bc5dShenning 
1375d7503dcfSkrw 	/* Can't do >1 inet_ntoa() in a printf()! */
1376d7503dcfSkrw 	strlcpy(ciaddrbuf, inet_ntoa(state->ciaddr), sizeof(ciaddrbuf));
1377d7503dcfSkrw 
1378e853bc5dShenning 	/* Say what we're doing... */
1379c9109787Skrw 	if ((state->offer == DHCPACK) && (lease->flags & INFORM_NOLEASE))
1380c525a185Skrw 		log_info("DHCPACK to %s (%s) via %s",
1381d7503dcfSkrw 		    ciaddrbuf,
1382c9109787Skrw 		    print_hw_addr(lease->hardware_addr.htype,
1383c9109787Skrw 		        lease->hardware_addr.hlen, lease->hardware_addr.haddr),
1384c9109787Skrw 		    state->giaddr.s_addr ? inet_ntoa(state->giaddr) :
1385c9109787Skrw 		        state->ip->name);
1386c9109787Skrw 	else
1387c525a185Skrw 		log_info("%s on %s to %s via %s",
1388c9109787Skrw 		    (state->offer ? (state->offer == DHCPACK ? "DHCPACK" :
1389c9109787Skrw 			"DHCPOFFER") : "BOOTREPLY"),
1390e853bc5dShenning 		    piaddr(lease->ip_addr),
1391c9109787Skrw 		    print_hw_addr(lease->hardware_addr.htype,
1392c9109787Skrw 		        lease->hardware_addr.hlen, lease->hardware_addr.haddr),
1393c9109787Skrw 		    state->giaddr.s_addr ? inet_ntoa(state->giaddr) :
1394c9109787Skrw 		        state->ip->name);
1395e853bc5dShenning 
1396e853bc5dShenning 	memset(&to, 0, sizeof to);
1397e853bc5dShenning 	to.sin_family = AF_INET;
1398e853bc5dShenning #ifdef HAVE_SA_LEN
1399e853bc5dShenning 	to.sin_len = sizeof to;
1400e853bc5dShenning #endif
1401e853bc5dShenning 
1402e853bc5dShenning 	/* Make sure outgoing packets are at least as big
1403e853bc5dShenning 	   as a BOOTP packet. */
1404e853bc5dShenning 	if (packet_length < BOOTP_MIN_LEN)
1405e853bc5dShenning 		packet_length = BOOTP_MIN_LEN;
1406e853bc5dShenning 
1407e853bc5dShenning 	/* If this was gatewayed, send it back to the gateway... */
1408e853bc5dShenning 	if (raw.giaddr.s_addr) {
1409e853bc5dShenning 		to.sin_addr = raw.giaddr;
1410390956b7Scanacar 		to.sin_port = server_port;
1411e853bc5dShenning 
1412bbbabac3Sclaudio 		memcpy(&from, state->from.iabuf, sizeof from);
1413bbbabac3Sclaudio 
141484d8c049Syasuoka 		(void) state->ip->send_packet(state->ip, &raw,
14156b08fb5bSclaudio 		    packet_length, from, &to, &state->haddr);
1416e853bc5dShenning 
1417cc3e8043Sclaudio 		free_lease_state(state, "dhcp_reply gateway");
14188e1c2028Sderaadt 		lease->state = NULL;
1419e853bc5dShenning 		return;
1420e853bc5dShenning 
1421e853bc5dShenning 	/* If the client is RENEWING, unicast to the client using the
1422e853bc5dShenning 	   regular IP stack.  Some clients, particularly those that
1423e853bc5dShenning 	   follow RFC1541, are buggy, and send both ciaddr and
1424e853bc5dShenning 	   server-identifier.  We deal with this situation by assuming
1425e853bc5dShenning 	   that if we got both dhcp-server-identifier and ciaddr, and
1426e853bc5dShenning 	   giaddr was not set, then the client is on the local
1427e853bc5dShenning 	   network, and we can therefore unicast or broadcast to it
1428e853bc5dShenning 	   successfully.  A client in REQUESTING state on another
1429e853bc5dShenning 	   network that's making this mistake will have set giaddr,
1430e853bc5dShenning 	   and will therefore get a relayed response from the above
1431e853bc5dShenning 	   code. */
1432e853bc5dShenning 	} else if (raw.ciaddr.s_addr &&
1433e853bc5dShenning 	    !((state->got_server_identifier ||
1434e853bc5dShenning 	    (raw.flags & htons(BOOTP_BROADCAST))) &&
1435e853bc5dShenning 	    /* XXX This won't work if giaddr isn't zero, but it is: */
1436e853bc5dShenning 	    (state->shared_network == lease->shared_network)) &&
1437e853bc5dShenning 	    state->offer == DHCPACK) {
1438e853bc5dShenning 		to.sin_addr = raw.ciaddr;
1439390956b7Scanacar 		to.sin_port = client_port;
1440e853bc5dShenning 
1441e853bc5dShenning 	/* If it comes from a client that already knows its address
1442e853bc5dShenning 	   and is not requesting a broadcast response, and we can
1443e853bc5dShenning 	   unicast to a client without using the ARP protocol, sent it
1444e853bc5dShenning 	   directly to that client. */
1445e853bc5dShenning 	} else if (!(raw.flags & htons(BOOTP_BROADCAST))) {
1446e853bc5dShenning 		to.sin_addr = raw.yiaddr;
1447390956b7Scanacar 		to.sin_port = client_port;
1448e853bc5dShenning 
1449e853bc5dShenning 	/* Otherwise, broadcast it on the local network. */
1450e853bc5dShenning 	} else {
1451e853bc5dShenning 		to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
1452390956b7Scanacar 		to.sin_port = client_port;
1453e4a7785eSkrw 		memset(&state->haddr, 0xff, sizeof state->haddr);
1454e853bc5dShenning 	}
1455e853bc5dShenning 
1456e853bc5dShenning 	memcpy(&from, state->from.iabuf, sizeof from);
1457e853bc5dShenning 
145884d8c049Syasuoka 	(void) state->ip->send_packet(state->ip, &raw, packet_length,
1459cc55341fSclaudio 	    from, &to, &state->haddr);
1460e853bc5dShenning 
1461e853bc5dShenning 	free_lease_state(state, "dhcp_reply");
14628e1c2028Sderaadt 	lease->state = NULL;
1463e853bc5dShenning }
1464e853bc5dShenning 
14658e1c2028Sderaadt struct lease *
find_lease(struct packet * packet,struct shared_network * share,int * ours)14668e1c2028Sderaadt find_lease(struct packet *packet, struct shared_network *share,
14678e1c2028Sderaadt     int *ours)
1468e853bc5dShenning {
1469e853bc5dShenning 	struct lease *uid_lease, *ip_lease, *hw_lease;
1470ea507cabSderaadt 	struct lease *lease = NULL;
1471e853bc5dShenning 	struct iaddr cip;
14728e1c2028Sderaadt 	struct host_decl *hp, *host = NULL;
1473e853bc5dShenning 	struct lease *fixed_lease;
1474e853bc5dShenning 
1475e853bc5dShenning 	/* Figure out what IP address the client is requesting, if any. */
147641b6b05bStobias 	if (packet->options[DHO_DHCP_REQUESTED_ADDRESS].len == 4) {
1477e853bc5dShenning 		packet->got_requested_address = 1;
1478e853bc5dShenning 		cip.len = 4;
1479e853bc5dShenning 		memcpy(cip.iabuf,
1480e853bc5dShenning 		    packet->options[DHO_DHCP_REQUESTED_ADDRESS].data,
1481e853bc5dShenning 		    cip.len);
1482e853bc5dShenning 	} else if (packet->raw->ciaddr.s_addr) {
1483e853bc5dShenning 		cip.len = 4;
1484e853bc5dShenning 		memcpy(cip.iabuf, &packet->raw->ciaddr, 4);
1485e853bc5dShenning 	} else
1486e853bc5dShenning 		cip.len = 0;
1487e853bc5dShenning 
1488e853bc5dShenning 	/* Try to find a host or lease that's been assigned to the
1489e853bc5dShenning 	   specified unique client identifier. */
1490e853bc5dShenning 	if (packet->options[DHO_DHCP_CLIENT_IDENTIFIER].len) {
1491e853bc5dShenning 		/* First, try to find a fixed host entry for the specified
1492e853bc5dShenning 		   client identifier... */
14938e1c2028Sderaadt 		hp = find_hosts_by_uid(
14948e1c2028Sderaadt 		    packet->options[DHO_DHCP_CLIENT_IDENTIFIER].data,
14958e1c2028Sderaadt 		    packet->options[DHO_DHCP_CLIENT_IDENTIFIER].len);
1496e853bc5dShenning 		if (hp) {
1497e853bc5dShenning 			host = hp;
1498e853bc5dShenning 			fixed_lease = mockup_lease(packet, share, hp);
1499ea507cabSderaadt 			uid_lease = NULL;
1500e853bc5dShenning 		} else {
15018e1c2028Sderaadt 			uid_lease = find_lease_by_uid(
15028e1c2028Sderaadt 			    packet->options[DHO_DHCP_CLIENT_IDENTIFIER].data,
15038e1c2028Sderaadt 			    packet->options[DHO_DHCP_CLIENT_IDENTIFIER].len);
1504e853bc5dShenning 			/* Find the lease matching this uid that's on the
1505e853bc5dShenning 			   network the packet came from (if any). */
1506e853bc5dShenning 			for (; uid_lease; uid_lease = uid_lease->n_uid)
1507e853bc5dShenning 				if (uid_lease->shared_network == share)
1508e853bc5dShenning 					break;
1509ea507cabSderaadt 			fixed_lease = NULL;
15108e1c2028Sderaadt 			if (uid_lease && (uid_lease->flags & ABANDONED_LEASE))
1511ea507cabSderaadt 				uid_lease = NULL;
1512e853bc5dShenning 		}
1513e853bc5dShenning 	} else {
1514ea507cabSderaadt 		uid_lease = NULL;
1515ea507cabSderaadt 		fixed_lease = NULL;
1516e853bc5dShenning 	}
1517e853bc5dShenning 
1518e853bc5dShenning 	/* If we didn't find a fixed lease using the uid, try doing
1519e853bc5dShenning 	   it with the hardware address... */
1520e853bc5dShenning 	if (!fixed_lease) {
1521e853bc5dShenning 		hp = find_hosts_by_haddr(packet->raw->htype,
15228e1c2028Sderaadt 		    packet->raw->chaddr, packet->raw->hlen);
1523e853bc5dShenning 		if (hp) {
1524e853bc5dShenning 			host = hp; /* Save it for later. */
1525e853bc5dShenning 			fixed_lease = mockup_lease(packet, share, hp);
1526e853bc5dShenning 		}
1527e853bc5dShenning 	}
1528e853bc5dShenning 
1529e853bc5dShenning 	/* If fixed_lease is present but does not match the requested
1530e853bc5dShenning 	   IP address, and this is a DHCPREQUEST, then we can't return
1531e853bc5dShenning 	   any other lease, so we might as well return now. */
1532e853bc5dShenning 	if (packet->packet_type == DHCPREQUEST && fixed_lease &&
1533e853bc5dShenning 	    (fixed_lease->ip_addr.len != cip.len ||
15348e1c2028Sderaadt 	    memcmp(fixed_lease->ip_addr.iabuf, cip.iabuf, cip.len))) {
1535e853bc5dShenning 		if (ours)
1536e853bc5dShenning 			*ours = 1;
1537e853bc5dShenning 		strlcpy(dhcp_message, "requested address is incorrect",
1538e853bc5dShenning 		    sizeof(dhcp_message));
1539ea507cabSderaadt 		return NULL;
1540e853bc5dShenning 	}
1541e853bc5dShenning 
1542e853bc5dShenning 	/* Try to find a lease that's been attached to the client's
1543e853bc5dShenning 	   hardware address... */
1544e853bc5dShenning 	hw_lease = find_lease_by_hw_addr(packet->raw->chaddr,
1545e853bc5dShenning 	    packet->raw->hlen);
1546e853bc5dShenning 	/* Find the lease that's on the network the packet came from
1547e853bc5dShenning 	   (if any). */
1548e853bc5dShenning 	for (; hw_lease; hw_lease = hw_lease->n_hw) {
1549e853bc5dShenning 		if (hw_lease->shared_network == share) {
1550e853bc5dShenning 			if ((hw_lease->flags & ABANDONED_LEASE))
1551e853bc5dShenning 				continue;
1552e853bc5dShenning 			if (packet->packet_type)
1553e853bc5dShenning 				break;
1554e853bc5dShenning 			if (hw_lease->flags &
1555e853bc5dShenning 			    (BOOTP_LEASE | DYNAMIC_BOOTP_OK))
1556e853bc5dShenning 				break;
1557e853bc5dShenning 		}
1558e853bc5dShenning 	}
1559e853bc5dShenning 
1560e853bc5dShenning 	/* Try to find a lease that's been allocated to the client's
1561e853bc5dShenning 	   IP address. */
1562e853bc5dShenning 	if (cip.len)
1563e853bc5dShenning 		ip_lease = find_lease_by_ip_addr(cip);
1564e853bc5dShenning 	else
1565ea507cabSderaadt 		ip_lease = NULL;
1566e853bc5dShenning 
1567e853bc5dShenning 	/* If ip_lease is valid at this point, set ours to one, so that
1568e853bc5dShenning 	   even if we choose a different lease, we know that the address
1569e853bc5dShenning 	   the client was requesting was ours, and thus we can NAK it. */
1570e853bc5dShenning 	if (ip_lease && ours)
1571e853bc5dShenning 		*ours = 1;
1572e853bc5dShenning 
1573e853bc5dShenning 	/* If the requested IP address isn't on the network the packet
1574e853bc5dShenning 	   came from, don't use it.  Allow abandoned leases to be matched
1575e853bc5dShenning 	   here - if the client is requesting it, there's a decent chance
1576e853bc5dShenning 	   that it's because the lease database got trashed and a client
1577e853bc5dShenning 	   that thought it had this lease answered an ARP or PING, causing the
1578e853bc5dShenning 	   lease to be abandoned.   If so, this request probably came from
1579e853bc5dShenning 	   that client. */
1580e853bc5dShenning 	if (ip_lease && (ip_lease->shared_network != share)) {
1581ea507cabSderaadt 		ip_lease = NULL;
1582e853bc5dShenning 		strlcpy(dhcp_message, "requested address on bad subnet",
1583e853bc5dShenning 		    sizeof(dhcp_message));
1584e853bc5dShenning 	}
1585e853bc5dShenning 
1586e853bc5dShenning 	/* Toss ip_lease if it hasn't yet expired and isn't owned by the
1587e853bc5dShenning 	   client. */
15888e1c2028Sderaadt 	if (ip_lease && ip_lease->ends >= cur_time && ip_lease != uid_lease) {
1589e853bc5dShenning 		int i = DHO_DHCP_CLIENT_IDENTIFIER;
15908e1c2028Sderaadt 
1591e853bc5dShenning 		/* Make sure that ip_lease actually belongs to the client,
1592e853bc5dShenning 		   and toss it if not. */
15938e1c2028Sderaadt 		if ((ip_lease->uid_len && packet->options[i].data &&
1594e853bc5dShenning 		    ip_lease->uid_len == packet->options[i].len &&
15958e1c2028Sderaadt 		    !memcmp(packet->options[i].data, ip_lease->uid,
15968e1c2028Sderaadt 		    ip_lease->uid_len)) ||
1597e853bc5dShenning 		    (!ip_lease->uid_len &&
15988e1c2028Sderaadt 		    ip_lease->hardware_addr.htype == packet->raw->htype &&
1599e853bc5dShenning 		    ip_lease->hardware_addr.hlen == packet->raw->hlen &&
16008e1c2028Sderaadt 		    !memcmp(ip_lease->hardware_addr.haddr, packet->raw->chaddr,
1601e853bc5dShenning 		    ip_lease->hardware_addr.hlen))) {
1602e853bc5dShenning 			if (uid_lease) {
1603e853bc5dShenning 				if (uid_lease->ends > cur_time) {
160435318e8fSkrw 					log_warnx("client %s has duplicate "
160535318e8fSkrw 					    "leases on %s",
1606e853bc5dShenning 					    print_hw_addr(packet->raw->htype,
160735318e8fSkrw 					    packet->raw->hlen,
160835318e8fSkrw 					    packet->raw->chaddr),
1609e853bc5dShenning 					    ip_lease->shared_network->name);
1610e853bc5dShenning 
161135318e8fSkrw 					if (uid_lease &&
161235318e8fSkrw 					   !packet->raw->ciaddr.s_addr)
1613e853bc5dShenning 						release_lease(uid_lease);
1614e853bc5dShenning 				}
1615e853bc5dShenning 				uid_lease = ip_lease;
1616e853bc5dShenning 			}
1617e853bc5dShenning 		} else {
161835318e8fSkrw 			strlcpy(dhcp_message, "requested address is not "
161935318e8fSkrw 			    "available", sizeof(dhcp_message));
1620ea507cabSderaadt 			ip_lease = NULL;
1621e853bc5dShenning 		}
1622e853bc5dShenning 
1623e853bc5dShenning 		/* If we get to here and fixed_lease is not null, that means
1624e853bc5dShenning 		   that there are both a dynamic lease and a fixed-address
1625e853bc5dShenning 		   declaration for the same IP address. */
1626e853bc5dShenning 		if (packet->packet_type == DHCPREQUEST && fixed_lease) {
1627ea507cabSderaadt 			fixed_lease = NULL;
1628e853bc5dShenning db_conflict:
162935318e8fSkrw 			log_warnx("Both dynamic and static leases present for "
163035318e8fSkrw 			    "%s.", piaddr(cip));
163135318e8fSkrw 			log_warnx("Either remove host declaration %s or "
163235318e8fSkrw 			    "remove %s", (fixed_lease && fixed_lease->host ?
163335318e8fSkrw 			    (fixed_lease->host->name ?
163435318e8fSkrw 			    fixed_lease->host->name : piaddr(cip)) :
163535318e8fSkrw 			    piaddr(cip)), piaddr(cip));
1636c525a185Skrw 			log_warnx("from the dynamic address pool for %s",
1637e853bc5dShenning 			    share->name);
1638e853bc5dShenning 			if (fixed_lease)
1639ea507cabSderaadt 				ip_lease = NULL;
164035318e8fSkrw 			strlcpy(dhcp_message, "database conflict - call for "
164135318e8fSkrw 			    "help!", sizeof(dhcp_message));
1642e853bc5dShenning 		}
1643e853bc5dShenning 	}
1644e853bc5dShenning 
1645e853bc5dShenning 	/* If we get to here with both fixed_lease and ip_lease not
1646e853bc5dShenning 	   null, then we have a configuration file bug. */
1647e853bc5dShenning 	if (packet->packet_type == DHCPREQUEST && fixed_lease && ip_lease)
1648e853bc5dShenning 		goto db_conflict;
1649e853bc5dShenning 
1650e853bc5dShenning 	/* Toss hw_lease if it hasn't yet expired and the uid doesn't
1651e853bc5dShenning 	   match, except that if the hardware address matches and the
1652e853bc5dShenning 	   client is now doing dynamic BOOTP (and thus hasn't provided
1653e853bc5dShenning 	   a uid) we let the client get away with it. */
16548e1c2028Sderaadt 	if (hw_lease && hw_lease->ends >= cur_time && hw_lease->uid &&
1655e853bc5dShenning 	    packet->options[DHO_DHCP_CLIENT_IDENTIFIER].len &&
1656e853bc5dShenning 	    hw_lease != uid_lease)
1657ea507cabSderaadt 		hw_lease = NULL;
1658e853bc5dShenning 
1659e853bc5dShenning 	/* Toss extra pointers to the same lease... */
1660e853bc5dShenning 	if (hw_lease == uid_lease)
1661ea507cabSderaadt 		hw_lease = NULL;
1662e853bc5dShenning 	if (ip_lease == hw_lease)
1663ea507cabSderaadt 		hw_lease = NULL;
1664e853bc5dShenning 	if (ip_lease == uid_lease)
1665ea507cabSderaadt 		uid_lease = NULL;
1666e853bc5dShenning 
1667e853bc5dShenning 	/* If we've already eliminated the lease, it wasn't there to
1668e853bc5dShenning 	   begin with.   If we have come up with a matching lease,
1669e853bc5dShenning 	   set the message to bad network in case we have to throw it out. */
1670e853bc5dShenning 	if (!ip_lease) {
1671e853bc5dShenning 		strlcpy(dhcp_message, "requested address not available",
1672e853bc5dShenning 		    sizeof(dhcp_message));
1673e853bc5dShenning 	}
1674e853bc5dShenning 
1675e853bc5dShenning 	/* Now eliminate leases that are on the wrong network... */
16768e1c2028Sderaadt 	if (ip_lease && share != ip_lease->shared_network) {
1677e853bc5dShenning 		if (packet->packet_type == DHCPREQUEST)
1678e853bc5dShenning 			release_lease(ip_lease);
1679ea507cabSderaadt 		ip_lease = NULL;
1680e853bc5dShenning 	}
16818e1c2028Sderaadt 	if (uid_lease && share != uid_lease->shared_network) {
1682e853bc5dShenning 		if (packet->packet_type == DHCPREQUEST)
1683e853bc5dShenning 			release_lease(uid_lease);
1684ea507cabSderaadt 		uid_lease = NULL;
1685e853bc5dShenning 	}
16868e1c2028Sderaadt 	if (hw_lease && share != hw_lease->shared_network) {
1687e853bc5dShenning 		if (packet->packet_type == DHCPREQUEST)
1688e853bc5dShenning 			release_lease(hw_lease);
1689ea507cabSderaadt 		hw_lease = NULL;
1690e853bc5dShenning 	}
1691e853bc5dShenning 
1692e853bc5dShenning 	/* If this is a DHCPREQUEST, make sure the lease we're going to return
1693e853bc5dShenning 	   matches the requested IP address.   If it doesn't, don't return a
1694e853bc5dShenning 	   lease at all. */
1695e853bc5dShenning 	if (packet->packet_type == DHCPREQUEST && !ip_lease && !fixed_lease)
1696ea507cabSderaadt 		return NULL;
1697e853bc5dShenning 
1698e853bc5dShenning 	/* At this point, if fixed_lease is nonzero, we can assign it to
1699e853bc5dShenning 	   this client. */
17008e1c2028Sderaadt 	if (fixed_lease)
1701e853bc5dShenning 		lease = fixed_lease;
1702e853bc5dShenning 
1703e853bc5dShenning 	/* If we got a lease that matched the ip address and don't have
1704e853bc5dShenning 	   a better offer, use that; otherwise, release it. */
1705e853bc5dShenning 	if (ip_lease) {
1706e853bc5dShenning 		if (lease) {
1707e853bc5dShenning 			if (packet->packet_type == DHCPREQUEST)
1708e853bc5dShenning 				release_lease(ip_lease);
1709e853bc5dShenning 		} else {
1710e853bc5dShenning 			lease = ip_lease;
17118e1c2028Sderaadt 			lease->host = NULL;
1712e853bc5dShenning 		}
1713e853bc5dShenning 	}
1714e853bc5dShenning 
1715e853bc5dShenning 	/* If we got a lease that matched the client identifier, we may want
1716e853bc5dShenning 	   to use it, but if we already have a lease we like, we must free
1717e853bc5dShenning 	   the lease that matched the client identifier. */
1718e853bc5dShenning 	if (uid_lease) {
1719e853bc5dShenning 		if (lease) {
1720e853bc5dShenning 			if (packet->packet_type == DHCPREQUEST)
1721e853bc5dShenning 				release_lease(uid_lease);
1722e853bc5dShenning 		} else {
1723e853bc5dShenning 			lease = uid_lease;
17248e1c2028Sderaadt 			lease->host = NULL;
1725e853bc5dShenning 		}
1726e853bc5dShenning 	}
1727e853bc5dShenning 
1728e853bc5dShenning 	/* The lease that matched the hardware address is treated likewise. */
1729e853bc5dShenning 	if (hw_lease) {
1730e853bc5dShenning 		if (lease) {
1731e853bc5dShenning 			if (packet->packet_type == DHCPREQUEST)
1732e853bc5dShenning 				release_lease(hw_lease);
1733e853bc5dShenning 		} else {
1734e853bc5dShenning 			lease = hw_lease;
17358e1c2028Sderaadt 			lease->host = NULL;
1736e853bc5dShenning 		}
1737e853bc5dShenning 	}
1738e853bc5dShenning 
1739e853bc5dShenning 	/* If we found a host_decl but no matching address, try to
1740e853bc5dShenning 	   find a host_decl that has no address, and if there is one,
1741e853bc5dShenning 	   hang it off the lease so that we can use the supplied
1742e853bc5dShenning 	   options. */
1743e853bc5dShenning 	if (lease && host && !lease->host) {
1744e853bc5dShenning 		for (; host; host = host->n_ipaddr) {
1745e853bc5dShenning 			if (!host->fixed_addr) {
1746e853bc5dShenning 				lease->host = host;
1747e853bc5dShenning 				break;
1748e853bc5dShenning 			}
1749e853bc5dShenning 		}
1750e853bc5dShenning 	}
1751e853bc5dShenning 
1752e853bc5dShenning 	/* If we find an abandoned lease, take it, but print a
1753e853bc5dShenning 	   warning message, so that if it continues to lose,
1754e853bc5dShenning 	   the administrator will eventually investigate. */
1755e853bc5dShenning 	if (lease && (lease->flags & ABANDONED_LEASE)) {
1756e853bc5dShenning 		if (packet->packet_type == DHCPREQUEST) {
175735318e8fSkrw 			log_warnx("Reclaiming REQUESTed abandoned IP address "
175835318e8fSkrw 			    "%s.", piaddr(lease->ip_addr));
1759e853bc5dShenning 			lease->flags &= ~ABANDONED_LEASE;
1760e853bc5dShenning 		} else
1761ea507cabSderaadt 			lease = NULL;
1762e853bc5dShenning 	}
1763e853bc5dShenning 	return lease;
1764e853bc5dShenning }
1765e853bc5dShenning 
1766ea507cabSderaadt /*
1767ea507cabSderaadt  * Search the provided host_decl structure list for an address that's on
1768ea507cabSderaadt  * the specified shared network.  If one is found, mock up and return a
1769ea507cabSderaadt  * lease structure for it; otherwise return the null pointer.
1770ea507cabSderaadt  */
1771ea507cabSderaadt struct lease *
mockup_lease(struct packet * packet,struct shared_network * share,struct host_decl * hp)1772ea507cabSderaadt mockup_lease(struct packet *packet, struct shared_network *share,
1773ea507cabSderaadt     struct host_decl *hp)
1774e853bc5dShenning {
1775e853bc5dShenning 	static struct lease mock;
1776e853bc5dShenning 
1777e853bc5dShenning 	mock.subnet = find_host_for_network(&hp, &mock.ip_addr, share);
1778e853bc5dShenning 	if (!mock.subnet)
1779ea507cabSderaadt 		return (NULL);
1780ea507cabSderaadt 	mock.next = mock.prev = NULL;
1781e853bc5dShenning 	mock.shared_network = mock.subnet->shared_network;
1782e853bc5dShenning 	mock.host = hp;
1783e853bc5dShenning 
1784e853bc5dShenning 	if (hp->group->options[DHO_DHCP_CLIENT_IDENTIFIER]) {
178535318e8fSkrw 		mock.uid =
178635318e8fSkrw 		    hp->group->options[DHO_DHCP_CLIENT_IDENTIFIER]->value;
178735318e8fSkrw 		mock.uid_len =
178835318e8fSkrw 		    hp->group->options[DHO_DHCP_CLIENT_IDENTIFIER]->len;
1789e853bc5dShenning 	} else {
1790ea507cabSderaadt 		mock.uid = NULL;
1791e853bc5dShenning 		mock.uid_len = 0;
1792e853bc5dShenning 	}
1793e853bc5dShenning 
1794e853bc5dShenning 	mock.hardware_addr = hp->interface;
1795e853bc5dShenning 	mock.starts = mock.timestamp = mock.ends = MIN_TIME;
1796e853bc5dShenning 	mock.flags = STATIC_LEASE;
1797e853bc5dShenning 	return &mock;
1798e853bc5dShenning }
1799