3 BOOTP Protocol support. */
6 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1995-2003 by Internet Software Consortium
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.
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.
21 * Internet Systems Consortium, Inc.
23 * Redwood City, CA 94063
27 * This software has been written for Internet Systems Consortium
28 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29 * To learn more about Internet Systems Consortium, see
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''.
36 static char copyright
[] =
37 "$Id: bootp.c,v 1.72 2005/03/17 20:15:26 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n";
43 # define send_packet trace_packet_send
47 struct packet
*packet
;
50 struct host_decl
*hp
= (struct host_decl
*)0;
51 struct host_decl
*host
= (struct host_decl
*)0;
52 struct packet outgoing
;
53 struct dhcp_packet raw
;
54 struct sockaddr_in to
;
57 struct option_state
*options
= (struct option_state
*)0;
58 struct subnet
*subnet
;
60 struct iaddr ip_address
;
62 struct data_string d1
;
63 struct option_cache
*oc
;
66 int peer_has_leases
= 0;
68 if (packet
-> raw
-> op
!= BOOTREQUEST
)
71 /* %Audit% This is log output. %2004.06.17,Safe%
72 * If we truncate we hope the user can get a hint from the log.
74 snprintf (msgbuf
, sizeof msgbuf
, "BOOTREQUEST from %s via %s",
75 print_hw_addr (packet
-> raw
-> htype
,
76 packet
-> raw
-> hlen
,
77 packet
-> raw
-> chaddr
),
78 packet
-> raw
-> giaddr
.s_addr
79 ? inet_ntoa (packet
-> raw
-> giaddr
)
80 : packet
-> interface
-> name
);
84 if (!locate_network (packet
)) {
85 log_info ("%s: network unknown", msgbuf
);
89 find_hosts_by_haddr (&hp
, packet
-> raw
-> htype
,
90 packet
-> raw
-> chaddr
,
91 packet
-> raw
-> hlen
, MDL
);
93 lease
= (struct lease
*)0;
94 find_lease (&lease
, packet
, packet
-> shared_network
,
95 0, 0, (struct lease
*)0, MDL
);
97 /* Find an IP address in the host_decl that matches the
99 subnet
= (struct subnet
*)0;
101 find_host_for_network (&subnet
, &hp
, &ip_address
,
102 packet
-> shared_network
);
106 /* We didn't find an applicable host declaration.
107 Just in case we may be able to dynamically assign
108 an address, see if there's a host declaration
109 that doesn't have an ip address associated with it. */
110 for (h
= hp
; h
; h
= h
-> n_ipaddr
) {
111 if (!h
-> fixed_addr
) {
112 host_reference (&host
, h
, MDL
);
117 host_dereference (&hp
, MDL
);
119 host_reference (&hp
, host
, MDL
);
122 /* If a lease has already been assigned to this client,
125 if (host
&& host
!= lease
-> host
) {
127 host_dereference (&lease
-> host
, MDL
);
128 host_reference (&lease
-> host
, host
, MDL
);
130 ack_lease (packet
, lease
, 0, 0, msgbuf
, 0);
134 /* Otherwise, try to allocate one. */
135 allocate_lease (&lease
, packet
,
136 packet
-> shared_network
-> pools
,
139 if (host
&& host
!= lease
-> host
) {
141 host_dereference (&lease
-> host
, MDL
);
142 host_reference (&lease
-> host
, host
, MDL
);
143 } else if (lease
-> host
)
144 host_dereference (&lease
-> host
, MDL
);
145 ack_lease (packet
, lease
, 0, 0, msgbuf
, 0);
149 /* We couldn't find an address to give this bootp client. */
150 log_info ("%s: BOOTP from unknown client and no dynamic leases",
155 /* Run the executable statements to compute the client and server
157 option_state_allocate (&options
, MDL
);
159 /* Execute the subnet statements. */
160 execute_statements_in_scope ((struct binding_value
**)0,
161 packet
, lease
, (struct client_state
*)0,
162 packet
-> options
, options
,
163 &lease
-> scope
, lease
-> subnet
-> group
,
166 /* Execute statements from class scopes. */
167 for (i
= packet
-> class_count
; i
> 0; i
--) {
168 execute_statements_in_scope
169 ((struct binding_value
**)0,
170 packet
, lease
, (struct client_state
*)0,
171 packet
-> options
, options
,
172 &lease
-> scope
, packet
-> classes
[i
- 1] -> group
,
173 lease
-> subnet
-> group
);
176 /* Execute the host statements. */
177 execute_statements_in_scope ((struct binding_value
**)0,
178 packet
, lease
, (struct client_state
*)0,
179 packet
-> options
, options
,
181 hp
-> group
, subnet
-> group
);
183 /* Drop the request if it's not allowed for this client. */
184 if ((oc
= lookup_option (&server_universe
, options
, SV_ALLOW_BOOTP
)) &&
185 !evaluate_boolean_option_cache (&ignorep
, packet
, lease
,
186 (struct client_state
*)0,
187 packet
-> options
, options
,
188 &lease
-> scope
, oc
, MDL
)) {
190 log_info ("%s: bootp disallowed", msgbuf
);
194 if ((oc
= lookup_option (&server_universe
,
195 options
, SV_ALLOW_BOOTING
)) &&
196 !evaluate_boolean_option_cache (&ignorep
, packet
, lease
,
197 (struct client_state
*)0,
198 packet
-> options
, options
,
199 &lease
-> scope
, oc
, MDL
)) {
201 log_info ("%s: booting disallowed", msgbuf
);
205 /* Set up the outgoing packet... */
206 memset (&outgoing
, 0, sizeof outgoing
);
207 memset (&raw
, 0, sizeof raw
);
210 /* If we didn't get a known vendor magic number on the way in,
211 just copy the input options to the output. */
212 if (!packet
-> options_valid
&&
213 !(evaluate_boolean_option_cache
214 (&ignorep
, packet
, lease
, (struct client_state
*)0,
215 packet
-> options
, options
, &lease
-> scope
,
216 lookup_option (&server_universe
, options
,
217 SV_ALWAYS_REPLY_RFC1048
), MDL
))) {
218 memcpy (outgoing
.raw
-> options
,
219 packet
-> raw
-> options
, DHCP_OPTION_LEN
);
220 outgoing
.packet_length
= BOOTP_MIN_LEN
;
223 /* Use the subnet mask from the subnet declaration if no other
224 mask has been provided. */
226 oc
= (struct option_cache
*)0;
228 if (!lookup_option (&dhcp_universe
, options
, i
)) {
229 if (option_cache_allocate (&oc
, MDL
)) {
232 lease
-> subnet
-> netmask
.iabuf
,
233 lease
-> subnet
-> netmask
.len
,
236 dhcp_universe
.options
[i
];
237 save_option (&dhcp_universe
,
240 option_cache_dereference (&oc
, MDL
);
244 /* Pack the options into the buffer. Unlike DHCP, we
245 can't pack options into the filename and server
248 outgoing
.packet_length
=
249 cons_options (packet
, outgoing
.raw
, lease
,
250 (struct client_state
*)0, 0,
251 packet
-> options
, options
,
253 0, 0, 1, (struct data_string
*)0,
255 if (outgoing
.packet_length
< BOOTP_MIN_LEN
)
256 outgoing
.packet_length
= BOOTP_MIN_LEN
;
259 /* Take the fields that we care about... */
261 raw
.htype
= packet
-> raw
-> htype
;
262 raw
.hlen
= packet
-> raw
-> hlen
;
263 memcpy (raw
.chaddr
, packet
-> raw
-> chaddr
, sizeof raw
.chaddr
);
264 raw
.hops
= packet
-> raw
-> hops
;
265 raw
.xid
= packet
-> raw
-> xid
;
266 raw
.secs
= packet
-> raw
-> secs
;
267 raw
.flags
= packet
-> raw
-> flags
;
268 raw
.ciaddr
= packet
-> raw
-> ciaddr
;
269 memcpy (&raw
.yiaddr
, ip_address
.iabuf
, sizeof raw
.yiaddr
);
271 /* If we're always supposed to broadcast to this client, set
272 the broadcast bit in the bootp flags field. */
273 if ((oc
= lookup_option (&server_universe
,
274 options
, SV_ALWAYS_BROADCAST
)) &&
275 evaluate_boolean_option_cache (&ignorep
, packet
, lease
,
276 (struct client_state
*)0,
277 packet
-> options
, options
,
278 &lease
-> scope
, oc
, MDL
))
279 raw
.flags
|= htons (BOOTP_BROADCAST
);
281 /* Figure out the address of the next server. */
282 memset (&d1
, 0, sizeof d1
);
283 oc
= lookup_option (&server_universe
, options
, SV_NEXT_SERVER
);
285 evaluate_option_cache (&d1
, packet
, lease
,
286 (struct client_state
*)0,
287 packet
-> options
, options
,
288 &lease
-> scope
, oc
, MDL
)) {
289 /* If there was more than one answer, take the first. */
290 if (d1
.len
>= 4 && d1
.data
)
291 memcpy (&raw
.siaddr
, d1
.data
, 4);
292 data_string_forget (&d1
, MDL
);
294 if (lease
-> subnet
-> shared_network
-> interface
)
295 raw
.siaddr
= (lease
-> subnet
-> shared_network
->
296 interface
-> primary_address
);
298 raw
.siaddr
= packet
-> interface
-> primary_address
;
301 raw
.giaddr
= packet
-> raw
-> giaddr
;
303 /* Figure out the filename. */
304 oc
= lookup_option (&server_universe
, options
, SV_FILENAME
);
306 evaluate_option_cache (&d1
, packet
, lease
,
307 (struct client_state
*)0,
308 packet
-> options
, options
,
309 &lease
-> scope
, oc
, MDL
)) {
310 memcpy (raw
.file
, d1
.data
,
311 d1
.len
> sizeof raw
.file
? sizeof raw
.file
: d1
.len
);
312 if (sizeof raw
.file
> d1
.len
)
313 memset (&raw
.file
[d1
.len
],
314 0, (sizeof raw
.file
) - d1
.len
);
315 data_string_forget (&d1
, MDL
);
317 memcpy (raw
.file
, packet
-> raw
-> file
, sizeof raw
.file
);
319 /* Choose a server name as above. */
320 oc
= lookup_option (&server_universe
, options
, SV_SERVER_NAME
);
322 evaluate_option_cache (&d1
, packet
, lease
,
323 (struct client_state
*)0,
324 packet
-> options
, options
,
325 &lease
-> scope
, oc
, MDL
)) {
326 memcpy (raw
.sname
, d1
.data
,
327 d1
.len
> sizeof raw
.sname
? sizeof raw
.sname
: d1
.len
);
328 if (sizeof raw
.sname
> d1
.len
)
329 memset (&raw
.sname
[d1
.len
],
330 0, (sizeof raw
.sname
) - d1
.len
);
331 data_string_forget (&d1
, MDL
);
334 /* Execute the commit statements, if there are any. */
335 execute_statements ((struct binding_value
**)0,
336 packet
, lease
, (struct client_state
*)0,
338 options
, &lease
-> scope
, lease
-> on_commit
);
340 /* We're done with the option state. */
341 option_state_dereference (&options
, MDL
);
343 /* Set up the hardware destination address... */
344 hto
.hbuf
[0] = packet
-> raw
-> htype
;
345 hto
.hlen
= packet
-> raw
-> hlen
+ 1;
346 memcpy (&hto
.hbuf
[1], packet
-> raw
-> chaddr
, packet
-> raw
-> hlen
);
348 from
= packet
-> interface
-> primary_address
;
350 /* Report what we're doing... */
351 log_info ("%s", msgbuf
);
352 log_info ("BOOTREPLY for %s to %s (%s) via %s",
353 piaddr (ip_address
), hp
-> name
,
354 print_hw_addr (packet
-> raw
-> htype
,
355 packet
-> raw
-> hlen
,
356 packet
-> raw
-> chaddr
),
357 packet
-> raw
-> giaddr
.s_addr
358 ? inet_ntoa (packet
-> raw
-> giaddr
)
359 : packet
-> interface
-> name
);
361 /* Set up the parts of the address that are in common. */
362 to
.sin_family
= AF_INET
;
364 to
.sin_len
= sizeof to
;
366 memset (to
.sin_zero
, 0, sizeof to
.sin_zero
);
368 /* If this was gatewayed, send it back to the gateway... */
369 if (raw
.giaddr
.s_addr
) {
370 to
.sin_addr
= raw
.giaddr
;
371 to
.sin_port
= local_port
;
373 if (fallback_interface
) {
374 result
= send_packet (fallback_interface
,
376 &raw
, outgoing
.packet_length
,
381 /* If it comes from a client that already knows its address
382 and is not requesting a broadcast response, and we can
383 unicast to a client without using the ARP protocol, sent it
384 directly to that client. */
385 } else if (!(raw
.flags
& htons (BOOTP_BROADCAST
)) &&
386 can_unicast_without_arp (packet
-> interface
)) {
387 to
.sin_addr
= raw
.yiaddr
;
388 to
.sin_port
= remote_port
;
390 /* Otherwise, broadcast it on the local network. */
392 to
.sin_addr
= limited_broadcast
;
393 to
.sin_port
= remote_port
; /* XXX */
397 result
= send_packet (packet
-> interface
,
398 packet
, &raw
, outgoing
.packet_length
,
402 option_state_dereference (&options
, MDL
);
404 lease_dereference (&lease
, MDL
);
406 host_dereference (&hp
, MDL
);
408 host_dereference (&host
, MDL
);
410 subnet_dereference (&subnet
, MDL
);