xref: /openbsd/usr.sbin/dhcpd/bootp.c (revision 09467b48)
1 /*	$OpenBSD: bootp.c,v 1.18 2017/02/13 22:33:39 krw Exp $	*/
2 
3 /*
4  * BOOTP Protocol support.
5  */
6 
7 /*
8  * Copyright (c) 1995, 1996, 1998, 1999 The Internet Software Consortium.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of The Internet Software Consortium nor the names
21  *    of its contributors may be used to endorse or promote products derived
22  *    from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
25  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
26  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
29  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
32  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
33  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
35  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  * This software has been written for the Internet Software Consortium
39  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
40  * Enterprises.  To learn more about the Internet Software Consortium,
41  * see ``http://www.vix.com/isc''.  To learn more about Vixie
42  * Enterprises, see ``http://www.vix.com''.
43  */
44 
45 #include <sys/socket.h>
46 
47 #include <arpa/inet.h>
48 
49 #include <net/if.h>
50 
51 #include <netinet/in.h>
52 
53 #include <errno.h>
54 #include <stdio.h>
55 #include <string.h>
56 
57 #include "dhcp.h"
58 #include "tree.h"
59 #include "dhcpd.h"
60 #include "log.h"
61 
62 void
63 bootp(struct packet *packet)
64 {
65 	struct host_decl *hp, *host = NULL;
66 	struct packet outgoing;
67 	struct dhcp_packet raw;
68 	struct sockaddr_in to;
69 	struct in_addr from;
70 	struct tree_cache *options[256];
71 	struct shared_network *s;
72 	struct subnet *subnet = NULL;
73 	struct lease *lease;
74 	struct iaddr ip_address;
75 	int i;
76 
77 	if (packet->raw->op != BOOTREQUEST)
78 		return;
79 
80 	log_info("BOOTREQUEST from %s via %s%s",
81 	    print_hw_addr(packet->raw->htype, packet->raw->hlen,
82 	    packet->raw->chaddr), packet->raw->giaddr.s_addr ?
83 	    inet_ntoa(packet->raw->giaddr) : packet->interface->name,
84 	    packet->options_valid ? "" : " (non-rfc1048)");
85 
86 	if (!locate_network(packet))
87 		return;
88 
89 	hp = find_hosts_by_haddr(packet->raw->htype, packet->raw->chaddr,
90 	    packet->raw->hlen);
91 
92 	s = packet->shared_network;
93 	lease = find_lease(packet, s, 0);
94 
95 	/*
96 	 * Find an IP address in the host_decl that matches the specified
97 	 * network.
98 	 */
99 	if (hp)
100 		subnet = find_host_for_network(&hp, &ip_address, s);
101 
102 	if (!subnet) {
103 		/*
104 		 * We didn't find an applicable host declaration. Just in case
105 		 * we may be able to dynamically assign an address, see if
106 		 * there's a host declaration that doesn't have an ip address
107 		 * associated with it.
108 		 */
109 		if (hp)
110 			for (; hp; hp = hp->n_ipaddr)
111 				if (!hp->fixed_addr) {
112 					host = hp;
113 					break;
114 				}
115 
116 		if (host && (!host->group->allow_booting)) {
117 			log_info("Ignoring excluded BOOTP client %s",
118 			    host->name ?  host->name :
119 			    print_hw_addr (packet->raw->htype,
120 			    packet->raw->hlen, packet->raw->chaddr));
121 			return;
122 		}
123 
124 		if (host && (!host->group->allow_bootp)) {
125 			log_info("Ignoring BOOTP request from client %s",
126 			    host->name ? host->name :
127 			    print_hw_addr(packet->raw->htype,
128 			    packet->raw->hlen, packet->raw->chaddr));
129 			return;
130 		}
131 
132 		/*
133 		 * If we've been told not to boot unknown clients, and we
134 		 * didn't find any host record for this client, ignore it.
135 		 */
136 		if (!host && !(s->group->boot_unknown_clients)) {
137 			log_info("Ignoring unknown BOOTP client %s via %s",
138 			    print_hw_addr(packet->raw->htype,
139 			    packet->raw->hlen, packet->raw->chaddr),
140 			    packet->raw->giaddr.s_addr ?
141 			    inet_ntoa(packet->raw->giaddr) :
142 			    packet->interface->name);
143 			return;
144 		}
145 
146 		/*
147 		 * If we've been told not to boot with bootp on this network,
148 		 * ignore it.
149 		 */
150 		if (!host && !(s->group->allow_bootp)) {
151 			log_info("Ignoring BOOTP request from client %s via "
152 			    "%s", print_hw_addr(packet->raw->htype,
153 			    packet->raw->hlen, packet->raw->chaddr),
154 			    packet->raw->giaddr.s_addr ?
155 			    inet_ntoa(packet->raw->giaddr) :
156 			    packet->interface->name);
157 			return;
158 		}
159 
160 		/*
161 		 * If the packet is from a host we don't know and there are no
162 		 * dynamic bootp addresses on the network it came in on, drop
163 		 * it on the floor.
164 		 */
165 		if (!(s->group->dynamic_bootp)) {
166 lose:
167 			log_info("No applicable record for BOOTP host %s via "
168 			    "%s", print_hw_addr(packet->raw->htype,
169 			    packet->raw->hlen, packet->raw->chaddr),
170 			    packet->raw->giaddr.s_addr ?
171 			    inet_ntoa(packet->raw->giaddr) :
172 			    packet->interface->name);
173 			return;
174 		}
175 
176 		/*
177 		 * If a lease has already been assigned to this client and it's
178 		 * still okay to use dynamic bootp on that lease, reassign it.
179 		 */
180 		if (lease) {
181 			/*
182 			 * If this lease can be used for dynamic bootp, do so.
183 			 */
184 			if ((lease->flags & DYNAMIC_BOOTP_OK)) {
185 				/*
186 				 * If it's not a DYNAMIC_BOOTP lease, release
187 				 * it before reassigning it so that we don't
188 				 * get a lease conflict.
189 				 */
190 				if (!(lease->flags & BOOTP_LEASE))
191 					release_lease(lease);
192 
193 				lease->host = host;
194 				ack_lease(packet, lease, 0, 0);
195 				return;
196 			}
197 
198 			 /*
199 			  * If dynamic BOOTP is no longer allowed for this
200 			  * lease, set it free.
201 			  */
202 			release_lease(lease);
203 		}
204 
205 		/*
206 		 * If there are dynamic bootp addresses that might be
207 		 * available, try to snag one.
208 		 */
209 		for (lease = s->last_lease;
210 		    lease && lease->ends <= cur_time;
211 		    lease = lease->prev) {
212 			if ((lease->flags & DYNAMIC_BOOTP_OK)) {
213 				lease->host = host;
214 				ack_lease(packet, lease, 0, 0);
215 				return;
216 			}
217 		}
218 		goto lose;
219 	}
220 
221 	/* Make sure we're allowed to boot this client. */
222 	if (hp && (!hp->group->allow_booting)) {
223 		log_info("Ignoring excluded BOOTP client %s", hp->name);
224 		return;
225 	}
226 
227 	/* Make sure we're allowed to boot this client with bootp. */
228 	if (hp && (!hp->group->allow_bootp)) {
229 		log_info("Ignoring BOOTP request from client %s", hp->name);
230 		return;
231 	}
232 
233 	/* Set up the outgoing packet... */
234 	memset(&outgoing, 0, sizeof outgoing);
235 	memset(&raw, 0, sizeof raw);
236 	outgoing.raw = &raw;
237 
238 	/*
239 	 * If we didn't get a known vendor magic number on the way in, just
240 	 * copy the input options to the output.
241 	 */
242 	if (!packet->options_valid && !subnet->group->always_reply_rfc1048 &&
243 	    (!hp || !hp->group->always_reply_rfc1048)) {
244 		memcpy(outgoing.raw->options, packet->raw->options,
245 		    DHCP_OPTION_LEN);
246 		outgoing.packet_length = BOOTP_MIN_LEN;
247 	} else {
248 		struct tree_cache netmask_tree;   /*  -- RBF */
249 
250 		/*
251 		 * Come up with a list of options that we want to send to this
252 		 * client. Start with the per-subnet options, and then override
253 		 * those with client-specific options.
254 		 */
255 
256 		memcpy(options, subnet->group->options, sizeof(options));
257 
258 		for (i = 0; i < 256; i++)
259 			if (hp->group->options[i])
260 				options[i] = hp->group->options[i];
261 
262 		/*
263 		 * Use the subnet mask from the subnet declaration if no other
264 		 * mask has been provided.
265 		 */
266 		if (!options[DHO_SUBNET_MASK]) {
267 			options[DHO_SUBNET_MASK] = &netmask_tree;
268 			netmask_tree.flags = TC_TEMPORARY;
269 			netmask_tree.value = lease->subnet->netmask.iabuf;
270 			netmask_tree.len = lease->subnet->netmask.len;
271 			netmask_tree.buf_size = lease->subnet->netmask.len;
272 			netmask_tree.timeout = -1;
273 			netmask_tree.tree = NULL;
274 		}
275 
276 		/*
277 		 * Pack the options into the buffer. Unlike DHCP, we can't pack
278 		 * options into the filename and server name buffers.
279 		 */
280 
281 		outgoing.packet_length = cons_options(packet, outgoing.raw,
282 		    0, options, 0, 0, 1, NULL, 0);
283 
284 		if (outgoing.packet_length < BOOTP_MIN_LEN)
285 			outgoing.packet_length = BOOTP_MIN_LEN;
286 	}
287 
288 	/* Take the fields that we care about... */
289 	raw.op = BOOTREPLY;
290 	raw.htype = packet->raw->htype;
291 	raw.hlen = packet->raw->hlen;
292 	memcpy(raw.chaddr, packet->raw->chaddr, sizeof(raw.chaddr));
293 	raw.hops = packet->raw->hops;
294 	raw.xid = packet->raw->xid;
295 	raw.secs = packet->raw->secs;
296 	raw.flags = packet->raw->flags;
297 	raw.ciaddr = packet->raw->ciaddr;
298 	memcpy(&raw.yiaddr, ip_address.iabuf, sizeof(raw.yiaddr));
299 
300 	/* Figure out the address of the next server. */
301 	if (hp && hp->group->next_server.len)
302 		memcpy(&raw.siaddr, hp->group->next_server.iabuf, 4);
303 	else if (subnet->group->next_server.len)
304 		memcpy(&raw.siaddr, subnet->group->next_server.iabuf, 4);
305 	else if (subnet->interface_address.len)
306 		memcpy(&raw.siaddr, subnet->interface_address.iabuf, 4);
307 	else
308 		raw.siaddr = packet->interface->primary_address;
309 
310 	raw.giaddr = packet->raw->giaddr;
311 	if (hp->group->server_name)
312 		strncpy(raw.sname, hp->group->server_name, sizeof(raw.sname));
313 	else if (subnet->group->server_name)
314 		strncpy(raw.sname, subnet->group->server_name,
315 		    sizeof(raw.sname));
316 
317 	if (hp->group->filename)
318 		strncpy(raw.file, hp->group->filename, sizeof(raw.file));
319 	else if (subnet->group->filename)
320 		strncpy(raw.file, subnet->group->filename, sizeof(raw.file));
321 	else
322 		memcpy(raw.file, packet->raw->file, sizeof(raw.file));
323 
324 	from = packet->interface->primary_address;
325 
326 	/* Report what we're doing... */
327 	log_info("BOOTREPLY for %s to %s (%s) via %s", piaddr(ip_address),
328 	    hp->name, print_hw_addr(packet->raw->htype, packet->raw->hlen,
329 	    packet->raw->chaddr), packet->raw->giaddr.s_addr ?
330 	    inet_ntoa(packet->raw->giaddr) : packet->interface->name);
331 
332 	/* Set up the parts of the address that are in common. */
333 	memset(&to, 0, sizeof(to));
334 	to.sin_family = AF_INET;
335 #ifdef HAVE_SA_LEN
336 	to.sin_len = sizeof(to);
337 #endif
338 
339 	/* If this was gatewayed, send it back to the gateway... */
340 	if (raw.giaddr.s_addr) {
341 		to.sin_addr = raw.giaddr;
342 		to.sin_port = server_port;
343 
344 		(void) packet->interface->send_packet(packet->interface, &raw,
345 		    outgoing.packet_length, from, &to, packet->haddr);
346 		return;
347 	}
348 
349 	/*
350 	 * If it comes from a client that already knows its address and is not
351 	 * requesting a broadcast response, and we can unicast to a client
352 	 * without using the ARP protocol, sent it directly to that client.
353 	 */
354 	else if (!(raw.flags & htons(BOOTP_BROADCAST))) {
355 		to.sin_addr = raw.yiaddr;
356 		to.sin_port = client_port;
357 	} else {
358 		/* Otherwise, broadcast it on the local network. */
359 		to.sin_addr.s_addr = INADDR_BROADCAST;
360 		to.sin_port = client_port; /* XXX */
361 	}
362 
363 	errno = 0;
364 	(void) packet->interface->send_packet(packet->interface, &raw,
365 	    outgoing.packet_length, from, &to, packet->haddr);
366 }
367