]> git.ipfire.org Git - thirdparty/dhcp.git/blob - server/bootp.c
DHCPv6 branch merged to HEAD.
[thirdparty/dhcp.git] / server / bootp.c
1 /* bootp.c
2
3 BOOTP Protocol support. */
4
5 /*
6 * Copyright (c) 2004-2005 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1995-2003 by Internet Software Consortium
8 *
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.
12 *
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.
20 *
21 * Internet Systems Consortium, Inc.
22 * 950 Charter Street
23 * Redwood City, CA 94063
24 * <info@isc.org>
25 * http://www.isc.org/
26 *
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''.
33 */
34
35 #ifndef lint
36 static char copyright[] =
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";
38 #endif /* not lint */
39
40 #include "dhcpd.h"
41
42 #if defined (TRACING)
43 # define send_packet trace_packet_send
44 #endif
45
46 void bootp (packet)
47 struct packet *packet;
48 {
49 int result;
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;
55 struct in_addr from;
56 struct hardware hto;
57 struct option_state *options = (struct option_state *)0;
58 struct lease *lease = (struct lease *)0;
59 unsigned i;
60 struct data_string d1;
61 struct option_cache *oc;
62 char msgbuf [1024];
63 int ignorep;
64 int peer_has_leases = 0;
65
66 if (packet -> raw -> op != BOOTREQUEST)
67 return;
68
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",
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);
79
80 if (!locate_network (packet)) {
81 log_info ("%s: network unknown", msgbuf);
82 return;
83 }
84
85 find_lease (&lease, packet, packet -> shared_network,
86 0, 0, (struct lease *)0, MDL);
87
88 if (lease && lease->host)
89 host_reference(&hp, lease->host, MDL);
90
91 if (!lease || ((lease->flags & STATIC_LEASE) == 0)) {
92 struct host_decl *h;
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
96 that doesn't have an ip address associated with it. */
97
98 if (!hp)
99 find_hosts_by_haddr(&hp, packet->raw->htype,
100 packet->raw->chaddr,
101 packet->raw->hlen, MDL);
102
103 for (h = hp; h; h = h -> n_ipaddr) {
104 if (!h -> fixed_addr) {
105 host_reference(&host, h, MDL);
106 break;
107 }
108 }
109
110 if (hp)
111 host_dereference(&hp, MDL);
112
113 if (host) {
114 host_reference(&hp, host, MDL);
115 host_dereference(&host, MDL);
116 }
117
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
130 goto out;
131 }
132
133 /* Run the executable statements to compute the client and server
134 options. */
135 option_state_allocate (&options, MDL);
136
137 /* Execute the subnet statements. */
138 execute_statements_in_scope ((struct binding_value **)0,
139 packet, lease, (struct client_state *)0,
140 packet -> options, options,
141 &lease -> scope, lease -> subnet -> group,
142 (struct group *)0);
143
144 /* Execute statements from class scopes. */
145 for (i = packet -> class_count; i > 0; i--) {
146 execute_statements_in_scope
147 ((struct binding_value **)0,
148 packet, lease, (struct client_state *)0,
149 packet -> options, options,
150 &lease -> scope, packet -> classes [i - 1] -> group,
151 lease -> subnet -> group);
152 }
153
154 /* Execute the host statements. */
155 execute_statements_in_scope ((struct binding_value **)0,
156 packet, lease, (struct client_state *)0,
157 packet -> options, options,
158 &lease -> scope,
159 hp -> group, lease -> subnet -> group);
160
161 /* Drop the request if it's not allowed for this client. */
162 if ((oc = lookup_option (&server_universe, options, SV_ALLOW_BOOTP)) &&
163 !evaluate_boolean_option_cache (&ignorep, packet, lease,
164 (struct client_state *)0,
165 packet -> options, options,
166 &lease -> scope, oc, MDL)) {
167 if (!ignorep)
168 log_info ("%s: bootp disallowed", msgbuf);
169 goto out;
170 }
171
172 if ((oc = lookup_option (&server_universe,
173 options, SV_ALLOW_BOOTING)) &&
174 !evaluate_boolean_option_cache (&ignorep, packet, lease,
175 (struct client_state *)0,
176 packet -> options, options,
177 &lease -> scope, oc, MDL)) {
178 if (!ignorep)
179 log_info ("%s: booting disallowed", msgbuf);
180 goto out;
181 }
182
183 /* Set up the outgoing packet... */
184 memset (&outgoing, 0, sizeof outgoing);
185 memset (&raw, 0, sizeof raw);
186 outgoing.raw = &raw;
187
188 /* If we didn't get a known vendor magic number on the way in,
189 just copy the input options to the output. */
190 if (!packet -> options_valid &&
191 !(evaluate_boolean_option_cache
192 (&ignorep, packet, lease, (struct client_state *)0,
193 packet -> options, options, &lease -> scope,
194 lookup_option (&server_universe, options,
195 SV_ALWAYS_REPLY_RFC1048), MDL))) {
196 memcpy (outgoing.raw -> options,
197 packet -> raw -> options, DHCP_OPTION_LEN);
198 outgoing.packet_length = BOOTP_MIN_LEN;
199 } else {
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)) {
207 if (option_cache_allocate (&oc, MDL)) {
208 if (make_const_data
209 (&oc -> expression,
210 lease -> subnet -> netmask.iabuf,
211 lease -> subnet -> netmask.len,
212 0, 0, MDL)) {
213 option_code_hash_lookup(&oc->option,
214 dhcp_universe.code_hash,
215 &i, 0, MDL);
216 save_option (&dhcp_universe,
217 options, oc);
218 }
219 option_cache_dereference (&oc, MDL);
220 }
221 }
222
223 /* Pack the options into the buffer. Unlike DHCP, we
224 can't pack options into the filename and server
225 name buffers. */
226
227 outgoing.packet_length =
228 cons_options (packet, outgoing.raw, lease,
229 (struct client_state *)0, 0,
230 packet -> options, options,
231 &lease -> scope,
232 0, 0, 1, (struct data_string *)0,
233 (const char *)0);
234 if (outgoing.packet_length < BOOTP_MIN_LEN)
235 outgoing.packet_length = BOOTP_MIN_LEN;
236 }
237
238 /* Take the fields that we care about... */
239 raw.op = BOOTREPLY;
240 raw.htype = packet -> raw -> htype;
241 raw.hlen = packet -> raw -> hlen;
242 memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr);
243 raw.hops = packet -> raw -> hops;
244 raw.xid = packet -> raw -> xid;
245 raw.secs = packet -> raw -> secs;
246 raw.flags = packet -> raw -> flags;
247 raw.ciaddr = packet -> raw -> ciaddr;
248
249 /* yiaddr is an ipv4 address, it must be 4 octets. */
250 memcpy (&raw.yiaddr, lease->ip_addr.iabuf, 4);
251
252 /* If we're always supposed to broadcast to this client, set
253 the broadcast bit in the bootp flags field. */
254 if ((oc = lookup_option (&server_universe,
255 options, SV_ALWAYS_BROADCAST)) &&
256 evaluate_boolean_option_cache (&ignorep, packet, lease,
257 (struct client_state *)0,
258 packet -> options, options,
259 &lease -> scope, oc, MDL))
260 raw.flags |= htons (BOOTP_BROADCAST);
261
262 /* Figure out the address of the next server. */
263 memset (&d1, 0, sizeof d1);
264 oc = lookup_option (&server_universe, options, SV_NEXT_SERVER);
265 if (oc &&
266 evaluate_option_cache (&d1, packet, lease,
267 (struct client_state *)0,
268 packet -> options, options,
269 &lease -> scope, oc, MDL)) {
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);
273 data_string_forget (&d1, MDL);
274 } else {
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];
281 }
282
283 raw.giaddr = packet -> raw -> giaddr;
284
285 /* Figure out the filename. */
286 oc = lookup_option (&server_universe, options, SV_FILENAME);
287 if (oc &&
288 evaluate_option_cache (&d1, packet, lease,
289 (struct client_state *)0,
290 packet -> options, options,
291 &lease -> scope, oc, MDL)) {
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);
297 data_string_forget (&d1, MDL);
298 } else
299 memcpy (raw.file, packet -> raw -> file, sizeof raw.file);
300
301 /* Choose a server name as above. */
302 oc = lookup_option (&server_universe, options, SV_SERVER_NAME);
303 if (oc &&
304 evaluate_option_cache (&d1, packet, lease,
305 (struct client_state *)0,
306 packet -> options, options,
307 &lease -> scope, oc, MDL)) {
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);
313 data_string_forget (&d1, MDL);
314 }
315
316 /* Execute the commit statements, if there are any. */
317 execute_statements ((struct binding_value **)0,
318 packet, lease, (struct client_state *)0,
319 packet -> options,
320 options, &lease -> scope, lease -> on_commit);
321
322 /* We're done with the option state. */
323 option_state_dereference (&options, MDL);
324
325 /* Set up the hardware destination address... */
326 hto.hbuf [0] = packet -> raw -> htype;
327 hto.hlen = packet -> raw -> hlen + 1;
328 memcpy (&hto.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen);
329
330 if (packet->interface->address_count)
331 from = packet->interface->addresses[0];
332
333 /* Report what we're doing... */
334 log_info ("%s", msgbuf);
335 log_info ("BOOTREPLY for %s to %s (%s) via %s",
336 piaddr (lease->ip_addr), hp -> name,
337 print_hw_addr (packet -> raw -> htype,
338 packet -> raw -> hlen,
339 packet -> raw -> chaddr),
340 packet -> raw -> giaddr.s_addr
341 ? inet_ntoa (packet -> raw -> giaddr)
342 : packet -> interface -> name);
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
351 /* If this was gatewayed, send it back to the gateway... */
352 if (raw.giaddr.s_addr) {
353 to.sin_addr = raw.giaddr;
354 to.sin_port = local_port;
355
356 if (fallback_interface) {
357 result = send_packet (fallback_interface,
358 (struct packet *)0,
359 &raw, outgoing.packet_length,
360 from, &to, &hto);
361 goto out;
362 }
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
373 /* Otherwise, broadcast it on the local network. */
374 } else {
375 to.sin_addr = limited_broadcast;
376 to.sin_port = remote_port; /* XXX */
377 }
378
379 errno = 0;
380 result = send_packet (packet -> interface,
381 packet, &raw, outgoing.packet_length,
382 from, &to, &hto);
383 out:
384 if (options)
385 option_state_dereference (&options, MDL);
386 if (lease)
387 lease_dereference (&lease, MDL);
388 if (hp)
389 host_dereference (&hp, MDL);
390 if (host)
391 host_dereference (&host, MDL);
392 }