xref: /openbsd-src/usr.sbin/dhcpd/bootp.c (revision 35318e8fe7d95f005335d5d2b39414681f271ddb)
1*35318e8fSkrw /*	$OpenBSD: bootp.c,v 1.18 2017/02/13 22:33:39 krw Exp $	*/
2e853bc5dShenning 
38052c184Shenning /*
48052c184Shenning  * BOOTP Protocol support.
58052c184Shenning  */
6e853bc5dShenning 
7e853bc5dShenning /*
8e853bc5dShenning  * Copyright (c) 1995, 1996, 1998, 1999 The Internet Software Consortium.
9e853bc5dShenning  * All rights reserved.
10e853bc5dShenning  *
11e853bc5dShenning  * Redistribution and use in source and binary forms, with or without
12e853bc5dShenning  * modification, are permitted provided that the following conditions
13e853bc5dShenning  * are met:
14e853bc5dShenning  *
15e853bc5dShenning  * 1. Redistributions of source code must retain the above copyright
16e853bc5dShenning  *    notice, this list of conditions and the following disclaimer.
17e853bc5dShenning  * 2. Redistributions in binary form must reproduce the above copyright
18e853bc5dShenning  *    notice, this list of conditions and the following disclaimer in the
19e853bc5dShenning  *    documentation and/or other materials provided with the distribution.
20e853bc5dShenning  * 3. Neither the name of The Internet Software Consortium nor the names
21e853bc5dShenning  *    of its contributors may be used to endorse or promote products derived
22e853bc5dShenning  *    from this software without specific prior written permission.
23e853bc5dShenning  *
24e853bc5dShenning  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
25e853bc5dShenning  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
26e853bc5dShenning  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27e853bc5dShenning  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28e853bc5dShenning  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
29e853bc5dShenning  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30e853bc5dShenning  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31e853bc5dShenning  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
32e853bc5dShenning  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
33e853bc5dShenning  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34e853bc5dShenning  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
35e853bc5dShenning  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36e853bc5dShenning  * SUCH DAMAGE.
37e853bc5dShenning  *
38e853bc5dShenning  * This software has been written for the Internet Software Consortium
39e853bc5dShenning  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
40e853bc5dShenning  * Enterprises.  To learn more about the Internet Software Consortium,
41e853bc5dShenning  * see ``http://www.vix.com/isc''.  To learn more about Vixie
42e853bc5dShenning  * Enterprises, see ``http://www.vix.com''.
43e853bc5dShenning  */
44e853bc5dShenning 
45837cddffSkrw #include <sys/socket.h>
46837cddffSkrw 
47837cddffSkrw #include <arpa/inet.h>
48837cddffSkrw 
49837cddffSkrw #include <net/if.h>
50837cddffSkrw 
51837cddffSkrw #include <netinet/in.h>
52837cddffSkrw 
53837cddffSkrw #include <errno.h>
54837cddffSkrw #include <stdio.h>
55837cddffSkrw #include <string.h>
56837cddffSkrw 
57837cddffSkrw #include "dhcp.h"
58837cddffSkrw #include "tree.h"
59e853bc5dShenning #include "dhcpd.h"
60c525a185Skrw #include "log.h"
61e853bc5dShenning 
628052c184Shenning void
bootp(struct packet * packet)638052c184Shenning bootp(struct packet *packet)
64e853bc5dShenning {
658e1c2028Sderaadt 	struct host_decl *hp, *host = NULL;
66e853bc5dShenning 	struct packet outgoing;
67e853bc5dShenning 	struct dhcp_packet raw;
68e853bc5dShenning 	struct sockaddr_in to;
69e853bc5dShenning 	struct in_addr from;
70e853bc5dShenning 	struct tree_cache *options[256];
71fbd070faSpelikan 	struct shared_network *s;
728e1c2028Sderaadt 	struct subnet *subnet = NULL;
73e853bc5dShenning 	struct lease *lease;
74e853bc5dShenning 	struct iaddr ip_address;
750795b389Sderaadt 	int i;
76e853bc5dShenning 
77e853bc5dShenning 	if (packet->raw->op != BOOTREQUEST)
78e853bc5dShenning 		return;
79e853bc5dShenning 
80*35318e8fSkrw 	log_info("BOOTREQUEST from %s via %s%s",
81*35318e8fSkrw 	    print_hw_addr(packet->raw->htype, packet->raw->hlen,
82*35318e8fSkrw 	    packet->raw->chaddr), packet->raw->giaddr.s_addr ?
83*35318e8fSkrw 	    inet_ntoa(packet->raw->giaddr) : packet->interface->name,
84fe8711f0Sderaadt 	    packet->options_valid ? "" : " (non-rfc1048)");
85e853bc5dShenning 
86e853bc5dShenning 	if (!locate_network(packet))
87e853bc5dShenning 		return;
88e853bc5dShenning 
898052c184Shenning 	hp = find_hosts_by_haddr(packet->raw->htype, packet->raw->chaddr,
90e853bc5dShenning 	    packet->raw->hlen);
91e853bc5dShenning 
92fbd070faSpelikan 	s = packet->shared_network;
93fbd070faSpelikan 	lease = find_lease(packet, s, 0);
94e853bc5dShenning 
958052c184Shenning 	/*
968052c184Shenning 	 * Find an IP address in the host_decl that matches the specified
978052c184Shenning 	 * network.
988052c184Shenning 	 */
99e853bc5dShenning 	if (hp)
100fbd070faSpelikan 		subnet = find_host_for_network(&hp, &ip_address, s);
101e853bc5dShenning 
102e853bc5dShenning 	if (!subnet) {
1038052c184Shenning 		/*
1048052c184Shenning 		 * We didn't find an applicable host declaration. Just in case
1058052c184Shenning 		 * we may be able to dynamically assign an address, see if
1068052c184Shenning 		 * there's a host declaration that doesn't have an ip address
1078052c184Shenning 		 * associated with it.
1088052c184Shenning 		 */
1098052c184Shenning 		if (hp)
1108052c184Shenning 			for (; hp; hp = hp->n_ipaddr)
111e853bc5dShenning 				if (!hp->fixed_addr) {
112e853bc5dShenning 					host = hp;
113e853bc5dShenning 					break;
114e853bc5dShenning 				}
115e853bc5dShenning 
116e853bc5dShenning 		if (host && (!host->group->allow_booting)) {
117*35318e8fSkrw 			log_info("Ignoring excluded BOOTP client %s",
118*35318e8fSkrw 			    host->name ?  host->name :
119*35318e8fSkrw 			    print_hw_addr (packet->raw->htype,
1208052c184Shenning 			    packet->raw->hlen, packet->raw->chaddr));
121e853bc5dShenning 			return;
122e853bc5dShenning 		}
123e853bc5dShenning 
124e853bc5dShenning 		if (host && (!host->group->allow_bootp)) {
125c525a185Skrw 			log_info("Ignoring BOOTP request from client %s",
1268052c184Shenning 			    host->name ? host->name :
1278052c184Shenning 			    print_hw_addr(packet->raw->htype,
1288052c184Shenning 			    packet->raw->hlen, packet->raw->chaddr));
129e853bc5dShenning 			return;
130e853bc5dShenning 		}
131e853bc5dShenning 
1328052c184Shenning 		/*
133*35318e8fSkrw 		 * If we've been told not to boot unknown clients, and we
134*35318e8fSkrw 		 * didn't find any host record for this client, ignore it.
1358052c184Shenning 		 */
136fbd070faSpelikan 		if (!host && !(s->group->boot_unknown_clients)) {
137c525a185Skrw 			log_info("Ignoring unknown BOOTP client %s via %s",
138e853bc5dShenning 			    print_hw_addr(packet->raw->htype,
1398052c184Shenning 			    packet->raw->hlen, packet->raw->chaddr),
1408052c184Shenning 			    packet->raw->giaddr.s_addr ?
1418052c184Shenning 			    inet_ntoa(packet->raw->giaddr) :
1428052c184Shenning 			    packet->interface->name);
143e853bc5dShenning 			return;
144e853bc5dShenning 		}
145e853bc5dShenning 
1468052c184Shenning 		/*
1478052c184Shenning 		 * If we've been told not to boot with bootp on this network,
1488052c184Shenning 		 * ignore it.
1498052c184Shenning 		 */
150fbd070faSpelikan 		if (!host && !(s->group->allow_bootp)) {
151*35318e8fSkrw 			log_info("Ignoring BOOTP request from client %s via "
152*35318e8fSkrw 			    "%s", print_hw_addr(packet->raw->htype,
1538052c184Shenning 			    packet->raw->hlen, packet->raw->chaddr),
1548052c184Shenning 			    packet->raw->giaddr.s_addr ?
1558052c184Shenning 			    inet_ntoa(packet->raw->giaddr) :
1568052c184Shenning 			    packet->interface->name);
157e853bc5dShenning 			return;
158e853bc5dShenning 		}
159e853bc5dShenning 
1608052c184Shenning 		/*
1618052c184Shenning 		 * If the packet is from a host we don't know and there are no
1628052c184Shenning 		 * dynamic bootp addresses on the network it came in on, drop
1638052c184Shenning 		 * it on the floor.
1648052c184Shenning 		 */
165fbd070faSpelikan 		if (!(s->group->dynamic_bootp)) {
166e853bc5dShenning lose:
167*35318e8fSkrw 			log_info("No applicable record for BOOTP host %s via "
168*35318e8fSkrw 			    "%s", print_hw_addr(packet->raw->htype,
1698052c184Shenning 			    packet->raw->hlen, packet->raw->chaddr),
1708052c184Shenning 			    packet->raw->giaddr.s_addr ?
1718052c184Shenning 			    inet_ntoa(packet->raw->giaddr) :
1728052c184Shenning 			    packet->interface->name);
173e853bc5dShenning 			return;
174e853bc5dShenning 		}
175e853bc5dShenning 
1768052c184Shenning 		/*
1778052c184Shenning 		 * If a lease has already been assigned to this client and it's
1788052c184Shenning 		 * still okay to use dynamic bootp on that lease, reassign it.
1798052c184Shenning 		 */
180e853bc5dShenning 		if (lease) {
1818052c184Shenning 			/*
1828052c184Shenning 			 * If this lease can be used for dynamic bootp, do so.
1838052c184Shenning 			 */
184e853bc5dShenning 			if ((lease->flags & DYNAMIC_BOOTP_OK)) {
1858052c184Shenning 				/*
186*35318e8fSkrw 				 * If it's not a DYNAMIC_BOOTP lease, release
187*35318e8fSkrw 				 * it before reassigning it so that we don't
188*35318e8fSkrw 				 * get a lease conflict.
1898052c184Shenning 				 */
190e853bc5dShenning 				if (!(lease->flags & BOOTP_LEASE))
191e853bc5dShenning 					release_lease(lease);
192e853bc5dShenning 
193e853bc5dShenning 				lease->host = host;
194e853bc5dShenning 				ack_lease(packet, lease, 0, 0);
195e853bc5dShenning 				return;
196e853bc5dShenning 			}
197e853bc5dShenning 
1988052c184Shenning 			 /*
1998052c184Shenning 			  * If dynamic BOOTP is no longer allowed for this
2008052c184Shenning 			  * lease, set it free.
2018052c184Shenning 			  */
202e853bc5dShenning 			release_lease(lease);
203e853bc5dShenning 		}
204e853bc5dShenning 
2058052c184Shenning 		/*
2068052c184Shenning 		 * If there are dynamic bootp addresses that might be
2078052c184Shenning 		 * available, try to snag one.
2088052c184Shenning 		 */
209fbd070faSpelikan 		for (lease = s->last_lease;
210e853bc5dShenning 		    lease && lease->ends <= cur_time;
211e853bc5dShenning 		    lease = lease->prev) {
212e853bc5dShenning 			if ((lease->flags & DYNAMIC_BOOTP_OK)) {
213e853bc5dShenning 				lease->host = host;
214e853bc5dShenning 				ack_lease(packet, lease, 0, 0);
215e853bc5dShenning 				return;
216e853bc5dShenning 			}
217e853bc5dShenning 		}
218e853bc5dShenning 		goto lose;
219e853bc5dShenning 	}
220e853bc5dShenning 
221e853bc5dShenning 	/* Make sure we're allowed to boot this client. */
222e853bc5dShenning 	if (hp && (!hp->group->allow_booting)) {
223c525a185Skrw 		log_info("Ignoring excluded BOOTP client %s", hp->name);
224e853bc5dShenning 		return;
225e853bc5dShenning 	}
226e853bc5dShenning 
227e853bc5dShenning 	/* Make sure we're allowed to boot this client with bootp. */
228e853bc5dShenning 	if (hp && (!hp->group->allow_bootp)) {
229c525a185Skrw 		log_info("Ignoring BOOTP request from client %s", hp->name);
230e853bc5dShenning 		return;
231e853bc5dShenning 	}
232e853bc5dShenning 
233e853bc5dShenning 	/* Set up the outgoing packet... */
234e853bc5dShenning 	memset(&outgoing, 0, sizeof outgoing);
235e853bc5dShenning 	memset(&raw, 0, sizeof raw);
236e853bc5dShenning 	outgoing.raw = &raw;
237e853bc5dShenning 
2388052c184Shenning 	/*
2398052c184Shenning 	 * If we didn't get a known vendor magic number on the way in, just
2408052c184Shenning 	 * copy the input options to the output.
2418052c184Shenning 	 */
2428052c184Shenning 	if (!packet->options_valid && !subnet->group->always_reply_rfc1048 &&
243e853bc5dShenning 	    (!hp || !hp->group->always_reply_rfc1048)) {
2448052c184Shenning 		memcpy(outgoing.raw->options, packet->raw->options,
2458052c184Shenning 		    DHCP_OPTION_LEN);
246e853bc5dShenning 		outgoing.packet_length = BOOTP_MIN_LEN;
247e853bc5dShenning 	} else {
248e853bc5dShenning 		struct tree_cache netmask_tree;   /*  -- RBF */
249e853bc5dShenning 
2508052c184Shenning 		/*
2518052c184Shenning 		 * Come up with a list of options that we want to send to this
2528052c184Shenning 		 * client. Start with the per-subnet options, and then override
2538052c184Shenning 		 * those with client-specific options.
2548052c184Shenning 		 */
255e853bc5dShenning 
2568052c184Shenning 		memcpy(options, subnet->group->options, sizeof(options));
257e853bc5dShenning 
2588052c184Shenning 		for (i = 0; i < 256; i++)
259e853bc5dShenning 			if (hp->group->options[i])
260e853bc5dShenning 				options[i] = hp->group->options[i];
261e853bc5dShenning 
2628052c184Shenning 		/*
2638052c184Shenning 		 * Use the subnet mask from the subnet declaration if no other
2648052c184Shenning 		 * mask has been provided.
2658052c184Shenning 		 */
266e853bc5dShenning 		if (!options[DHO_SUBNET_MASK]) {
267e853bc5dShenning 			options[DHO_SUBNET_MASK] = &netmask_tree;
268e853bc5dShenning 			netmask_tree.flags = TC_TEMPORARY;
269e853bc5dShenning 			netmask_tree.value = lease->subnet->netmask.iabuf;
270e853bc5dShenning 			netmask_tree.len = lease->subnet->netmask.len;
271e853bc5dShenning 			netmask_tree.buf_size = lease->subnet->netmask.len;
2725dee4edfSotto 			netmask_tree.timeout = -1;
2738e1c2028Sderaadt 			netmask_tree.tree = NULL;
274e853bc5dShenning 		}
275e853bc5dShenning 
2768052c184Shenning 		/*
2778052c184Shenning 		 * Pack the options into the buffer. Unlike DHCP, we can't pack
2788052c184Shenning 		 * options into the filename and server name buffers.
2798052c184Shenning 		 */
280e853bc5dShenning 
2818052c184Shenning 		outgoing.packet_length = cons_options(packet, outgoing.raw,
2828e1c2028Sderaadt 		    0, options, 0, 0, 1, NULL, 0);
2838052c184Shenning 
284e853bc5dShenning 		if (outgoing.packet_length < BOOTP_MIN_LEN)
285e853bc5dShenning 			outgoing.packet_length = BOOTP_MIN_LEN;
286e853bc5dShenning 	}
287e853bc5dShenning 
288e853bc5dShenning 	/* Take the fields that we care about... */
289e853bc5dShenning 	raw.op = BOOTREPLY;
290e853bc5dShenning 	raw.htype = packet->raw->htype;
291e853bc5dShenning 	raw.hlen = packet->raw->hlen;
2928052c184Shenning 	memcpy(raw.chaddr, packet->raw->chaddr, sizeof(raw.chaddr));
293e853bc5dShenning 	raw.hops = packet->raw->hops;
294e853bc5dShenning 	raw.xid = packet->raw->xid;
295e853bc5dShenning 	raw.secs = packet->raw->secs;
296e853bc5dShenning 	raw.flags = packet->raw->flags;
297e853bc5dShenning 	raw.ciaddr = packet->raw->ciaddr;
2988052c184Shenning 	memcpy(&raw.yiaddr, ip_address.iabuf, sizeof(raw.yiaddr));
299e853bc5dShenning 
300e853bc5dShenning 	/* Figure out the address of the next server. */
301e853bc5dShenning 	if (hp && hp->group->next_server.len)
302e853bc5dShenning 		memcpy(&raw.siaddr, hp->group->next_server.iabuf, 4);
303e853bc5dShenning 	else if (subnet->group->next_server.len)
304e853bc5dShenning 		memcpy(&raw.siaddr, subnet->group->next_server.iabuf, 4);
305e853bc5dShenning 	else if (subnet->interface_address.len)
306e853bc5dShenning 		memcpy(&raw.siaddr, subnet->interface_address.iabuf, 4);
307e853bc5dShenning 	else
308e853bc5dShenning 		raw.siaddr = packet->interface->primary_address;
309e853bc5dShenning 
310e853bc5dShenning 	raw.giaddr = packet->raw->giaddr;
311e853bc5dShenning 	if (hp->group->server_name)
3128052c184Shenning 		strncpy(raw.sname, hp->group->server_name, sizeof(raw.sname));
313e853bc5dShenning 	else if (subnet->group->server_name)
314e853bc5dShenning 		strncpy(raw.sname, subnet->group->server_name,
3158052c184Shenning 		    sizeof(raw.sname));
316e853bc5dShenning 
317e853bc5dShenning 	if (hp->group->filename)
3188052c184Shenning 		strncpy(raw.file, hp->group->filename, sizeof(raw.file));
319e853bc5dShenning 	else if (subnet->group->filename)
3208052c184Shenning 		strncpy(raw.file, subnet->group->filename, sizeof(raw.file));
321e853bc5dShenning 	else
3228052c184Shenning 		memcpy(raw.file, packet->raw->file, sizeof(raw.file));
323e853bc5dShenning 
324e853bc5dShenning 	from = packet->interface->primary_address;
325e853bc5dShenning 
326e853bc5dShenning 	/* Report what we're doing... */
327c525a185Skrw 	log_info("BOOTREPLY for %s to %s (%s) via %s", piaddr(ip_address),
3288052c184Shenning 	    hp->name, print_hw_addr(packet->raw->htype, packet->raw->hlen,
3298052c184Shenning 	    packet->raw->chaddr), packet->raw->giaddr.s_addr ?
3308052c184Shenning 	    inet_ntoa(packet->raw->giaddr) : packet->interface->name);
331e853bc5dShenning 
332e853bc5dShenning 	/* Set up the parts of the address that are in common. */
3338052c184Shenning 	memset(&to, 0, sizeof(to));
334e853bc5dShenning 	to.sin_family = AF_INET;
335e853bc5dShenning #ifdef HAVE_SA_LEN
3368052c184Shenning 	to.sin_len = sizeof(to);
337e853bc5dShenning #endif
338e853bc5dShenning 
339e853bc5dShenning 	/* If this was gatewayed, send it back to the gateway... */
340e853bc5dShenning 	if (raw.giaddr.s_addr) {
341e853bc5dShenning 		to.sin_addr = raw.giaddr;
342390956b7Scanacar 		to.sin_port = server_port;
343e853bc5dShenning 
34484d8c049Syasuoka 		(void) packet->interface->send_packet(packet->interface, &raw,
345cc3e8043Sclaudio 		    outgoing.packet_length, from, &to, packet->haddr);
346e853bc5dShenning 		return;
347e853bc5dShenning 	}
348e853bc5dShenning 
3498052c184Shenning 	/*
3508052c184Shenning 	 * If it comes from a client that already knows its address and is not
3518052c184Shenning 	 * requesting a broadcast response, and we can unicast to a client
3528052c184Shenning 	 * without using the ARP protocol, sent it directly to that client.
3538052c184Shenning 	 */
3548052c184Shenning 	else if (!(raw.flags & htons(BOOTP_BROADCAST))) {
355e853bc5dShenning 		to.sin_addr = raw.yiaddr;
356390956b7Scanacar 		to.sin_port = client_port;
357e853bc5dShenning 	} else {
3588e1c2028Sderaadt 		/* Otherwise, broadcast it on the local network. */
359e853bc5dShenning 		to.sin_addr.s_addr = INADDR_BROADCAST;
360390956b7Scanacar 		to.sin_port = client_port; /* XXX */
361e853bc5dShenning 	}
362e853bc5dShenning 
363e853bc5dShenning 	errno = 0;
36484d8c049Syasuoka 	(void) packet->interface->send_packet(packet->interface, &raw,
365cc55341fSclaudio 	    outgoing.packet_length, from, &to, packet->haddr);
366e853bc5dShenning }
367