]>
Commit | Line | Data |
---|---|---|
d7837182 TL |
1 | /* bootp.c |
2 | ||
3 | BOOTP Protocol support. */ | |
4 | ||
5 | /* | |
c75473d8 | 6 | * Copyright (c) 2004-2005 by Internet Systems Consortium, Inc. ("ISC") |
98311e4b | 7 | * Copyright (c) 1995-2003 by Internet Software Consortium |
d7837182 | 8 | * |
98311e4b DH |
9 | * Permission to use, copy, modify, and distribute this software for any |
10 | * purpose with or without fee is hereby granted, provided that the above | |
11 | * copyright notice and this permission notice appear in all copies. | |
d7837182 | 12 | * |
98311e4b DH |
13 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES |
14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR | |
16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
19 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
d7837182 | 20 | * |
98311e4b DH |
21 | * Internet Systems Consortium, Inc. |
22 | * 950 Charter Street | |
23 | * Redwood City, CA 94063 | |
24 | * <info@isc.org> | |
25 | * http://www.isc.org/ | |
49733f31 | 26 | * |
98311e4b | 27 | * This software has been written for Internet Systems Consortium |
49733f31 | 28 | * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. |
98311e4b | 29 | * To learn more about Internet Systems Consortium, see |
49733f31 TL |
30 | * ``http://www.isc.org/''. To learn more about Vixie Enterprises, |
31 | * see ``http://www.vix.com''. To learn more about Nominum, Inc., see | |
32 | * ``http://www.nominum.com''. | |
d7837182 TL |
33 | */ |
34 | ||
35 | #ifndef lint | |
36 | static char copyright[] = | |
98bd7ca0 | 37 | "$Id: bootp.c,v 1.77 2007/05/08 23:05:21 dhankins Exp $ Copyright (c) 2004-2005 Internet Systems Consortium. All rights reserved.\n"; |
d7837182 TL |
38 | #endif /* not lint */ |
39 | ||
40 | #include "dhcpd.h" | |
41 | ||
93016999 TL |
42 | #if defined (TRACING) |
43 | # define send_packet trace_packet_send | |
44 | #endif | |
45 | ||
d7837182 TL |
46 | void bootp (packet) |
47 | struct packet *packet; | |
48 | { | |
49 | int result; | |
20916cae | 50 | struct host_decl *hp = (struct host_decl *)0; |
cfb326fd | 51 | struct host_decl *host = (struct host_decl *)0; |
9c238be6 TL |
52 | struct packet outgoing; |
53 | struct dhcp_packet raw; | |
d7837182 | 54 | struct sockaddr_in to; |
d1c53034 | 55 | struct in_addr from; |
fde927d2 | 56 | struct hardware hto; |
7e6f3635 | 57 | struct option_state *options = (struct option_state *)0; |
c75473d8 | 58 | struct lease *lease = (struct lease *)0; |
b1b7b521 | 59 | unsigned i; |
ef0afca9 | 60 | struct data_string d1; |
5d25508c | 61 | struct option_cache *oc; |
cc70b445 | 62 | char msgbuf [1024]; |
e13ff8b2 | 63 | int ignorep; |
ac3ecc7f | 64 | int peer_has_leases = 0; |
d7837182 | 65 | |
c0257f22 TL |
66 | if (packet -> raw -> op != BOOTREQUEST) |
67 | return; | |
68 | ||
98311e4b DH |
69 | /* %Audit% This is log output. %2004.06.17,Safe% |
70 | * If we truncate we hope the user can get a hint from the log. | |
71 | */ | |
72 | snprintf (msgbuf, sizeof msgbuf, "BOOTREQUEST from %s via %s", | |
cc70b445 TL |
73 | print_hw_addr (packet -> raw -> htype, |
74 | packet -> raw -> hlen, | |
75 | packet -> raw -> chaddr), | |
76 | packet -> raw -> giaddr.s_addr | |
77 | ? inet_ntoa (packet -> raw -> giaddr) | |
78 | : packet -> interface -> name); | |
b837c98f | 79 | |
cc70b445 | 80 | if (!locate_network (packet)) { |
8ae2d595 | 81 | log_info ("%s: network unknown", msgbuf); |
d1c53034 | 82 | return; |
cc70b445 | 83 | } |
3a581108 | 84 | |
9e383163 TL |
85 | find_lease (&lease, packet, packet -> shared_network, |
86 | 0, 0, (struct lease *)0, MDL); | |
cfb326fd | 87 | |
c75473d8 DH |
88 | if (lease && lease->host) |
89 | host_reference(&hp, lease->host, MDL); | |
cfb326fd | 90 | |
c75473d8 | 91 | if (!lease || ((lease->flags & STATIC_LEASE) == 0)) { |
20916cae | 92 | struct host_decl *h; |
c75473d8 DH |
93 | /* We didn't find an applicable fixed-address host |
94 | declaration. Just in case we may be able to dynamically | |
95 | assign an address, see if there's a host declaration | |
cfb326fd | 96 | that doesn't have an ip address associated with it. */ |
c75473d8 DH |
97 | |
98 | if (!hp) | |
99 | find_hosts_by_haddr(&hp, packet->raw->htype, | |
100 | packet->raw->chaddr, | |
101 | packet->raw->hlen, MDL); | |
102 | ||
20916cae TL |
103 | for (h = hp; h; h = h -> n_ipaddr) { |
104 | if (!h -> fixed_addr) { | |
c75473d8 | 105 | host_reference(&host, h, MDL); |
20916cae | 106 | break; |
cfb326fd TL |
107 | } |
108 | } | |
cfb326fd | 109 | |
c75473d8 DH |
110 | if (hp) |
111 | host_dereference(&hp, MDL); | |
112 | ||
113 | if (host) { | |
114 | host_reference(&hp, host, MDL); | |
115 | host_dereference(&host, MDL); | |
cfb326fd | 116 | } |
98311e4b | 117 | |
c75473d8 DH |
118 | /* Allocate a lease if we have not yet found one. */ |
119 | if (!lease) | |
120 | allocate_lease (&lease, packet, | |
121 | packet -> shared_network -> pools, | |
122 | &peer_has_leases); | |
123 | ||
124 | if (lease) | |
125 | ack_lease (packet, lease, 0, 0, msgbuf, 0, hp); | |
126 | else | |
127 | log_info ("%s: BOOTP from dynamic client and no " | |
128 | "dynamic leases", msgbuf); | |
129 | ||
20916cae | 130 | goto out; |
d7837182 | 131 | } |
9c238be6 | 132 | |
ef0afca9 TL |
133 | /* Run the executable statements to compute the client and server |
134 | options. */ | |
12c9957e | 135 | option_state_allocate (&options, MDL); |
c75473d8 | 136 | |
ef0afca9 | 137 | /* Execute the subnet statements. */ |
1b234d44 | 138 | execute_statements_in_scope ((struct binding_value **)0, |
9e383163 TL |
139 | packet, lease, (struct client_state *)0, |
140 | packet -> options, options, | |
12c9957e | 141 | &lease -> scope, lease -> subnet -> group, |
ef0afca9 | 142 | (struct group *)0); |
c75473d8 | 143 | |
fa661adb TL |
144 | /* Execute statements from class scopes. */ |
145 | for (i = packet -> class_count; i > 0; i--) { | |
146 | execute_statements_in_scope | |
1b234d44 | 147 | ((struct binding_value **)0, |
9e383163 TL |
148 | packet, lease, (struct client_state *)0, |
149 | packet -> options, options, | |
12c9957e | 150 | &lease -> scope, packet -> classes [i - 1] -> group, |
fa661adb TL |
151 | lease -> subnet -> group); |
152 | } | |
153 | ||
ef0afca9 | 154 | /* Execute the host statements. */ |
1b234d44 | 155 | execute_statements_in_scope ((struct binding_value **)0, |
9e383163 TL |
156 | packet, lease, (struct client_state *)0, |
157 | packet -> options, options, | |
12c9957e | 158 | &lease -> scope, |
c75473d8 | 159 | hp -> group, lease -> subnet -> group); |
ef0afca9 TL |
160 | |
161 | /* Drop the request if it's not allowed for this client. */ | |
fe849040 | 162 | if ((oc = lookup_option (&server_universe, options, SV_ALLOW_BOOTP)) && |
e13ff8b2 | 163 | !evaluate_boolean_option_cache (&ignorep, packet, lease, |
9e383163 | 164 | (struct client_state *)0, |
12c9957e TL |
165 | packet -> options, options, |
166 | &lease -> scope, oc, MDL)) { | |
e13ff8b2 TL |
167 | if (!ignorep) |
168 | log_info ("%s: bootp disallowed", msgbuf); | |
20916cae | 169 | goto out; |
ef0afca9 TL |
170 | } |
171 | ||
fe849040 TL |
172 | if ((oc = lookup_option (&server_universe, |
173 | options, SV_ALLOW_BOOTING)) && | |
e13ff8b2 | 174 | !evaluate_boolean_option_cache (&ignorep, packet, lease, |
9e383163 | 175 | (struct client_state *)0, |
12c9957e TL |
176 | packet -> options, options, |
177 | &lease -> scope, oc, MDL)) { | |
e13ff8b2 TL |
178 | if (!ignorep) |
179 | log_info ("%s: booting disallowed", msgbuf); | |
20916cae | 180 | goto out; |
e07ac89a | 181 | } |
ef0afca9 | 182 | |
9c238be6 TL |
183 | /* Set up the outgoing packet... */ |
184 | memset (&outgoing, 0, sizeof outgoing); | |
185 | memset (&raw, 0, sizeof raw); | |
186 | outgoing.raw = &raw; | |
187 | ||
d8ae14c6 TL |
188 | /* If we didn't get a known vendor magic number on the way in, |
189 | just copy the input options to the output. */ | |
9804a56a TL |
190 | if (!packet -> options_valid && |
191 | !(evaluate_boolean_option_cache | |
9e383163 TL |
192 | (&ignorep, packet, lease, (struct client_state *)0, |
193 | packet -> options, options, &lease -> scope, | |
02124b3c | 194 | lookup_option (&server_universe, options, |
12c9957e | 195 | SV_ALWAYS_REPLY_RFC1048), MDL))) { |
d8ae14c6 TL |
196 | memcpy (outgoing.raw -> options, |
197 | packet -> raw -> options, DHCP_OPTION_LEN); | |
198 | outgoing.packet_length = BOOTP_MIN_LEN; | |
199 | } else { | |
9804a56a TL |
200 | |
201 | /* Use the subnet mask from the subnet declaration if no other | |
202 | mask has been provided. */ | |
203 | ||
204 | oc = (struct option_cache *)0; | |
205 | i = DHO_SUBNET_MASK; | |
206 | if (!lookup_option (&dhcp_universe, options, i)) { | |
12c9957e | 207 | if (option_cache_allocate (&oc, MDL)) { |
9804a56a TL |
208 | if (make_const_data |
209 | (&oc -> expression, | |
210 | lease -> subnet -> netmask.iabuf, | |
d758ad8c TL |
211 | lease -> subnet -> netmask.len, |
212 | 0, 0, MDL)) { | |
f7fdb216 DH |
213 | option_code_hash_lookup(&oc->option, |
214 | dhcp_universe.code_hash, | |
215 | &i, 0, MDL); | |
9804a56a TL |
216 | save_option (&dhcp_universe, |
217 | options, oc); | |
218 | } | |
12c9957e | 219 | option_cache_dereference (&oc, MDL); |
9804a56a TL |
220 | } |
221 | } | |
222 | ||
d8ae14c6 TL |
223 | /* Pack the options into the buffer. Unlike DHCP, we |
224 | can't pack options into the filename and server | |
225 | name buffers. */ | |
9c238be6 | 226 | |
d8ae14c6 | 227 | outgoing.packet_length = |
9e383163 TL |
228 | cons_options (packet, outgoing.raw, lease, |
229 | (struct client_state *)0, 0, | |
2cc0151c | 230 | packet -> options, options, |
12c9957e | 231 | &lease -> scope, |
63b935b6 TL |
232 | 0, 0, 1, (struct data_string *)0, |
233 | (const char *)0); | |
d8ae14c6 TL |
234 | if (outgoing.packet_length < BOOTP_MIN_LEN) |
235 | outgoing.packet_length = BOOTP_MIN_LEN; | |
236 | } | |
9c238be6 | 237 | |
d7837182 | 238 | /* Take the fields that we care about... */ |
9c238be6 TL |
239 | raw.op = BOOTREPLY; |
240 | raw.htype = packet -> raw -> htype; | |
241 | raw.hlen = packet -> raw -> hlen; | |
d8ae14c6 | 242 | memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr); |
9c238be6 TL |
243 | raw.hops = packet -> raw -> hops; |
244 | raw.xid = packet -> raw -> xid; | |
245 | raw.secs = packet -> raw -> secs; | |
9129caed | 246 | raw.flags = packet -> raw -> flags; |
9c238be6 | 247 | raw.ciaddr = packet -> raw -> ciaddr; |
c75473d8 DH |
248 | |
249 | /* yiaddr is an ipv4 address, it must be 4 octets. */ | |
250 | memcpy (&raw.yiaddr, lease->ip_addr.iabuf, 4); | |
cfb326fd | 251 | |
9129caed TL |
252 | /* If we're always supposed to broadcast to this client, set |
253 | the broadcast bit in the bootp flags field. */ | |
e3b5f7f8 TL |
254 | if ((oc = lookup_option (&server_universe, |
255 | options, SV_ALWAYS_BROADCAST)) && | |
e13ff8b2 | 256 | evaluate_boolean_option_cache (&ignorep, packet, lease, |
9e383163 | 257 | (struct client_state *)0, |
12c9957e TL |
258 | packet -> options, options, |
259 | &lease -> scope, oc, MDL)) | |
9129caed TL |
260 | raw.flags |= htons (BOOTP_BROADCAST); |
261 | ||
d1c53034 | 262 | /* Figure out the address of the next server. */ |
e40810bf | 263 | memset (&d1, 0, sizeof d1); |
02124b3c | 264 | oc = lookup_option (&server_universe, options, SV_NEXT_SERVER); |
5d25508c | 265 | if (oc && |
2cc0151c | 266 | evaluate_option_cache (&d1, packet, lease, |
9e383163 | 267 | (struct client_state *)0, |
12c9957e TL |
268 | packet -> options, options, |
269 | &lease -> scope, oc, MDL)) { | |
ef0afca9 TL |
270 | /* If there was more than one answer, take the first. */ |
271 | if (d1.len >= 4 && d1.data) | |
272 | memcpy (&raw.siaddr, d1.data, 4); | |
12c9957e | 273 | data_string_forget (&d1, MDL); |
17f1bc9a | 274 | } else { |
98bd7ca0 DH |
275 | if ((lease->subnet->shared_network->interface != NULL) && |
276 | lease->subnet->shared_network->interface->address_count) | |
277 | raw.siaddr = | |
278 | lease->subnet->shared_network->interface->addresses[0]; | |
279 | else if (packet->interface->address_count) | |
280 | raw.siaddr = packet->interface->addresses[0]; | |
ef0afca9 | 281 | } |
cfb326fd | 282 | |
9c238be6 | 283 | raw.giaddr = packet -> raw -> giaddr; |
ef0afca9 TL |
284 | |
285 | /* Figure out the filename. */ | |
02124b3c | 286 | oc = lookup_option (&server_universe, options, SV_FILENAME); |
5d25508c | 287 | if (oc && |
2cc0151c | 288 | evaluate_option_cache (&d1, packet, lease, |
9e383163 | 289 | (struct client_state *)0, |
12c9957e TL |
290 | packet -> options, options, |
291 | &lease -> scope, oc, MDL)) { | |
ef0afca9 TL |
292 | memcpy (raw.file, d1.data, |
293 | d1.len > sizeof raw.file ? sizeof raw.file : d1.len); | |
294 | if (sizeof raw.file > d1.len) | |
295 | memset (&raw.file [d1.len], | |
296 | 0, (sizeof raw.file) - d1.len); | |
12c9957e | 297 | data_string_forget (&d1, MDL); |
339b0231 | 298 | } else |
92e3e691 | 299 | memcpy (raw.file, packet -> raw -> file, sizeof raw.file); |
ef0afca9 TL |
300 | |
301 | /* Choose a server name as above. */ | |
02124b3c | 302 | oc = lookup_option (&server_universe, options, SV_SERVER_NAME); |
5d25508c | 303 | if (oc && |
2cc0151c | 304 | evaluate_option_cache (&d1, packet, lease, |
9e383163 | 305 | (struct client_state *)0, |
12c9957e TL |
306 | packet -> options, options, |
307 | &lease -> scope, oc, MDL)) { | |
ef0afca9 TL |
308 | memcpy (raw.sname, d1.data, |
309 | d1.len > sizeof raw.sname ? sizeof raw.sname : d1.len); | |
310 | if (sizeof raw.sname > d1.len) | |
311 | memset (&raw.sname [d1.len], | |
312 | 0, (sizeof raw.sname) - d1.len); | |
12c9957e | 313 | data_string_forget (&d1, MDL); |
d7837182 | 314 | } |
d7837182 | 315 | |
26413cf1 | 316 | /* Execute the commit statements, if there are any. */ |
1b234d44 | 317 | execute_statements ((struct binding_value **)0, |
9e383163 TL |
318 | packet, lease, (struct client_state *)0, |
319 | packet -> options, | |
12c9957e | 320 | options, &lease -> scope, lease -> on_commit); |
26413cf1 | 321 | |
7e6f3635 | 322 | /* We're done with the option state. */ |
12c9957e | 323 | option_state_dereference (&options, MDL); |
7e6f3635 | 324 | |
cfb326fd | 325 | /* Set up the hardware destination address... */ |
9e9b2261 TL |
326 | hto.hbuf [0] = packet -> raw -> htype; |
327 | hto.hlen = packet -> raw -> hlen + 1; | |
012c4143 | 328 | memcpy (&hto.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen); |
cfb326fd | 329 | |
98bd7ca0 DH |
330 | if (packet->interface->address_count) |
331 | from = packet->interface->addresses[0]; | |
d1c53034 | 332 | |
cfb326fd | 333 | /* Report what we're doing... */ |
8ae2d595 TL |
334 | log_info ("%s", msgbuf); |
335 | log_info ("BOOTREPLY for %s to %s (%s) via %s", | |
c75473d8 | 336 | piaddr (lease->ip_addr), hp -> name, |
cfb326fd TL |
337 | print_hw_addr (packet -> raw -> htype, |
338 | packet -> raw -> hlen, | |
b837c98f TL |
339 | packet -> raw -> chaddr), |
340 | packet -> raw -> giaddr.s_addr | |
341 | ? inet_ntoa (packet -> raw -> giaddr) | |
342 | : packet -> interface -> name); | |
cfb326fd TL |
343 | |
344 | /* Set up the parts of the address that are in common. */ | |
345 | to.sin_family = AF_INET; | |
346 | #ifdef HAVE_SA_LEN | |
347 | to.sin_len = sizeof to; | |
348 | #endif | |
349 | memset (to.sin_zero, 0, sizeof to.sin_zero); | |
350 | ||
9c238be6 TL |
351 | /* If this was gatewayed, send it back to the gateway... */ |
352 | if (raw.giaddr.s_addr) { | |
353 | to.sin_addr = raw.giaddr; | |
bfa885c9 | 354 | to.sin_port = local_port; |
cfb326fd | 355 | |
92e3e691 TL |
356 | if (fallback_interface) { |
357 | result = send_packet (fallback_interface, | |
358 | (struct packet *)0, | |
359 | &raw, outgoing.packet_length, | |
360 | from, &to, &hto); | |
20916cae | 361 | goto out; |
92e3e691 | 362 | } |
fe849040 TL |
363 | |
364 | /* If it comes from a client that already knows its address | |
365 | and is not requesting a broadcast response, and we can | |
366 | unicast to a client without using the ARP protocol, sent it | |
367 | directly to that client. */ | |
368 | } else if (!(raw.flags & htons (BOOTP_BROADCAST)) && | |
369 | can_unicast_without_arp (packet -> interface)) { | |
370 | to.sin_addr = raw.yiaddr; | |
371 | to.sin_port = remote_port; | |
372 | ||
9c238be6 TL |
373 | /* Otherwise, broadcast it on the local network. */ |
374 | } else { | |
71df44f5 | 375 | to.sin_addr = limited_broadcast; |
bfa885c9 | 376 | to.sin_port = remote_port; /* XXX */ |
9c238be6 | 377 | } |
d7837182 | 378 | |
d7837182 | 379 | errno = 0; |
fde927d2 | 380 | result = send_packet (packet -> interface, |
cfb326fd | 381 | packet, &raw, outgoing.packet_length, |
d1c53034 | 382 | from, &to, &hto); |
20916cae TL |
383 | out: |
384 | if (options) | |
385 | option_state_dereference (&options, MDL); | |
31bbee78 | 386 | if (lease) |
20916cae | 387 | lease_dereference (&lease, MDL); |
20916cae TL |
388 | if (hp) |
389 | host_dereference (&hp, MDL); | |
390 | if (host) | |
391 | host_dereference (&host, MDL); | |
d7837182 | 392 | } |