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 |
|
|
} |