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
bootp(struct packet * packet)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