xref: /openbsd/usr.sbin/dhcpd/bootp.c (revision 404b540a)
1 /*	$OpenBSD: bootp.c,v 1.13 2006/03/13 19:57:42 otto 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 "dhcpd.h"
46 
47 void
48 bootp(struct packet *packet)
49 {
50 	struct host_decl *hp, *host = NULL;
51 	struct packet outgoing;
52 	struct dhcp_packet raw;
53 	struct sockaddr_in to;
54 	struct in_addr from;
55 	struct tree_cache *options[256];
56 	struct subnet *subnet = NULL;
57 	struct lease *lease;
58 	struct iaddr ip_address;
59 	int i;
60 
61 	if (packet->raw->op != BOOTREQUEST)
62 		return;
63 
64 	note("BOOTREQUEST from %s via %s%s", print_hw_addr(packet->raw->htype,
65 	    packet->raw->hlen, packet->raw->chaddr),
66 	    packet->raw->giaddr.s_addr ? inet_ntoa(packet->raw->giaddr) :
67 	    packet->interface->name,
68 	    packet->options_valid ? "" : " (non-rfc1048)");
69 
70 	if (!locate_network(packet))
71 		return;
72 
73 	hp = find_hosts_by_haddr(packet->raw->htype, packet->raw->chaddr,
74 	    packet->raw->hlen);
75 
76 	lease = find_lease(packet, packet->shared_network, 0);
77 
78 	/*
79 	 * Find an IP address in the host_decl that matches the specified
80 	 * network.
81 	 */
82 	if (hp)
83 		subnet = find_host_for_network(&hp, &ip_address,
84 		    packet->shared_network);
85 
86 	if (!subnet) {
87 		/*
88 		 * We didn't find an applicable host declaration. Just in case
89 		 * we may be able to dynamically assign an address, see if
90 		 * there's a host declaration that doesn't have an ip address
91 		 * associated with it.
92 		 */
93 		if (hp)
94 			for (; hp; hp = hp->n_ipaddr)
95 				if (!hp->fixed_addr) {
96 					host = hp;
97 					break;
98 				}
99 
100 		if (host && (!host->group->allow_booting)) {
101 			note("Ignoring excluded BOOTP client %s", host->name ?
102 			    host->name : print_hw_addr (packet->raw->htype,
103 			    packet->raw->hlen, packet->raw->chaddr));
104 			return;
105 		}
106 
107 		if (host && (!host->group->allow_bootp)) {
108 			note("Ignoring BOOTP request from client %s",
109 			    host->name ? host->name :
110 			    print_hw_addr(packet->raw->htype,
111 			    packet->raw->hlen, packet->raw->chaddr));
112 			return;
113 		}
114 
115 		/*
116 		 * If we've been told not to boot unknown clients, and we didn't
117 		 * find any host record for this client, ignore it.
118 		 */
119 		if (!host &&
120 		    !(packet->shared_network->group->boot_unknown_clients)) {
121 			note("Ignoring unknown BOOTP client %s via %s",
122 			    print_hw_addr(packet->raw->htype,
123 			    packet->raw->hlen, packet->raw->chaddr),
124 			    packet->raw->giaddr.s_addr ?
125 			    inet_ntoa(packet->raw->giaddr) :
126 			    packet->interface->name);
127 			return;
128 		}
129 
130 		/*
131 		 * If we've been told not to boot with bootp on this network,
132 		 * ignore it.
133 		 */
134 		if (!host &&
135 		    !(packet->shared_network->group->allow_bootp)) {
136 			note("Ignoring BOOTP request from client %s via %s",
137 			    print_hw_addr(packet->raw->htype,
138 			    packet->raw->hlen, packet->raw->chaddr),
139 			    packet->raw->giaddr.s_addr ?
140 			    inet_ntoa(packet->raw->giaddr) :
141 			    packet->interface->name);
142 			return;
143 		}
144 
145 		/*
146 		 * If the packet is from a host we don't know and there are no
147 		 * dynamic bootp addresses on the network it came in on, drop
148 		 * it on the floor.
149 		 */
150 		if (!(packet->shared_network->group->dynamic_bootp)) {
151 lose:
152 			note("No applicable record for BOOTP host %s via %s",
153 			    print_hw_addr(packet->raw->htype,
154 			    packet->raw->hlen, packet->raw->chaddr),
155 			    packet->raw->giaddr.s_addr ?
156 			    inet_ntoa(packet->raw->giaddr) :
157 			    packet->interface->name);
158 			return;
159 		}
160 
161 		/*
162 		 * If a lease has already been assigned to this client and it's
163 		 * still okay to use dynamic bootp on that lease, reassign it.
164 		 */
165 		if (lease) {
166 			/*
167 			 * If this lease can be used for dynamic bootp, do so.
168 			 */
169 			if ((lease->flags & DYNAMIC_BOOTP_OK)) {
170 				/*
171 				 * If it's not a DYNAMIC_BOOTP lease, release it
172 				 * before reassigning it so that we don't get a
173 				 * lease conflict.
174 				 */
175 				if (!(lease->flags & BOOTP_LEASE))
176 					release_lease(lease);
177 
178 				lease->host = host;
179 				ack_lease(packet, lease, 0, 0);
180 				return;
181 			}
182 
183 			 /*
184 			  * If dynamic BOOTP is no longer allowed for this
185 			  * lease, set it free.
186 			  */
187 			release_lease(lease);
188 		}
189 
190 		/*
191 		 * If there are dynamic bootp addresses that might be
192 		 * available, try to snag one.
193 		 */
194 		for (lease = packet->shared_network->last_lease;
195 		    lease && lease->ends <= cur_time;
196 		    lease = lease->prev) {
197 			if ((lease->flags & DYNAMIC_BOOTP_OK)) {
198 				lease->host = host;
199 				ack_lease(packet, lease, 0, 0);
200 				return;
201 			}
202 		}
203 		goto lose;
204 	}
205 
206 	/* Make sure we're allowed to boot this client. */
207 	if (hp && (!hp->group->allow_booting)) {
208 		note("Ignoring excluded BOOTP client %s", hp->name);
209 		return;
210 	}
211 
212 	/* Make sure we're allowed to boot this client with bootp. */
213 	if (hp && (!hp->group->allow_bootp)) {
214 		note("Ignoring BOOTP request from client %s", hp->name);
215 		return;
216 	}
217 
218 	/* Set up the outgoing packet... */
219 	memset(&outgoing, 0, sizeof outgoing);
220 	memset(&raw, 0, sizeof raw);
221 	outgoing.raw = &raw;
222 
223 	/*
224 	 * If we didn't get a known vendor magic number on the way in, just
225 	 * copy the input options to the output.
226 	 */
227 	if (!packet->options_valid && !subnet->group->always_reply_rfc1048 &&
228 	    (!hp || !hp->group->always_reply_rfc1048)) {
229 		memcpy(outgoing.raw->options, packet->raw->options,
230 		    DHCP_OPTION_LEN);
231 		outgoing.packet_length = BOOTP_MIN_LEN;
232 	} else {
233 		struct tree_cache netmask_tree;   /*  -- RBF */
234 
235 		/*
236 		 * Come up with a list of options that we want to send to this
237 		 * client. Start with the per-subnet options, and then override
238 		 * those with client-specific options.
239 		 */
240 
241 		memcpy(options, subnet->group->options, sizeof(options));
242 
243 		for (i = 0; i < 256; i++)
244 			if (hp->group->options[i])
245 				options[i] = hp->group->options[i];
246 
247 		/*
248 		 * Use the subnet mask from the subnet declaration if no other
249 		 * mask has been provided.
250 		 */
251 		if (!options[DHO_SUBNET_MASK]) {
252 			options[DHO_SUBNET_MASK] = &netmask_tree;
253 			netmask_tree.flags = TC_TEMPORARY;
254 			netmask_tree.value = lease->subnet->netmask.iabuf;
255 			netmask_tree.len = lease->subnet->netmask.len;
256 			netmask_tree.buf_size = lease->subnet->netmask.len;
257 			netmask_tree.timeout = -1;
258 			netmask_tree.tree = NULL;
259 		}
260 
261 		/*
262 		 * Pack the options into the buffer. Unlike DHCP, we can't pack
263 		 * options into the filename and server name buffers.
264 		 */
265 
266 		outgoing.packet_length = cons_options(packet, outgoing.raw,
267 		    0, options, 0, 0, 1, NULL, 0);
268 
269 		if (outgoing.packet_length < BOOTP_MIN_LEN)
270 			outgoing.packet_length = BOOTP_MIN_LEN;
271 	}
272 
273 	/* Take the fields that we care about... */
274 	raw.op = BOOTREPLY;
275 	raw.htype = packet->raw->htype;
276 	raw.hlen = packet->raw->hlen;
277 	memcpy(raw.chaddr, packet->raw->chaddr, sizeof(raw.chaddr));
278 	raw.hops = packet->raw->hops;
279 	raw.xid = packet->raw->xid;
280 	raw.secs = packet->raw->secs;
281 	raw.flags = packet->raw->flags;
282 	raw.ciaddr = packet->raw->ciaddr;
283 	memcpy(&raw.yiaddr, ip_address.iabuf, sizeof(raw.yiaddr));
284 
285 	/* Figure out the address of the next server. */
286 	if (hp && hp->group->next_server.len)
287 		memcpy(&raw.siaddr, hp->group->next_server.iabuf, 4);
288 	else if (subnet->group->next_server.len)
289 		memcpy(&raw.siaddr, subnet->group->next_server.iabuf, 4);
290 	else if (subnet->interface_address.len)
291 		memcpy(&raw.siaddr, subnet->interface_address.iabuf, 4);
292 	else
293 		raw.siaddr = packet->interface->primary_address;
294 
295 	raw.giaddr = packet->raw->giaddr;
296 	if (hp->group->server_name)
297 		strncpy(raw.sname, hp->group->server_name, sizeof(raw.sname));
298 	else if (subnet->group->server_name)
299 		strncpy(raw.sname, subnet->group->server_name,
300 		    sizeof(raw.sname));
301 
302 	if (hp->group->filename)
303 		strncpy(raw.file, hp->group->filename, sizeof(raw.file));
304 	else if (subnet->group->filename)
305 		strncpy(raw.file, subnet->group->filename, sizeof(raw.file));
306 	else
307 		memcpy(raw.file, packet->raw->file, sizeof(raw.file));
308 
309 	from = packet->interface->primary_address;
310 
311 	/* Report what we're doing... */
312 	note("BOOTREPLY for %s to %s (%s) via %s", piaddr(ip_address),
313 	    hp->name, print_hw_addr(packet->raw->htype, packet->raw->hlen,
314 	    packet->raw->chaddr), packet->raw->giaddr.s_addr ?
315 	    inet_ntoa(packet->raw->giaddr) : packet->interface->name);
316 
317 	/* Set up the parts of the address that are in common. */
318 	memset(&to, 0, sizeof(to));
319 	to.sin_family = AF_INET;
320 #ifdef HAVE_SA_LEN
321 	to.sin_len = sizeof(to);
322 #endif
323 
324 	/* If this was gatewayed, send it back to the gateway... */
325 	if (raw.giaddr.s_addr) {
326 		to.sin_addr = raw.giaddr;
327 		to.sin_port = server_port;
328 
329 		(void) send_packet(packet->interface, &raw,
330 		    outgoing.packet_length, from, &to, packet->haddr);
331 		return;
332 	}
333 
334 	/*
335 	 * If it comes from a client that already knows its address and is not
336 	 * requesting a broadcast response, and we can unicast to a client
337 	 * without using the ARP protocol, sent it directly to that client.
338 	 */
339 	else if (!(raw.flags & htons(BOOTP_BROADCAST))) {
340 		to.sin_addr = raw.yiaddr;
341 		to.sin_port = client_port;
342 	} else {
343 		/* Otherwise, broadcast it on the local network. */
344 		to.sin_addr.s_addr = INADDR_BROADCAST;
345 		to.sin_port = client_port; /* XXX */
346 	}
347 
348 	errno = 0;
349 	(void) send_packet(packet->interface, &raw,
350 	    outgoing.packet_length, from, &to, packet->haddr);
351 }
352