Import (slightly modified) ru.koi8-r.win.kbd:1.1 from FreeBSD (fjoe):
[dragonfly.git] / contrib / dhcp-3.0 / server / bootp.c
1 /* bootp.c
2
3    BOOTP Protocol support. */
4
5 /*
6  * Copyright (c) 2004 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.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 */
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 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;
67
68         if (packet -> raw -> op != BOOTREQUEST)
69                 return;
70
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.
73          */
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);
81
82
83
84         if (!locate_network (packet)) {
85                 log_info ("%s: network unknown", msgbuf);
86                 return;
87         }
88
89         find_hosts_by_haddr (&hp, packet -> raw -> htype,
90                              packet -> raw -> chaddr,
91                              packet -> raw -> hlen, MDL);
92
93         lease  = (struct lease *)0;
94         find_lease (&lease, packet, packet -> shared_network,
95                     0, 0, (struct lease *)0, MDL);
96
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);
103
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;
114                         }
115                 }
116                 if (hp) {
117                         host_dereference (&hp, MDL);
118                         if (host)
119                                 host_reference (&hp, host, MDL);
120                 }                       
121
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);
129                         }
130                         ack_lease (packet, lease, 0, 0, msgbuf, 0);
131                         goto out;
132                 }
133
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;
147                 }
148
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;
153         }
154
155         /* Run the executable statements to compute the client and server
156            options. */
157         option_state_allocate (&options, MDL);
158         
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);
165         
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);
174         }
175
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);
182         
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;
192         } 
193
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;
203         }
204
205         /* Set up the outgoing packet... */
206         memset (&outgoing, 0, sizeof outgoing);
207         memset (&raw, 0, sizeof raw);
208         outgoing.raw = &raw;
209
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 {
222
223                 /* Use the subnet mask from the subnet declaration if no other
224                    mask has been provided. */
225
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);
239                                 }
240                                 option_cache_dereference (&oc, MDL);
241                         }
242                 }
243
244                 /* Pack the options into the buffer.  Unlike DHCP, we
245                    can't pack options into the filename and server
246                    name buffers. */
247
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;
257         }
258
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);
270
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);
280
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;
299         }
300
301         raw.giaddr = packet -> raw -> giaddr;
302
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);
318
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);
332         }
333
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);
339
340         /* We're done with the option state. */
341         option_state_dereference (&options, MDL);
342
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);
347
348         from = packet -> interface -> primary_address;
349
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);
360
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);
367
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;
372
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;
379                 }
380
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;
389
390         /* Otherwise, broadcast it on the local network. */
391         } else {
392                 to.sin_addr = limited_broadcast;
393                 to.sin_port = remote_port; /* XXX */
394         }
395
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);
411 }