Remove tm.h and xm.h handling, as it wasn't used. Use nm.h only when needed.
[dragonfly.git] / contrib / dhcp-3.0 / server / bootp.c
blob6113fc53e813cf658d558041a6ef978afb06a593
1 /* bootp.c
3 BOOTP Protocol support. */
5 /*
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.
22 * 950 Charter Street
23 * Redwood City, CA 94063
24 * <info@isc.org>
25 * http://www.isc.org/
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''.
35 #ifndef lint
36 static char copyright[] =
37 "$Id: bootp.c,v 1.69.2.8 2004/09/01 17:06:36 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n";
38 #endif /* not lint */
40 #include "dhcpd.h"
42 #if defined (TRACING)
43 # define send_packet trace_packet_send
44 #endif
46 void bootp (packet)
47 struct packet *packet;
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 subnet *subnet;
59 struct lease *lease;
60 struct iaddr ip_address;
61 unsigned i;
62 struct data_string d1;
63 struct option_cache *oc;
64 char msgbuf [1024];
65 int ignorep;
66 int peer_has_leases = 0;
68 if (packet -> raw -> op != BOOTREQUEST)
69 return;
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);
86 return;
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
98 specified network. */
99 subnet = (struct subnet *)0;
100 if (hp)
101 find_host_for_network (&subnet, &hp, &ip_address,
102 packet -> shared_network);
104 if (!subnet) {
105 struct host_decl *h;
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);
113 break;
116 if (hp) {
117 host_dereference (&hp, MDL);
118 if (host)
119 host_reference (&hp, host, MDL);
122 /* If a lease has already been assigned to this client,
123 use it. */
124 if (lease) {
125 if (host && host != lease -> host) {
126 if (lease -> host)
127 host_dereference (&lease -> host, MDL);
128 host_reference (&lease -> host, host, MDL);
130 ack_lease (packet, lease, 0, 0, msgbuf, 0);
131 goto out;
134 /* Otherwise, try to allocate one. */
135 allocate_lease (&lease, packet,
136 packet -> shared_network -> pools,
137 &peer_has_leases);
138 if (lease) {
139 if (host && host != lease -> host) {
140 if (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);
146 goto out;
149 /* We couldn't find an address to give this bootp client. */
150 log_info ("%s: BOOTP from unknown client and no dynamic leases",
151 msgbuf);
152 goto out;
155 /* Run the executable statements to compute the client and server
156 options. */
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,
164 (struct group *)0);
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,
180 &lease -> scope,
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)) {
189 if (!ignorep)
190 log_info ("%s: bootp disallowed", msgbuf);
191 goto out;
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)) {
200 if (!ignorep)
201 log_info ("%s: booting disallowed", msgbuf);
202 goto out;
205 /* Set up the outgoing packet... */
206 memset (&outgoing, 0, sizeof outgoing);
207 memset (&raw, 0, sizeof raw);
208 outgoing.raw = &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;
221 } else {
223 /* Use the subnet mask from the subnet declaration if no other
224 mask has been provided. */
226 oc = (struct option_cache *)0;
227 i = DHO_SUBNET_MASK;
228 if (!lookup_option (&dhcp_universe, options, i)) {
229 if (option_cache_allocate (&oc, MDL)) {
230 if (make_const_data
231 (&oc -> expression,
232 lease -> subnet -> netmask.iabuf,
233 lease -> subnet -> netmask.len,
234 0, 0, MDL)) {
235 oc -> option =
236 dhcp_universe.options [i];
237 save_option (&dhcp_universe,
238 options, oc);
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
246 name buffers. */
248 outgoing.packet_length =
249 cons_options (packet, outgoing.raw, lease,
250 (struct client_state *)0, 0,
251 packet -> options, options,
252 &lease -> scope,
253 0, 0, 1, (struct data_string *)0,
254 (const char *)0);
255 if (outgoing.packet_length < BOOTP_MIN_LEN)
256 outgoing.packet_length = BOOTP_MIN_LEN;
259 /* Take the fields that we care about... */
260 raw.op = BOOTREPLY;
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);
284 if (oc &&
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);
293 } else {
294 if (lease -> subnet -> shared_network -> interface)
295 raw.siaddr = (lease -> subnet -> shared_network ->
296 interface -> primary_address);
297 else
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);
305 if (oc &&
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);
316 } else
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);
321 if (oc &&
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,
337 packet -> options,
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;
363 #ifdef HAVE_SA_LEN
364 to.sin_len = sizeof to;
365 #endif
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,
375 (struct packet *)0,
376 &raw, outgoing.packet_length,
377 from, &to, &hto);
378 goto out;
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. */
391 } else {
392 to.sin_addr = limited_broadcast;
393 to.sin_port = remote_port; /* XXX */
396 errno = 0;
397 result = send_packet (packet -> interface,
398 packet, &raw, outgoing.packet_length,
399 from, &to, &hto);
400 out:
401 if (options)
402 option_state_dereference (&options, MDL);
403 if (lease)
404 lease_dereference (&lease, MDL);
405 if (hp)
406 host_dereference (&hp, MDL);
407 if (host)
408 host_dereference (&host, MDL);
409 if (subnet)
410 subnet_dereference (&subnet, MDL);