]>
Commit | Line | Data |
---|---|---|
469cf3a4 | 1 | /* dhclient.c |
95821729 | 2 | |
9bdb9271 | 3 | DHCP Client. */ |
95821729 TL |
4 | |
5 | /* | |
49733f31 TL |
6 | * Copyright (c) 1995-2000 Internet Software Consortium. |
7 | * All rights reserved. | |
95821729 | 8 | * |
49733f31 TL |
9 | * Redistribution and use in source and binary forms, with or without |
10 | * modification, are permitted provided that the following conditions | |
11 | * are met: | |
95821729 | 12 | * |
49733f31 TL |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | |
18 | * 3. Neither the name of The Internet Software Consortium nor the names | |
19 | * of its contributors may be used to endorse or promote products derived | |
20 | * from this software without specific prior written permission. | |
95821729 | 21 | * |
49733f31 TL |
22 | * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND |
23 | * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, | |
24 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
25 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
26 | * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR | |
27 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
28 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
29 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |
30 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
31 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
33 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
34 | * SUCH DAMAGE. | |
31730f17 TL |
35 | * |
36 | * This code was originally written by Ted Lemon. Elliot Poger wrote | |
37 | * a state machine to fully implement the client side of the DHCP | |
38 | * protocol. Ted Lemon then added the configuration file, stuffed the | |
39 | * state machine into its own data structure so there could be more | |
40 | * than one, and added the client scripting code to produce the first | |
41 | * ISC release (2.0b1pl0) of the client. | |
95821729 TL |
42 | */ |
43 | ||
c1503c57 | 44 | #ifndef lint |
339b0231 | 45 | static char ocopyright[] = |
49733f31 | 46 | "$Id: dhclient.c,v 1.98 2000/03/17 03:58:56 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium. All rights reserved.\n"; |
c1503c57 | 47 | #endif /* not lint */ |
95821729 | 48 | |
c1503c57 | 49 | #include "dhcpd.h" |
31020a36 | 50 | #include "version.h" |
e581d615 | 51 | |
c1503c57 | 52 | TIME cur_time; |
4a0d788a TL |
53 | TIME default_lease_time = 43200; /* 12 hours... */ |
54 | TIME max_lease_time = 86400; /* 24 hours... */ | |
55 | ||
b1b7b521 TL |
56 | const char *path_dhclient_conf = _PATH_DHCLIENT_CONF; |
57 | const char *path_dhclient_db = _PATH_DHCLIENT_DB; | |
58 | const char *path_dhclient_pid = _PATH_DHCLIENT_PID; | |
cc26de46 | 59 | |
36f5cb7c TL |
60 | int dhcp_max_agent_option_packet_length = 0; |
61 | ||
b8cf055d TL |
62 | int interfaces_requested = 0; |
63 | ||
cc26de46 TL |
64 | struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } }; |
65 | struct iaddr iaddr_any = { 4, { 0, 0, 0, 0 } }; | |
48d68880 | 66 | struct in_addr inaddr_any; |
cc26de46 | 67 | struct sockaddr_in sockaddr_broadcast; |
11373fb6 | 68 | struct in_addr giaddr; |
469cf3a4 | 69 | |
cf78bf20 TL |
70 | struct binding_scope global_scope; |
71 | ||
cc26de46 TL |
72 | /* ASSERT_STATE() does nothing now; it used to be |
73 | assert (state_is == state_shouldbe). */ | |
74 | #define ASSERT_STATE(state_is, state_shouldbe) {} | |
469cf3a4 | 75 | |
62d0cb47 TL |
76 | static char copyright[] = |
77 | "Copyright 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium."; | |
78 | static char arr [] = "All rights reserved."; | |
31020a36 | 79 | static char message [] = "Internet Software Consortium DHCP Client"; |
62d0cb47 TL |
80 | static char contrib [] = "\nPlease contribute if you find this software useful."; |
81 | static char url [] = "For info, please visit http://www.isc.org/dhcp-contrib.html\n"; | |
c1503c57 | 82 | |
cc26de46 TL |
83 | u_int16_t local_port; |
84 | u_int16_t remote_port; | |
b00d3884 | 85 | int no_daemon; |
126965a9 | 86 | int save_scripts; |
c1503c57 | 87 | |
c1503c57 | 88 | static void usage PROTO ((void)); |
95821729 | 89 | |
347de8bd TL |
90 | void do_release(struct client_state *); |
91 | ||
95821729 TL |
92 | int main (argc, argv, envp) |
93 | int argc; | |
c1503c57 | 94 | char **argv, **envp; |
95821729 | 95 | { |
c1503c57 | 96 | int i; |
c1503c57 | 97 | struct servent *ent; |
cc26de46 | 98 | struct interface_info *ip; |
02d9e453 | 99 | struct client_state *client; |
b1b7b521 | 100 | unsigned seed; |
74f45f96 | 101 | int quiet = 0; |
3020a2c1 | 102 | char *server = (char *)0; |
11373fb6 | 103 | char *relay = (char *)0; |
628beb0e | 104 | isc_result_t status; |
347de8bd TL |
105 | int release_mode = 0; |
106 | omapi_object_t *listener; | |
107 | isc_result_t result; | |
6c6d5928 | 108 | |
c1503c57 TL |
109 | #ifdef SYSLOG_4_2 |
110 | openlog ("dhclient", LOG_NDELAY); | |
b5ac808e | 111 | log_priority = LOG_DAEMON; |
c1503c57 TL |
112 | #else |
113 | openlog ("dhclient", LOG_NDELAY, LOG_DAEMON); | |
114 | #endif | |
95821729 | 115 | |
66f973e4 | 116 | #if !(defined (DEBUG) || defined (SYSLOG_4_2) || defined (__CYGWIN32__)) |
c1503c57 TL |
117 | setlogmask (LOG_UPTO (LOG_INFO)); |
118 | #endif | |
119 | ||
120 | for (i = 1; i < argc; i++) { | |
347de8bd TL |
121 | if (!strcmp (argv [i], "-r")) { |
122 | release_mode = 1; | |
123 | no_daemon = 1; | |
124 | } else if (!strcmp (argv [i], "-p")) { | |
c1503c57 TL |
125 | if (++i == argc) |
126 | usage (); | |
cc26de46 | 127 | local_port = htons (atoi (argv [i])); |
8ae2d595 | 128 | log_debug ("binding to user-specified port %d", |
cc26de46 | 129 | ntohs (local_port)); |
b00d3884 TL |
130 | } else if (!strcmp (argv [i], "-d")) { |
131 | no_daemon = 1; | |
126965a9 TL |
132 | } else if (!strcmp (argv [i], "-D")) { |
133 | save_scripts = 1; | |
73530742 TL |
134 | } else if (!strcmp (argv [i], "-pf")) { |
135 | if (++i == argc) | |
136 | usage (); | |
137 | path_dhclient_pid = argv [i]; | |
138 | } else if (!strcmp (argv [i], "-cf")) { | |
139 | if (++i == argc) | |
140 | usage (); | |
141 | path_dhclient_conf = argv [i]; | |
142 | } else if (!strcmp (argv [i], "-lf")) { | |
143 | if (++i == argc) | |
144 | usage (); | |
145 | path_dhclient_db = argv [i]; | |
62d0cb47 TL |
146 | } else if (!strcmp (argv [i], "-q")) { |
147 | quiet = 1; | |
148 | quiet_interface_discovery = 1; | |
3020a2c1 TL |
149 | } else if (!strcmp (argv [i], "-s")) { |
150 | if (++i == argc) | |
151 | usage (); | |
152 | server = argv [i]; | |
11373fb6 TL |
153 | } else if (!strcmp (argv [i], "-g")) { |
154 | if (++i == argc) | |
155 | usage (); | |
156 | relay = argv [i]; | |
347de8bd TL |
157 | } else if (!strcmp (argv [i], "-n")) { |
158 | /* do not start up any interfaces */ | |
159 | interfaces_requested = 1; | |
469cf3a4 TL |
160 | } else if (argv [i][0] == '-') { |
161 | usage (); | |
162 | } else { | |
cf78bf20 TL |
163 | struct interface_info *tmp = ((struct interface_info *) |
164 | dmalloc (sizeof *tmp, MDL)); | |
469cf3a4 | 165 | if (!tmp) |
8ae2d595 | 166 | log_fatal ("Insufficient memory to %s %s", |
469cf3a4 TL |
167 | "record interface", argv [i]); |
168 | memset (tmp, 0, sizeof *tmp); | |
169 | strcpy (tmp -> name, argv [i]); | |
170 | tmp -> next = interfaces; | |
171 | tmp -> flags = INTERFACE_REQUESTED; | |
b8cf055d | 172 | interfaces_requested = 1; |
469cf3a4 TL |
173 | interfaces = tmp; |
174 | } | |
c1503c57 | 175 | } |
62d0cb47 | 176 | |
347de8bd TL |
177 | /* first kill of any currently running client */ |
178 | if (release_mode) { | |
179 | /* XXX inelegant hack to prove concept */ | |
180 | char command[1024]; | |
181 | ||
9458e9aa | 182 | #if !defined (NO_SNPRINTF) |
7804c030 | 183 | snprintf (command, 1024, "kill `cat %s`", path_dhclient_pid); |
9458e9aa | 184 | #else |
7804c030 | 185 | sprintf (command, "kill `cat %s`", path_dhclient_pid); |
9458e9aa | 186 | #endif |
347de8bd TL |
187 | system (command); |
188 | } | |
189 | ||
62d0cb47 | 190 | if (!quiet) { |
31020a36 | 191 | log_info ("%s %s", message, DHCP_VERSION); |
8ae2d595 TL |
192 | log_info (copyright); |
193 | log_info (arr); | |
194 | log_info (contrib); | |
195 | log_info (url); | |
78a33b82 TL |
196 | } else |
197 | log_perror = 0; | |
62d0cb47 | 198 | |
11373fb6 TL |
199 | /* If we're given a relay agent address to insert, for testing |
200 | purposes, figure out what it is. */ | |
201 | if (relay) { | |
202 | if (!inet_aton (relay, &giaddr)) { | |
203 | struct hostent *he; | |
204 | he = gethostbyname (relay); | |
205 | if (he) { | |
206 | memcpy (&giaddr, he -> h_addr_list [0], | |
207 | sizeof giaddr); | |
208 | } else { | |
209 | log_fatal ("%s: no such host", relay); | |
210 | } | |
211 | } | |
212 | } | |
213 | ||
c1503c57 | 214 | /* Default to the DHCP/BOOTP port. */ |
cc26de46 | 215 | if (!local_port) { |
11373fb6 TL |
216 | if (relay && giaddr.s_addr != INADDR_LOOPBACK) { |
217 | local_port = 67; | |
218 | } else { | |
219 | ent = getservbyname ("dhcpc", "udp"); | |
220 | if (!ent) | |
221 | local_port = htons (68); | |
222 | else | |
223 | local_port = ent -> s_port; | |
66f973e4 | 224 | #ifndef __CYGWIN32__ |
11373fb6 | 225 | endservent (); |
66f973e4 | 226 | #endif |
11373fb6 | 227 | } |
c1503c57 | 228 | } |
11373fb6 TL |
229 | |
230 | /* If we're faking a relay agent, and we're not using loopback, | |
231 | use the server port, not the client port. */ | |
232 | if (relay && giaddr.s_addr != INADDR_LOOPBACK) | |
233 | remote_port = 67; | |
234 | else | |
235 | remote_port = htons (ntohs (local_port) - 1); /* XXX */ | |
c1503c57 | 236 | |
95821729 TL |
237 | /* Get the current time... */ |
238 | GET_TIME (&cur_time); | |
239 | ||
b00d3884 | 240 | sockaddr_broadcast.sin_family = AF_INET; |
cc26de46 | 241 | sockaddr_broadcast.sin_port = remote_port; |
3020a2c1 TL |
242 | if (server) { |
243 | if (!inet_aton (server, &sockaddr_broadcast.sin_addr)) { | |
244 | struct hostent *he; | |
245 | he = gethostbyname (server); | |
246 | if (he) { | |
247 | memcpy (&sockaddr_broadcast.sin_addr, | |
248 | he -> h_addr_list [0], | |
249 | sizeof sockaddr_broadcast.sin_addr); | |
250 | } else | |
251 | sockaddr_broadcast.sin_addr.s_addr = | |
252 | INADDR_BROADCAST; | |
253 | } | |
254 | } else { | |
255 | sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST; | |
256 | } | |
11373fb6 | 257 | |
48d68880 | 258 | inaddr_any.s_addr = INADDR_ANY; |
469cf3a4 | 259 | |
628beb0e TL |
260 | /* Set up the OMAPI. */ |
261 | status = omapi_init (); | |
262 | if (status != ISC_R_SUCCESS) | |
263 | log_fatal ("Can't initialize OMAPI: %s", | |
264 | isc_result_totext (status)); | |
265 | ||
347de8bd TL |
266 | /* Set up the OMAPI wrappers for various server database internal |
267 | objects. */ | |
268 | dhclient_db_objects_setup (); | |
269 | ||
cc26de46 TL |
270 | /* Discover all the network interfaces. */ |
271 | discover_interfaces (DISCOVER_UNCONFIGURED); | |
469cf3a4 | 272 | |
cc26de46 TL |
273 | /* Parse the dhclient.conf file. */ |
274 | read_client_conf (); | |
469cf3a4 | 275 | |
cc26de46 TL |
276 | /* Parse the lease database. */ |
277 | read_client_leases (); | |
469cf3a4 | 278 | |
cc26de46 TL |
279 | /* Rewrite the lease database... */ |
280 | rewrite_client_leases (); | |
469cf3a4 | 281 | |
ce0ec46d TL |
282 | /* XXX */ |
283 | /* config_counter(&snd_counter, &rcv_counter); */ | |
284 | ||
cc26de46 TL |
285 | /* If no broadcast interfaces were discovered, call the script |
286 | and tell it so. */ | |
287 | if (!interfaces) { | |
02d9e453 | 288 | script_init ((struct client_state *)0, "NBI", |
9bdb9271 | 289 | (struct string_list *)0); |
02d9e453 | 290 | script_go ((struct client_state *)0); |
469cf3a4 | 291 | |
8ae2d595 | 292 | log_info ("No broadcast interfaces found - exiting."); |
62d0cb47 | 293 | |
cc26de46 TL |
294 | /* Nothing more to do. */ |
295 | exit (0); | |
347de8bd | 296 | } else if (!release_mode) { |
cc26de46 TL |
297 | /* Call the script with the list of interfaces. */ |
298 | for (ip = interfaces; ip; ip = ip -> next) { | |
5c2f78b4 TL |
299 | /* If interfaces were specified, don't configure |
300 | interfaces that weren't specified! */ | |
301 | if (interfaces_requested && | |
302 | ((ip -> flags & (INTERFACE_REQUESTED | | |
62d0cb47 TL |
303 | INTERFACE_AUTOMATIC)) != |
304 | INTERFACE_REQUESTED)) | |
5c2f78b4 | 305 | continue; |
02d9e453 TL |
306 | script_init (ip -> client, |
307 | "PREINIT", (struct string_list *)0); | |
f79e49f3 | 308 | if (ip -> client -> alias) |
02d9e453 | 309 | script_write_params (ip -> client, "alias_", |
f79e49f3 | 310 | ip -> client -> alias); |
02d9e453 | 311 | script_go (ip -> client); |
cc26de46 TL |
312 | } |
313 | } | |
469cf3a4 | 314 | |
cc26de46 TL |
315 | /* At this point, all the interfaces that the script thinks |
316 | are relevant should be running, so now we once again call | |
317 | discover_interfaces(), and this time ask it to actually set | |
318 | up the interfaces. */ | |
b8cf055d TL |
319 | discover_interfaces (interfaces_requested |
320 | ? DISCOVER_REQUESTED | |
321 | : DISCOVER_RUNNING); | |
cc26de46 | 322 | |
66f973e4 TL |
323 | /* Make up a seed for the random number generator from current |
324 | time plus the sum of the last four bytes of each | |
325 | interface's hardware address interpreted as an integer. | |
326 | Not much entropy, but we're booting, so we're not likely to | |
327 | find anything better. */ | |
02d9e453 | 328 | seed = 0; |
66f973e4 TL |
329 | for (ip = interfaces; ip; ip = ip -> next) { |
330 | int junk; | |
331 | memcpy (&junk, | |
31730f17 TL |
332 | &ip -> hw_address.hbuf [ip -> hw_address.hlen - |
333 | sizeof seed], sizeof seed); | |
66f973e4 TL |
334 | seed += junk; |
335 | } | |
336 | srandom (seed + cur_time); | |
337 | ||
cc26de46 TL |
338 | /* Start a configuration state machine for each interface. */ |
339 | for (ip = interfaces; ip; ip = ip -> next) { | |
347de8bd | 340 | ip -> flags |= INTERFACE_RUNNING; |
02d9e453 | 341 | for (client = ip -> client; client; client = client -> next) { |
347de8bd TL |
342 | if (release_mode) |
343 | do_release (client); | |
344 | else { | |
345 | client -> state = S_INIT; | |
346 | /* Set up a timeout to start the initialization | |
347 | process. */ | |
348 | add_timeout (cur_time + random () % 5, | |
349 | state_reboot, client); | |
350 | } | |
02d9e453 | 351 | } |
cc26de46 | 352 | } |
469cf3a4 | 353 | |
347de8bd TL |
354 | if (release_mode) |
355 | return 0; | |
356 | ||
357 | /* Start up a listener for the object management API protocol. */ | |
358 | listener = (omapi_object_t *)0; | |
359 | result = omapi_generic_new (&listener, MDL); | |
360 | if (result != ISC_R_SUCCESS) | |
361 | log_fatal ("Can't allocate new generic object: %s\n", | |
362 | isc_result_totext (result)); | |
363 | result = omapi_protocol_listen (listener, | |
364 | OMAPI_PROTOCOL_PORT, 1); | |
365 | if (result != ISC_R_SUCCESS) | |
366 | log_fatal ("Can't start OMAPI protocol: %s", | |
367 | isc_result_totext (result)); | |
368 | ||
84c4adde TL |
369 | /* Set up the bootp packet handler... */ |
370 | bootp_packet_handler = do_packet; | |
371 | ||
cc26de46 | 372 | /* Start dispatching packets and timeouts... */ |
84c4adde TL |
373 | dispatch (); |
374 | ||
cc26de46 TL |
375 | /*NOTREACHED*/ |
376 | return 0; | |
469cf3a4 TL |
377 | } |
378 | ||
c1503c57 TL |
379 | static void usage () |
380 | { | |
3020a2c1 TL |
381 | log_error ("Usage: dhclient [-d] [-D] [-q] [-p <port>] %s", |
382 | "[-s server]"); | |
383 | log_error (" [-lf lease-file] [-pf pid-file]%s", | |
384 | "[-cf config-file] [interface]"); | |
c1503c57 TL |
385 | } |
386 | ||
02a015fb | 387 | struct class *find_class (s) |
b1b7b521 | 388 | const char *s; |
02a015fb TL |
389 | { |
390 | return (struct class *)0; | |
391 | } | |
392 | ||
da38df14 | 393 | int check_collection (packet, lease, collection) |
02a015fb | 394 | struct packet *packet; |
da38df14 | 395 | struct lease *lease; |
02a015fb TL |
396 | struct collection *collection; |
397 | { | |
398 | return 0; | |
399 | } | |
400 | ||
401 | void classify (packet, class) | |
402 | struct packet *packet; | |
403 | struct class *class; | |
404 | { | |
405 | } | |
406 | ||
73530742 TL |
407 | int unbill_class (lease, class) |
408 | struct lease *lease; | |
409 | struct class *class; | |
410 | { | |
411 | return 0; | |
412 | } | |
413 | ||
bfdb842e TL |
414 | struct subnet *find_subnet (addr) |
415 | struct iaddr addr; | |
416 | { | |
417 | return (struct subnet *)0; | |
418 | } | |
419 | ||
469cf3a4 TL |
420 | /* Individual States: |
421 | * | |
422 | * Each routine is called from the dhclient_state_machine() in one of | |
423 | * these conditions: | |
424 | * -> entering INIT state | |
425 | * -> recvpacket_flag == 0: timeout in this state | |
426 | * -> otherwise: received a packet in this state | |
427 | * | |
428 | * Return conditions as handled by dhclient_state_machine(): | |
429 | * Returns 1, sendpacket_flag = 1: send packet, reset timer. | |
430 | * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone). | |
431 | * Returns 0: finish the nap which was interrupted for no good reason. | |
432 | * | |
cc26de46 TL |
433 | * Several per-interface variables are used to keep track of the process: |
434 | * active_lease: the lease that is being used on the interface | |
435 | * (null pointer if not configured yet). | |
436 | * offered_leases: leases corresponding to DHCPOFFER messages that have | |
437 | * been sent to us by DHCP servers. | |
438 | * acked_leases: leases corresponding to DHCPACK messages that have been | |
439 | * sent to us by DHCP servers. | |
440 | * sendpacket: DHCP packet we're trying to send. | |
469cf3a4 | 441 | * destination: IP address to send sendpacket to |
cc26de46 | 442 | * In addition, there are several relevant per-lease variables. |
469cf3a4 | 443 | * T1_expiry, T2_expiry, lease_expiry: lease milestones |
cc26de46 TL |
444 | * In the active lease, these control the process of renewing the lease; |
445 | * In leases on the acked_leases list, this simply determines when we | |
446 | * can no longer legitimately use the lease. | |
469cf3a4 TL |
447 | */ |
448 | ||
02d9e453 TL |
449 | void state_reboot (cpp) |
450 | void *cpp; | |
deff2d59 | 451 | { |
02d9e453 | 452 | struct client_state *client = cpp; |
84c4adde | 453 | |
deff2d59 | 454 | /* If we don't remember an active lease, go straight to INIT. */ |
02d9e453 TL |
455 | if (!client -> active || |
456 | client -> active -> is_bootp) { | |
457 | state_init (client); | |
deff2d59 TL |
458 | return; |
459 | } | |
460 | ||
461 | /* We are in the rebooting state. */ | |
02d9e453 | 462 | client -> state = S_REBOOTING; |
deff2d59 | 463 | |
5c2f78b4 TL |
464 | /* make_request doesn't initialize xid because it normally comes |
465 | from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER, | |
466 | so pick an xid now. */ | |
02d9e453 | 467 | client -> xid = random (); |
5c2f78b4 | 468 | |
deff2d59 TL |
469 | /* Make a DHCPREQUEST packet, and set appropriate per-interface |
470 | flags. */ | |
02d9e453 TL |
471 | make_request (client, client -> active); |
472 | client -> destination = iaddr_broadcast; | |
473 | client -> first_sending = cur_time; | |
474 | client -> interval = client -> config -> initial_interval; | |
deff2d59 | 475 | |
b8cf055d | 476 | /* Zap the medium list... */ |
02d9e453 | 477 | client -> medium = (struct string_list *)0; |
b8cf055d TL |
478 | |
479 | /* Send out the first DHCPREQUEST packet. */ | |
02d9e453 | 480 | send_request (client); |
deff2d59 TL |
481 | } |
482 | ||
483 | /* Called when a lease has completely expired and we've been unable to | |
484 | renew it. */ | |
cc26de46 | 485 | |
02d9e453 TL |
486 | void state_init (cpp) |
487 | void *cpp; | |
469cf3a4 | 488 | { |
02d9e453 | 489 | struct client_state *client = cpp; |
84c4adde | 490 | |
469cf3a4 TL |
491 | ASSERT_STATE(state, S_INIT); |
492 | ||
cc26de46 TL |
493 | /* Make a DHCPDISCOVER packet, and set appropriate per-interface |
494 | flags. */ | |
02d9e453 TL |
495 | make_discover (client, client -> active); |
496 | client -> xid = client -> packet.xid; | |
497 | client -> destination = iaddr_broadcast; | |
498 | client -> state = S_SELECTING; | |
499 | client -> first_sending = cur_time; | |
500 | client -> interval = client -> config -> initial_interval; | |
cc26de46 TL |
501 | |
502 | /* Add an immediate timeout to cause the first DHCPDISCOVER packet | |
503 | to go out. */ | |
02d9e453 | 504 | send_discover (client); |
469cf3a4 TL |
505 | } |
506 | ||
cc26de46 TL |
507 | /* state_selecting is called when one or more DHCPOFFER packets have been |
508 | received and a configurable period of time has passed. */ | |
509 | ||
02d9e453 TL |
510 | void state_selecting (cpp) |
511 | void *cpp; | |
469cf3a4 | 512 | { |
02d9e453 | 513 | struct client_state *client = cpp; |
b00d3884 TL |
514 | struct client_lease *lp, *next, *picked; |
515 | ||
02d9e453 | 516 | |
469cf3a4 TL |
517 | ASSERT_STATE(state, S_SELECTING); |
518 | ||
cc26de46 TL |
519 | /* Cancel state_selecting and send_discover timeouts, since either |
520 | one could have got us here. */ | |
02d9e453 TL |
521 | cancel_timeout (state_selecting, client); |
522 | cancel_timeout (send_discover, client); | |
cc26de46 | 523 | |
b00d3884 TL |
524 | /* We have received one or more DHCPOFFER packets. Currently, |
525 | the only criterion by which we judge leases is whether or | |
526 | not we get a response when we arp for them. */ | |
527 | picked = (struct client_lease *)0; | |
02d9e453 | 528 | for (lp = client -> offered_leases; lp; lp = next) { |
b00d3884 TL |
529 | next = lp -> next; |
530 | ||
531 | /* Check to see if we got an ARPREPLY for the address | |
532 | in this particular lease. */ | |
533 | if (!picked) { | |
b00d3884 TL |
534 | picked = lp; |
535 | picked -> next = (struct client_lease *)0; | |
536 | } else { | |
537 | freeit: | |
02a015fb | 538 | destroy_client_lease (lp); |
b00d3884 TL |
539 | } |
540 | } | |
02d9e453 | 541 | client -> offered_leases = (struct client_lease *)0; |
b00d3884 TL |
542 | |
543 | /* If we just tossed all the leases we were offered, go back | |
544 | to square one. */ | |
545 | if (!picked) { | |
02d9e453 TL |
546 | client -> state = S_INIT; |
547 | state_init (client); | |
b00d3884 TL |
548 | return; |
549 | } | |
550 | ||
66f973e4 | 551 | /* If it was a BOOTREPLY, we can just take the address right now. */ |
2455808f | 552 | if (picked -> is_bootp) { |
02d9e453 | 553 | client -> new = picked; |
66f973e4 TL |
554 | |
555 | /* Make up some lease expiry times | |
556 | XXX these should be configurable. */ | |
02d9e453 TL |
557 | client -> new -> expiry = cur_time + 12000; |
558 | client -> new -> renewal += cur_time + 8000; | |
559 | client -> new -> rebind += cur_time + 10000; | |
66f973e4 | 560 | |
02d9e453 | 561 | client -> state = S_REQUESTING; |
66f973e4 TL |
562 | |
563 | /* Bind to the address we received. */ | |
02d9e453 | 564 | bind_lease (client); |
66f973e4 TL |
565 | return; |
566 | } | |
567 | ||
b00d3884 | 568 | /* Go to the REQUESTING state. */ |
02d9e453 TL |
569 | client -> destination = iaddr_broadcast; |
570 | client -> state = S_REQUESTING; | |
571 | client -> first_sending = cur_time; | |
572 | client -> interval = client -> config -> initial_interval; | |
cc26de46 | 573 | |
b00d3884 | 574 | /* Make a DHCPREQUEST packet from the lease we picked. */ |
02d9e453 TL |
575 | make_request (client, picked); |
576 | client -> xid = client -> packet.xid; | |
469cf3a4 | 577 | |
b00d3884 | 578 | /* Toss the lease we picked - we'll get it back in a DHCPACK. */ |
02a015fb | 579 | destroy_client_lease (picked); |
b00d3884 | 580 | |
cc26de46 | 581 | /* Add an immediate timeout to send the first DHCPREQUEST packet. */ |
02d9e453 | 582 | send_request (client); |
469cf3a4 TL |
583 | } |
584 | ||
cc26de46 TL |
585 | /* state_requesting is called when we receive a DHCPACK message after |
586 | having sent out one or more DHCPREQUEST packets. */ | |
469cf3a4 | 587 | |
cc26de46 TL |
588 | void dhcpack (packet) |
589 | struct packet *packet; | |
469cf3a4 | 590 | { |
cc26de46 | 591 | struct interface_info *ip = packet -> interface; |
02d9e453 | 592 | struct client_state *client; |
cc26de46 | 593 | struct client_lease *lease; |
02a015fb TL |
594 | struct option_cache *oc; |
595 | struct data_string ds; | |
cc26de46 TL |
596 | int i; |
597 | ||
cc26de46 TL |
598 | /* If we're not receptive to an offer right now, or if the offer |
599 | has an unrecognizable transaction id, then just drop it. */ | |
02d9e453 TL |
600 | for (client = ip -> client; client; client = client -> next) { |
601 | if (client -> xid == packet -> raw -> xid) | |
602 | break; | |
603 | } | |
604 | if (!client || | |
31730f17 | 605 | (packet -> interface -> hw_address.hlen - 1 != |
5c2f78b4 | 606 | packet -> raw -> hlen) || |
31730f17 | 607 | (memcmp (&packet -> interface -> hw_address.hbuf [1], |
fcaec4ef | 608 | packet -> raw -> chaddr, packet -> raw -> hlen))) { |
2d1b06e0 | 609 | #if defined (DEBUG) |
8ae2d595 | 610 | log_debug ("DHCPACK in wrong transaction."); |
2d1b06e0 | 611 | #endif |
cc26de46 | 612 | return; |
469cf3a4 | 613 | } |
469cf3a4 | 614 | |
02d9e453 TL |
615 | if (client -> state != S_REBOOTING && |
616 | client -> state != S_REQUESTING && | |
617 | client -> state != S_RENEWING && | |
618 | client -> state != S_REBINDING) { | |
2d1b06e0 | 619 | #if defined (DEBUG) |
8ae2d595 | 620 | log_debug ("DHCPACK in wrong state."); |
2d1b06e0 | 621 | #endif |
cc26de46 | 622 | return; |
469cf3a4 TL |
623 | } |
624 | ||
8ae2d595 | 625 | log_info ("DHCPACK from %s", piaddr (packet -> client_addr)); |
34fdad6c | 626 | |
cc26de46 TL |
627 | lease = packet_to_lease (packet); |
628 | if (!lease) { | |
8ae2d595 | 629 | log_info ("packet_to_lease failed."); |
cc26de46 | 630 | return; |
469cf3a4 TL |
631 | } |
632 | ||
02d9e453 | 633 | client -> new = lease; |
cc26de46 TL |
634 | |
635 | /* Stop resending DHCPREQUEST. */ | |
02d9e453 | 636 | cancel_timeout (send_request, client); |
cc26de46 TL |
637 | |
638 | /* Figure out the lease time. */ | |
230e73e4 | 639 | oc = lookup_option (&dhcp_universe, client -> new -> options, |
02a015fb TL |
640 | DHO_DHCP_LEASE_TIME); |
641 | memset (&ds, 0, sizeof ds); | |
642 | if (oc && | |
0852a27f TL |
643 | evaluate_option_cache (&ds, packet, (struct lease *)0, |
644 | packet -> options, client -> new -> options, | |
cf78bf20 | 645 | &global_scope, oc, MDL)) { |
02a015fb | 646 | if (ds.len > 3) |
02d9e453 | 647 | client -> new -> expiry = getULong (ds.data); |
02a015fb | 648 | else |
02d9e453 | 649 | client -> new -> expiry = 0; |
cf78bf20 | 650 | data_string_forget (&ds, MDL); |
02a015fb | 651 | } else |
02d9e453 | 652 | client -> new -> expiry = 0; |
02a015fb | 653 | |
02d9e453 | 654 | if (!client -> new -> expiry) { |
8ae2d595 | 655 | log_error ("no expiry time on offered lease."); |
02a015fb TL |
656 | /* XXX this is going to be bad - if this _does_ |
657 | XXX happen, we should probably dynamically | |
658 | XXX disqualify the DHCP server that gave us the | |
659 | XXX bad packet from future selections and | |
660 | XXX then go back into the init state. */ | |
02d9e453 | 661 | state_init (client); |
02a015fb TL |
662 | return; |
663 | } | |
cc26de46 | 664 | |
62d0cb47 TL |
665 | /* A number that looks negative here is really just very large, |
666 | because the lease expiry offset is unsigned. */ | |
667 | if (client -> new -> expiry < 0) | |
668 | client -> new -> expiry = TIME_MAX; | |
02a015fb | 669 | /* Take the server-provided renewal time if there is one. */ |
230e73e4 | 670 | oc = lookup_option (&dhcp_universe, client -> new -> options, |
02a015fb TL |
671 | DHO_DHCP_RENEWAL_TIME); |
672 | if (oc && | |
0852a27f TL |
673 | evaluate_option_cache (&ds, packet, (struct lease *)0, |
674 | packet -> options, client -> new -> options, | |
cf78bf20 | 675 | &global_scope, oc, MDL)) { |
02a015fb | 676 | if (ds.len > 3) |
02d9e453 | 677 | client -> new -> renewal = getULong (ds.data); |
02a015fb | 678 | else |
02d9e453 | 679 | client -> new -> renewal = 0; |
cf78bf20 | 680 | data_string_forget (&ds, MDL); |
02a015fb | 681 | } else |
02d9e453 | 682 | client -> new -> renewal = 0; |
02a015fb TL |
683 | |
684 | /* If it wasn't specified by the server, calculate it. */ | |
02d9e453 TL |
685 | if (!client -> new -> renewal) |
686 | client -> new -> renewal = | |
687 | client -> new -> expiry / 2; | |
cc26de46 | 688 | |
11373fb6 TL |
689 | /* Now introduce some randomness to the renewal time: */ |
690 | client -> new -> renewal = (((client -> new -> renewal + 3) * 3 / 4) + | |
691 | (random () % /* XXX NUMS */ | |
692 | ((client -> new -> renewal + 3) / 4))); | |
693 | ||
cc26de46 | 694 | /* Same deal with the rebind time. */ |
230e73e4 | 695 | oc = lookup_option (&dhcp_universe, client -> new -> options, |
02a015fb TL |
696 | DHO_DHCP_REBINDING_TIME); |
697 | if (oc && | |
0852a27f TL |
698 | evaluate_option_cache (&ds, packet, (struct lease *)0, |
699 | packet -> options, client -> new -> options, | |
cf78bf20 | 700 | &global_scope, oc, MDL)) { |
02a015fb | 701 | if (ds.len > 3) |
02d9e453 | 702 | client -> new -> rebind = getULong (ds.data); |
02a015fb | 703 | else |
02d9e453 | 704 | client -> new -> rebind = 0; |
cf78bf20 | 705 | data_string_forget (&ds, MDL); |
02a015fb | 706 | } else |
02d9e453 | 707 | client -> new -> rebind = 0; |
02a015fb | 708 | |
02d9e453 TL |
709 | if (!client -> new -> rebind) |
710 | client -> new -> rebind = | |
11373fb6 TL |
711 | (client -> new -> expiry * 7) / 8; /* XXX NUMS */ |
712 | ||
713 | /* Make sure our randomness didn't run the renewal time past the | |
714 | rebind time. */ | |
715 | if (client -> new -> renewal > client -> new -> rebind) | |
716 | client -> new -> renewal = (client -> new -> rebind * 3) / 4; | |
cc26de46 | 717 | |
02d9e453 | 718 | client -> new -> expiry += cur_time; |
62d0cb47 TL |
719 | /* Lease lengths can never be negative. */ |
720 | if (client -> new -> expiry < cur_time) | |
721 | client -> new -> expiry = TIME_MAX; | |
02d9e453 | 722 | client -> new -> renewal += cur_time; |
62d0cb47 TL |
723 | if (client -> new -> renewal < cur_time) |
724 | client -> new -> renewal = TIME_MAX; | |
02d9e453 | 725 | client -> new -> rebind += cur_time; |
62d0cb47 TL |
726 | if (client -> new -> rebind < cur_time) |
727 | client -> new -> rebind = TIME_MAX; | |
cc26de46 | 728 | |
02d9e453 | 729 | bind_lease (client); |
66f973e4 TL |
730 | } |
731 | ||
02d9e453 TL |
732 | void bind_lease (client) |
733 | struct client_state *client; | |
66f973e4 | 734 | { |
02d9e453 TL |
735 | struct interface_info *ip = client -> interface; |
736 | ||
9bdb9271 | 737 | /* Remember the medium. */ |
02d9e453 | 738 | client -> new -> medium = client -> medium; |
9bdb9271 | 739 | |
cc26de46 | 740 | /* Run the client script with the new parameters. */ |
02d9e453 | 741 | script_init (client, (client -> state == S_REQUESTING |
cc26de46 | 742 | ? "BOUND" |
02d9e453 | 743 | : (client -> state == S_RENEWING |
cc26de46 | 744 | ? "RENEW" |
02d9e453 | 745 | : (client -> state == S_REBOOTING |
77967680 | 746 | ? "REBOOT" : "REBIND"))), |
02d9e453 TL |
747 | client -> new -> medium); |
748 | if (client -> active && client -> state != S_REBOOTING) | |
749 | script_write_params (client, "old_", client -> active); | |
750 | script_write_params (client, "new_", client -> new); | |
751 | if (client -> alias) | |
752 | script_write_params (client, "alias_", client -> alias); | |
e16f4c3e TL |
753 | |
754 | /* If the BOUND/RENEW code detects another machine using the | |
755 | offered address, it exits nonzero. We need to send a | |
756 | DHCPDECLINE and toss the lease. */ | |
757 | if (script_go (client)) { | |
758 | make_decline (client, client -> new); | |
759 | send_decline (client); | |
760 | destroy_client_lease (client -> new); | |
7d3bc735 | 761 | client -> new = (struct client_lease *)0; |
e16f4c3e TL |
762 | state_init (client); |
763 | return; | |
764 | } | |
765 | ||
766 | /* Write out the new lease. */ | |
767 | write_client_lease (client, client -> new, 0); | |
cc26de46 TL |
768 | |
769 | /* Replace the old active lease with the new one. */ | |
02d9e453 TL |
770 | if (client -> active) |
771 | destroy_client_lease (client -> active); | |
772 | client -> active = client -> new; | |
773 | client -> new = (struct client_lease *)0; | |
cc26de46 TL |
774 | |
775 | /* Set up a timeout to start the renewal process. */ | |
02d9e453 TL |
776 | add_timeout (client -> active -> renewal, |
777 | state_bound, client); | |
cc26de46 | 778 | |
ab58ff49 | 779 | log_info ("bound to %s -- renewal in %ld seconds.", |
02d9e453 | 780 | piaddr (client -> active -> address), |
ab58ff49 | 781 | (long)(client -> active -> renewal - cur_time)); |
02d9e453 | 782 | client -> state = S_BOUND; |
b00d3884 TL |
783 | reinitialize_interfaces (); |
784 | go_daemon (); | |
cc26de46 | 785 | } |
469cf3a4 | 786 | |
cc26de46 TL |
787 | /* state_bound is called when we've successfully bound to a particular |
788 | lease, but the renewal time on that lease has expired. We are | |
789 | expected to unicast a DHCPREQUEST to the server that gave us our | |
790 | original lease. */ | |
469cf3a4 | 791 | |
02d9e453 TL |
792 | void state_bound (cpp) |
793 | void *cpp; | |
469cf3a4 | 794 | { |
02d9e453 | 795 | struct client_state *client = cpp; |
02a015fb TL |
796 | int i; |
797 | struct option_cache *oc; | |
798 | struct data_string ds; | |
84c4adde | 799 | |
cc26de46 | 800 | ASSERT_STATE(state, S_BOUND); |
469cf3a4 | 801 | |
cc26de46 | 802 | /* T1 has expired. */ |
02d9e453 TL |
803 | make_request (client, client -> active); |
804 | client -> xid = client -> packet.xid; | |
469cf3a4 | 805 | |
02a015fb | 806 | memset (&ds, 0, sizeof ds); |
230e73e4 | 807 | oc = lookup_option (&dhcp_universe, client -> active -> options, |
02a015fb TL |
808 | DHO_DHCP_SERVER_IDENTIFIER); |
809 | if (oc && | |
0852a27f TL |
810 | evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0, |
811 | (struct option_state *)0, | |
da38df14 | 812 | client -> active -> options, |
cf78bf20 | 813 | &global_scope, oc, MDL)) { |
02a015fb | 814 | if (ds.len > 3) { |
02d9e453 TL |
815 | memcpy (client -> destination.iabuf, ds.data, 4); |
816 | client -> destination.len = 4; | |
02a015fb | 817 | } else |
02d9e453 | 818 | client -> destination = iaddr_broadcast; |
cc26de46 | 819 | } else |
02d9e453 | 820 | client -> destination = iaddr_broadcast; |
469cf3a4 | 821 | |
02d9e453 TL |
822 | client -> first_sending = cur_time; |
823 | client -> interval = client -> config -> initial_interval; | |
824 | client -> state = S_RENEWING; | |
cc26de46 TL |
825 | |
826 | /* Send the first packet immediately. */ | |
02d9e453 | 827 | send_request (client); |
cc26de46 | 828 | } |
469cf3a4 | 829 | |
c1503c57 TL |
830 | int commit_leases () |
831 | { | |
832 | return 0; | |
833 | } | |
834 | ||
835 | int write_lease (lease) | |
836 | struct lease *lease; | |
837 | { | |
838 | return 0; | |
95821729 TL |
839 | } |
840 | ||
628beb0e TL |
841 | int write_host (host) |
842 | struct host_decl *host; | |
843 | { | |
844 | return 0; | |
845 | } | |
846 | ||
7d7a35fa TL |
847 | void db_startup (testp) |
848 | int testp; | |
c1503c57 TL |
849 | { |
850 | } | |
851 | ||
852 | void bootp (packet) | |
853 | struct packet *packet; | |
854 | { | |
fa2b3e59 TL |
855 | struct iaddrlist *ap; |
856 | ||
857 | if (packet -> raw -> op != BOOTREPLY) | |
858 | return; | |
859 | ||
860 | /* If there's a reject list, make sure this packet's sender isn't | |
861 | on it. */ | |
862 | for (ap = packet -> interface -> client -> config -> reject_list; | |
863 | ap; ap = ap -> next) { | |
864 | if (addr_eq (packet -> client_addr, ap -> addr)) { | |
8ae2d595 | 865 | log_info ("BOOTREPLY from %s rejected.", |
fa2b3e59 TL |
866 | piaddr (ap -> addr)); |
867 | return; | |
868 | } | |
869 | } | |
870 | ||
871 | dhcpoffer (packet); | |
66f973e4 | 872 | |
c1503c57 | 873 | } |
95821729 | 874 | |
c1503c57 TL |
875 | void dhcp (packet) |
876 | struct packet *packet; | |
95821729 | 877 | { |
fa2b3e59 | 878 | struct iaddrlist *ap; |
ea65d407 | 879 | void (*handler) PROTO ((struct packet *)); |
b1b7b521 | 880 | const char *type; |
fa2b3e59 | 881 | |
c1503c57 TL |
882 | switch (packet -> packet_type) { |
883 | case DHCPOFFER: | |
fa2b3e59 TL |
884 | handler = dhcpoffer; |
885 | type = "DHCPOFFER"; | |
95821729 | 886 | break; |
c1503c57 TL |
887 | |
888 | case DHCPNAK: | |
fa2b3e59 TL |
889 | handler = dhcpnak; |
890 | type = "DHCPNACK"; | |
c1503c57 TL |
891 | break; |
892 | ||
893 | case DHCPACK: | |
fa2b3e59 TL |
894 | handler = dhcpack; |
895 | type = "DHCPACK"; | |
c1503c57 TL |
896 | break; |
897 | ||
95821729 | 898 | default: |
fa2b3e59 TL |
899 | return; |
900 | } | |
901 | ||
902 | /* If there's a reject list, make sure this packet's sender isn't | |
903 | on it. */ | |
904 | for (ap = packet -> interface -> client -> config -> reject_list; | |
905 | ap; ap = ap -> next) { | |
906 | if (addr_eq (packet -> client_addr, ap -> addr)) { | |
8ae2d595 | 907 | log_info ("%s from %s rejected.", |
fa2b3e59 TL |
908 | type, piaddr (ap -> addr)); |
909 | return; | |
910 | } | |
95821729 | 911 | } |
fa2b3e59 | 912 | (*handler) (packet); |
95821729 TL |
913 | } |
914 | ||
c1503c57 TL |
915 | void dhcpoffer (packet) |
916 | struct packet *packet; | |
95821729 | 917 | { |
cc26de46 | 918 | struct interface_info *ip = packet -> interface; |
02d9e453 | 919 | struct client_state *client; |
b00d3884 | 920 | struct client_lease *lease, *lp; |
cc26de46 | 921 | int i; |
e16f4c3e | 922 | int stop_selecting; |
b1b7b521 | 923 | const char *name = packet -> packet_type ? "DHCPOFFER" : "BOOTREPLY"; |
fa2b3e59 | 924 | struct iaddrlist *ap; |
02a015fb | 925 | struct option_cache *oc; |
cc26de46 | 926 | |
cc26de46 | 927 | #ifdef DEBUG_PACKET |
c1503c57 | 928 | dump_packet (packet); |
cc26de46 TL |
929 | #endif |
930 | ||
02d9e453 TL |
931 | /* Find a client state that matches the xid... */ |
932 | for (client = ip -> client; client; client = client -> next) | |
933 | if (client -> xid == packet -> raw -> xid) | |
934 | break; | |
935 | ||
cc26de46 TL |
936 | /* If we're not receptive to an offer right now, or if the offer |
937 | has an unrecognizable transaction id, then just drop it. */ | |
02d9e453 | 938 | if (!client || |
73530742 | 939 | client -> state != S_SELECTING || |
31730f17 | 940 | (packet -> interface -> hw_address.hlen - 1 != |
fcaec4ef | 941 | packet -> raw -> hlen) || |
31730f17 | 942 | (memcmp (&packet -> interface -> hw_address.hbuf [1], |
fcaec4ef | 943 | packet -> raw -> chaddr, packet -> raw -> hlen))) { |
2d1b06e0 | 944 | #if defined (DEBUG) |
8ae2d595 | 945 | log_debug ("%s in wrong transaction.", name); |
2d1b06e0 | 946 | #endif |
cc26de46 TL |
947 | return; |
948 | } | |
949 | ||
8ae2d595 | 950 | log_info ("%s from %s", name, piaddr (packet -> client_addr)); |
34fdad6c | 951 | |
b8cf055d | 952 | |
cc26de46 TL |
953 | /* If this lease doesn't supply the minimum required parameters, |
954 | blow it off. */ | |
02d9e453 TL |
955 | if (client -> config -> required_options) { |
956 | for (i = 0; client -> config -> required_options [i]; i++) { | |
02a015fb | 957 | if (!lookup_option |
230e73e4 | 958 | (&dhcp_universe, packet -> options, |
02d9e453 | 959 | client -> config -> required_options [i])) { |
8ae2d595 | 960 | log_info ("%s isn't satisfactory.", name); |
02a015fb TL |
961 | return; |
962 | } | |
cc26de46 TL |
963 | } |
964 | } | |
965 | ||
966 | /* If we've already seen this lease, don't record it again. */ | |
02d9e453 | 967 | for (lease = client -> offered_leases; lease; lease = lease -> next) { |
cc26de46 TL |
968 | if (lease -> address.len == sizeof packet -> raw -> yiaddr && |
969 | !memcmp (lease -> address.iabuf, | |
970 | &packet -> raw -> yiaddr, lease -> address.len)) { | |
8ae2d595 | 971 | log_debug ("%s already seen.", name); |
cc26de46 TL |
972 | return; |
973 | } | |
974 | } | |
975 | ||
976 | lease = packet_to_lease (packet); | |
977 | if (!lease) { | |
8ae2d595 | 978 | log_info ("packet_to_lease failed."); |
cc26de46 TL |
979 | return; |
980 | } | |
981 | ||
66f973e4 TL |
982 | /* If this lease was acquired through a BOOTREPLY, record that |
983 | fact. */ | |
02a015fb | 984 | if (!packet -> options_valid || !packet -> packet_type) |
66f973e4 TL |
985 | lease -> is_bootp = 1; |
986 | ||
9bdb9271 | 987 | /* Record the medium under which this lease was offered. */ |
02d9e453 | 988 | lease -> medium = client -> medium; |
9bdb9271 | 989 | |
b00d3884 | 990 | /* Figure out when we're supposed to stop selecting. */ |
02d9e453 TL |
991 | stop_selecting = (client -> first_sending + |
992 | client -> config -> select_interval); | |
b00d3884 TL |
993 | |
994 | /* If this is the lease we asked for, put it at the head of the | |
995 | list, and don't mess with the arp request timeout. */ | |
02d9e453 | 996 | if (lease -> address.len == client -> requested_address.len && |
b00d3884 | 997 | !memcmp (lease -> address.iabuf, |
02d9e453 TL |
998 | client -> requested_address.iabuf, |
999 | client -> requested_address.len)) { | |
1000 | lease -> next = client -> offered_leases; | |
1001 | client -> offered_leases = lease; | |
b00d3884 | 1002 | } else { |
b00d3884 TL |
1003 | /* Put the lease at the end of the list. */ |
1004 | lease -> next = (struct client_lease *)0; | |
02d9e453 TL |
1005 | if (!client -> offered_leases) |
1006 | client -> offered_leases = lease; | |
b00d3884 | 1007 | else { |
02d9e453 | 1008 | for (lp = client -> offered_leases; lp -> next; |
b00d3884 TL |
1009 | lp = lp -> next) |
1010 | ; | |
1011 | lp -> next = lease; | |
1012 | } | |
1013 | } | |
1014 | ||
cc26de46 TL |
1015 | /* If the selecting interval has expired, go immediately to |
1016 | state_selecting(). Otherwise, time out into | |
1017 | state_selecting at the select interval. */ | |
b00d3884 | 1018 | if (stop_selecting <= 0) |
cc26de46 | 1019 | state_selecting (ip); |
84c4adde | 1020 | else { |
02d9e453 TL |
1021 | add_timeout (stop_selecting, state_selecting, client); |
1022 | cancel_timeout (send_discover, client); | |
84c4adde | 1023 | } |
95821729 | 1024 | } |
e581d615 | 1025 | |
cc26de46 TL |
1026 | /* Allocate a client_lease structure and initialize it from the parameters |
1027 | in the specified packet. */ | |
1028 | ||
1029 | struct client_lease *packet_to_lease (packet) | |
c1503c57 | 1030 | struct packet *packet; |
e581d615 | 1031 | { |
cc26de46 TL |
1032 | struct client_lease *lease; |
1033 | int i; | |
02a015fb TL |
1034 | struct option_cache *oc; |
1035 | struct data_string data; | |
cc26de46 | 1036 | |
cf78bf20 | 1037 | lease = (struct client_lease *)new_client_lease (MDL); |
cc26de46 TL |
1038 | |
1039 | if (!lease) { | |
8ae2d595 | 1040 | log_error ("dhcpoffer: no memory to record lease.\n"); |
cc26de46 TL |
1041 | return (struct client_lease *)0; |
1042 | } | |
1043 | ||
1044 | memset (lease, 0, sizeof *lease); | |
1045 | ||
1046 | /* Copy the lease options. */ | |
cf78bf20 | 1047 | option_state_reference (&lease -> options, packet -> options, MDL); |
cc26de46 TL |
1048 | |
1049 | lease -> address.len = sizeof (packet -> raw -> yiaddr); | |
1050 | memcpy (lease -> address.iabuf, &packet -> raw -> yiaddr, | |
1051 | lease -> address.len); | |
1052 | ||
02a015fb | 1053 | /* Figure out the overload flag. */ |
230e73e4 | 1054 | oc = lookup_option (&dhcp_universe, lease -> options, |
02a015fb TL |
1055 | DHO_DHCP_OPTION_OVERLOAD); |
1056 | memset (&data, 0, sizeof data); | |
1057 | if (oc && | |
0852a27f TL |
1058 | evaluate_option_cache (&data, packet, (struct lease *)0, |
1059 | packet -> options, lease -> options, | |
cf78bf20 | 1060 | &global_scope, oc, MDL)) { |
02a015fb TL |
1061 | if (data.len > 0) |
1062 | i = data.data [0]; | |
1063 | else | |
1064 | i = 0; | |
cf78bf20 | 1065 | data_string_forget (&data, MDL); |
02a015fb TL |
1066 | } else |
1067 | i = 0; | |
1068 | ||
cc26de46 | 1069 | /* If the server name was filled out, copy it. */ |
02a015fb | 1070 | if (!(i & 2) && packet -> raw -> sname [0]) { |
b1b7b521 | 1071 | unsigned len; |
cc26de46 TL |
1072 | /* Don't count on the NUL terminator. */ |
1073 | for (len = 0; len < 64; len++) | |
1074 | if (!packet -> raw -> sname [len]) | |
1075 | break; | |
cf78bf20 | 1076 | lease -> server_name = dmalloc (len + 1, MDL); |
cc26de46 | 1077 | if (!lease -> server_name) { |
8ae2d595 | 1078 | log_error ("dhcpoffer: no memory for filename.\n"); |
02a015fb | 1079 | destroy_client_lease (lease); |
cc26de46 TL |
1080 | return (struct client_lease *)0; |
1081 | } else { | |
1082 | memcpy (lease -> server_name, | |
1083 | packet -> raw -> sname, len); | |
1084 | lease -> server_name [len] = 0; | |
1085 | } | |
1086 | } | |
1087 | ||
1088 | /* Ditto for the filename. */ | |
0852a27f | 1089 | if (!(i & 1) && packet -> raw -> file [0]) { |
b1b7b521 | 1090 | unsigned len; |
cc26de46 TL |
1091 | /* Don't count on the NUL terminator. */ |
1092 | for (len = 0; len < 64; len++) | |
1093 | if (!packet -> raw -> file [len]) | |
1094 | break; | |
cf78bf20 | 1095 | lease -> filename = dmalloc (len + 1, MDL); |
cc26de46 | 1096 | if (!lease -> filename) { |
8ae2d595 | 1097 | log_error ("dhcpoffer: no memory for filename.\n"); |
02a015fb | 1098 | destroy_client_lease (lease); |
cc26de46 TL |
1099 | return (struct client_lease *)0; |
1100 | } else { | |
1101 | memcpy (lease -> filename, | |
1102 | packet -> raw -> file, len); | |
1103 | lease -> filename [len] = 0; | |
1104 | } | |
1105 | } | |
1106 | return lease; | |
1107 | } | |
e581d615 | 1108 | |
c1503c57 TL |
1109 | void dhcpnak (packet) |
1110 | struct packet *packet; | |
1111 | { | |
deff2d59 | 1112 | struct interface_info *ip = packet -> interface; |
02d9e453 TL |
1113 | struct client_state *client; |
1114 | ||
1115 | /* Find a client state that matches the xid... */ | |
1116 | for (client = ip -> client; client; client = client -> next) | |
1117 | if (client -> xid == packet -> raw -> xid) | |
1118 | break; | |
deff2d59 | 1119 | |
deff2d59 TL |
1120 | /* If we're not receptive to an offer right now, or if the offer |
1121 | has an unrecognizable transaction id, then just drop it. */ | |
02d9e453 | 1122 | if (!client || |
31730f17 | 1123 | (packet -> interface -> hw_address.hlen - 1 != |
fcaec4ef | 1124 | packet -> raw -> hlen) || |
31730f17 | 1125 | (memcmp (&packet -> interface -> hw_address.hbuf [1], |
fcaec4ef | 1126 | packet -> raw -> chaddr, packet -> raw -> hlen))) { |
2d1b06e0 | 1127 | #if defined (DEBUG) |
8ae2d595 | 1128 | log_debug ("DHCPNAK in wrong transaction."); |
2d1b06e0 | 1129 | #endif |
deff2d59 TL |
1130 | return; |
1131 | } | |
1132 | ||
02d9e453 TL |
1133 | if (client -> state != S_REBOOTING && |
1134 | client -> state != S_REQUESTING && | |
1135 | client -> state != S_RENEWING && | |
1136 | client -> state != S_REBINDING) { | |
2d1b06e0 | 1137 | #if defined (DEBUG) |
8ae2d595 | 1138 | log_debug ("DHCPNAK in wrong state."); |
2d1b06e0 | 1139 | #endif |
deff2d59 TL |
1140 | return; |
1141 | } | |
1142 | ||
8ae2d595 | 1143 | log_info ("DHCPNAK from %s", piaddr (packet -> client_addr)); |
34fdad6c | 1144 | |
02d9e453 | 1145 | if (!client -> active) { |
2d1b06e0 | 1146 | #if defined (DEBUG) |
8ae2d595 | 1147 | log_info ("DHCPNAK with no active lease.\n"); |
2d1b06e0 | 1148 | #endif |
deff2d59 TL |
1149 | return; |
1150 | } | |
1151 | ||
02d9e453 TL |
1152 | destroy_client_lease (client -> active); |
1153 | client -> active = (struct client_lease *)0; | |
deff2d59 TL |
1154 | |
1155 | /* Stop sending DHCPREQUEST packets... */ | |
02d9e453 | 1156 | cancel_timeout (send_request, client); |
deff2d59 | 1157 | |
02d9e453 TL |
1158 | client -> state = S_INIT; |
1159 | state_init (client); | |
e581d615 TL |
1160 | } |
1161 | ||
cc26de46 | 1162 | /* Send out a DHCPDISCOVER packet, and set a timeout to send out another |
66f973e4 TL |
1163 | one after the right interval has expired. If we don't get an offer by |
1164 | the time we reach the panic interval, call the panic function. */ | |
469cf3a4 | 1165 | |
02d9e453 TL |
1166 | void send_discover (cpp) |
1167 | void *cpp; | |
469cf3a4 | 1168 | { |
02d9e453 | 1169 | struct client_state *client = cpp; |
84c4adde | 1170 | |
cc26de46 TL |
1171 | int result; |
1172 | int interval; | |
9bdb9271 | 1173 | int increase = 1; |
cc26de46 TL |
1174 | |
1175 | /* Figure out how long it's been since we started transmitting. */ | |
02d9e453 | 1176 | interval = cur_time - client -> first_sending; |
cc26de46 TL |
1177 | |
1178 | /* If we're past the panic timeout, call the script and tell it | |
1179 | we haven't found anything for this interface yet. */ | |
02d9e453 TL |
1180 | if (interval > client -> config -> timeout) { |
1181 | state_panic (client); | |
b00d3884 | 1182 | return; |
cc26de46 | 1183 | } |
469cf3a4 | 1184 | |
9bdb9271 TL |
1185 | /* If we're selecting media, try the whole list before doing |
1186 | the exponential backoff, but if we've already received an | |
1187 | offer, stop looping, because we obviously have it right. */ | |
02d9e453 TL |
1188 | if (!client -> offered_leases && |
1189 | client -> config -> media) { | |
9bdb9271 TL |
1190 | int fail = 0; |
1191 | again: | |
02d9e453 TL |
1192 | if (client -> medium) { |
1193 | client -> medium = client -> medium -> next; | |
9bdb9271 TL |
1194 | increase = 0; |
1195 | } | |
02d9e453 | 1196 | if (!client -> medium) { |
9bdb9271 | 1197 | if (fail) |
8ae2d595 | 1198 | log_fatal ("No valid media types for %s!", |
02d9e453 TL |
1199 | client -> interface -> name); |
1200 | client -> medium = | |
1201 | client -> config -> media; | |
9bdb9271 TL |
1202 | increase = 1; |
1203 | } | |
1204 | ||
8ae2d595 | 1205 | log_info ("Trying medium \"%s\" %d", |
02d9e453 TL |
1206 | client -> medium -> string, increase); |
1207 | script_init (client, "MEDIUM", client -> medium); | |
1208 | if (script_go (client)) { | |
9bdb9271 TL |
1209 | goto again; |
1210 | } | |
1211 | } | |
cc26de46 | 1212 | |
9bdb9271 TL |
1213 | /* If we're supposed to increase the interval, do so. If it's |
1214 | currently zero (i.e., we haven't sent any packets yet), set | |
1215 | it to one; otherwise, add to it a random number between | |
1216 | zero and two times itself. On average, this means that it | |
1217 | will double with every transmission. */ | |
1218 | if (increase) { | |
02d9e453 TL |
1219 | if (!client -> interval) |
1220 | client -> interval = | |
1221 | client -> config -> initial_interval; | |
9bdb9271 | 1222 | else { |
02d9e453 | 1223 | client -> interval += |
34fdad6c | 1224 | ((random () >> 2) % |
02d9e453 | 1225 | (2 * client -> interval)); |
9bdb9271 | 1226 | } |
cc26de46 | 1227 | |
34fdad6c | 1228 | /* Don't backoff past cutoff. */ |
02d9e453 TL |
1229 | if (client -> interval > |
1230 | client -> config -> backoff_cutoff) | |
1231 | client -> interval = | |
1232 | ((client -> config -> backoff_cutoff / 2) | |
ae6ea807 | 1233 | + ((random () >> 2) % |
02d9e453 TL |
1234 | client -> config -> backoff_cutoff)); |
1235 | } else if (!client -> interval) | |
1236 | client -> interval = client -> config -> initial_interval; | |
9bdb9271 | 1237 | |
cc26de46 TL |
1238 | /* If the backoff would take us to the panic timeout, just use that |
1239 | as the interval. */ | |
02d9e453 TL |
1240 | if (cur_time + client -> interval > |
1241 | client -> first_sending + client -> config -> timeout) | |
1242 | client -> interval = | |
1243 | (client -> first_sending + | |
1244 | client -> config -> timeout) - cur_time + 1; | |
cc26de46 | 1245 | |
34fdad6c | 1246 | /* Record the number of seconds since we started sending. */ |
af6df788 TL |
1247 | if (interval < 65536) |
1248 | client -> packet.secs = htons (interval); | |
34fdad6c | 1249 | else |
af6df788 TL |
1250 | client -> packet.secs = htons (65535); |
1251 | client -> secs = client -> packet.secs; | |
34fdad6c | 1252 | |
8ae2d595 | 1253 | log_info ("DHCPDISCOVER on %s to %s port %d interval %ld", |
02d9e453 | 1254 | client -> name ? client -> name : client -> interface -> name, |
66f973e4 | 1255 | inet_ntoa (sockaddr_broadcast.sin_addr), |
02d9e453 | 1256 | ntohs (sockaddr_broadcast.sin_port), client -> interval); |
66f973e4 | 1257 | |
cc26de46 | 1258 | /* Send out a packet. */ |
02d9e453 TL |
1259 | result = send_packet (client -> interface, (struct packet *)0, |
1260 | &client -> packet, | |
1261 | client -> packet_length, | |
cc26de46 TL |
1262 | inaddr_any, &sockaddr_broadcast, |
1263 | (struct hardware *)0); | |
469cf3a4 | 1264 | |
02d9e453 | 1265 | add_timeout (cur_time + client -> interval, send_discover, client); |
469cf3a4 TL |
1266 | } |
1267 | ||
b00d3884 TL |
1268 | /* state_panic gets called if we haven't received any offers in a preset |
1269 | amount of time. When this happens, we try to use existing leases that | |
1270 | haven't yet expired, and failing that, we call the client script and | |
1271 | hope it can do something. */ | |
1272 | ||
02d9e453 TL |
1273 | void state_panic (cpp) |
1274 | void *cpp; | |
b00d3884 | 1275 | { |
02d9e453 TL |
1276 | struct client_state *client = cpp; |
1277 | struct client_lease *loop; | |
b00d3884 TL |
1278 | struct client_lease *lp; |
1279 | ||
02d9e453 TL |
1280 | loop = lp = client -> active; |
1281 | ||
8ae2d595 | 1282 | log_info ("No DHCPOFFERS received."); |
b00d3884 | 1283 | |
deff2d59 TL |
1284 | /* We may not have an active lease, but we may have some |
1285 | predefined leases that we can try. */ | |
02d9e453 | 1286 | if (!client -> active && client -> leases) |
deff2d59 | 1287 | goto activate_next; |
deff2d59 | 1288 | |
b00d3884 | 1289 | /* Run through the list of leases and see if one can be used. */ |
02d9e453 TL |
1290 | while (client -> active) { |
1291 | if (client -> active -> expiry > cur_time) { | |
8ae2d595 | 1292 | log_info ("Trying recorded lease %s", |
02d9e453 | 1293 | piaddr (client -> active -> address)); |
b00d3884 TL |
1294 | /* Run the client script with the existing |
1295 | parameters. */ | |
02d9e453 TL |
1296 | script_init (client, "TIMEOUT", |
1297 | client -> active -> medium); | |
1298 | script_write_params (client, "new_", client -> active); | |
1299 | if (client -> alias) | |
1300 | script_write_params (client, "alias_", | |
1301 | client -> alias); | |
b00d3884 TL |
1302 | |
1303 | /* If the old lease is still good and doesn't | |
1304 | yet need renewal, go into BOUND state and | |
1305 | timeout at the renewal time. */ | |
02d9e453 | 1306 | if (!script_go (client)) { |
1c016679 TL |
1307 | if (cur_time < client -> active -> renewal) { |
1308 | client -> state = S_BOUND; | |
1309 | log_info ("bound: renewal in %ld %s.", | |
1310 | (long)(client -> active -> renewal - | |
1311 | cur_time), "seconds"); | |
1312 | add_timeout (client -> active -> renewal, | |
1313 | state_bound, client); | |
1314 | } else { | |
1315 | client -> state = S_BOUND; | |
1316 | log_info ("bound: immediate renewal."); | |
1317 | state_bound (client); | |
1318 | } | |
1319 | reinitialize_interfaces (); | |
1320 | go_daemon (); | |
1321 | return; | |
b00d3884 TL |
1322 | } |
1323 | } | |
1324 | ||
1325 | /* If there are no other leases, give up. */ | |
02d9e453 TL |
1326 | if (!client -> leases) { |
1327 | client -> leases = client -> active; | |
1328 | client -> active = (struct client_lease *)0; | |
b00d3884 TL |
1329 | break; |
1330 | } | |
1331 | ||
deff2d59 | 1332 | activate_next: |
b00d3884 TL |
1333 | /* Otherwise, put the active lease at the end of the |
1334 | lease list, and try another lease.. */ | |
02d9e453 | 1335 | for (lp = client -> leases; lp -> next; lp = lp -> next) |
b00d3884 | 1336 | ; |
02d9e453 | 1337 | lp -> next = client -> active; |
34fdad6c TL |
1338 | if (lp -> next) { |
1339 | lp -> next -> next = (struct client_lease *)0; | |
1340 | } | |
02d9e453 TL |
1341 | client -> active = client -> leases; |
1342 | client -> leases = client -> leases -> next; | |
b00d3884 TL |
1343 | |
1344 | /* If we already tried this lease, we've exhausted the | |
1345 | set of leases, so we might as well give up for | |
1346 | now. */ | |
02d9e453 | 1347 | if (client -> active == loop) |
b00d3884 | 1348 | break; |
66f973e4 | 1349 | else if (!loop) |
02d9e453 | 1350 | loop = client -> active; |
b00d3884 TL |
1351 | } |
1352 | ||
1353 | /* No leases were available, or what was available didn't work, so | |
1354 | tell the shell script that we failed to allocate an address, | |
1355 | and try again later. */ | |
11373fb6 | 1356 | log_info ("No working leases in persistent database - sleeping."); |
02d9e453 TL |
1357 | script_init (client, "FAIL", (struct string_list *)0); |
1358 | if (client -> alias) | |
1359 | script_write_params (client, "alias_", client -> alias); | |
1360 | script_go (client); | |
1361 | client -> state = S_INIT; | |
11373fb6 TL |
1362 | add_timeout (cur_time + |
1363 | ((client -> config -> retry_interval + 1) / 2 + | |
1364 | (random () % client -> config -> retry_interval)), | |
02d9e453 | 1365 | state_init, client); |
ae04fa03 | 1366 | go_daemon (); |
b00d3884 TL |
1367 | } |
1368 | ||
02d9e453 TL |
1369 | void send_request (cpp) |
1370 | void *cpp; | |
e581d615 | 1371 | { |
02d9e453 | 1372 | struct client_state *client = cpp; |
84c4adde | 1373 | |
c1503c57 | 1374 | int result; |
cc26de46 TL |
1375 | int interval; |
1376 | struct sockaddr_in destination; | |
1377 | struct in_addr from; | |
1378 | ||
1379 | /* Figure out how long it's been since we started transmitting. */ | |
02d9e453 | 1380 | interval = cur_time - client -> first_sending; |
cc26de46 | 1381 | |
34fdad6c TL |
1382 | /* If we're in the INIT-REBOOT or REQUESTING state and we're |
1383 | past the reboot timeout, go to INIT and see if we can | |
1384 | DISCOVER an address... */ | |
1385 | /* XXX In the INIT-REBOOT state, if we don't get an ACK, it | |
1386 | means either that we're on a network with no DHCP server, | |
1387 | or that our server is down. In the latter case, assuming | |
1388 | that there is a backup DHCP server, DHCPDISCOVER will get | |
1389 | us a new address, but we could also have successfully | |
1390 | reused our old address. In the former case, we're hosed | |
1391 | anyway. This is not a win-prone situation. */ | |
02d9e453 TL |
1392 | if ((client -> state == S_REBOOTING || |
1393 | client -> state == S_REQUESTING) && | |
1394 | interval > client -> config -> reboot_timeout) { | |
b8cf055d | 1395 | cancel: |
02d9e453 TL |
1396 | client -> state = S_INIT; |
1397 | cancel_timeout (send_request, client); | |
1398 | state_init (client); | |
deff2d59 TL |
1399 | return; |
1400 | } | |
1401 | ||
b8cf055d TL |
1402 | /* If we're in the reboot state, make sure the media is set up |
1403 | correctly. */ | |
02d9e453 TL |
1404 | if (client -> state == S_REBOOTING && |
1405 | !client -> medium && | |
1406 | client -> active -> medium ) { | |
1407 | script_init (client, "MEDIUM", client -> active -> medium); | |
b8cf055d TL |
1408 | |
1409 | /* If the medium we chose won't fly, go to INIT state. */ | |
02d9e453 | 1410 | if (script_go (client)) |
b8cf055d TL |
1411 | goto cancel; |
1412 | ||
1413 | /* Record the medium. */ | |
02d9e453 | 1414 | client -> medium = client -> active -> medium; |
b8cf055d TL |
1415 | } |
1416 | ||
cc26de46 TL |
1417 | /* If the lease has expired, relinquish the address and go back |
1418 | to the INIT state. */ | |
02d9e453 TL |
1419 | if (client -> state != S_REQUESTING && |
1420 | cur_time > client -> active -> expiry) { | |
cc26de46 | 1421 | /* Run the client script with the new parameters. */ |
02d9e453 TL |
1422 | script_init (client, "EXPIRE", (struct string_list *)0); |
1423 | script_write_params (client, "old_", client -> active); | |
1424 | if (client -> alias) | |
1425 | script_write_params (client, "alias_", | |
1426 | client -> alias); | |
1427 | script_go (client); | |
1428 | ||
62d0cb47 TL |
1429 | /* Now do a preinit on the interface so that we can |
1430 | discover a new address. */ | |
1431 | script_init (client, "PREINIT", (struct string_list *)0); | |
1432 | if (client -> alias) | |
1433 | script_write_params (client, "alias_", | |
1434 | client -> alias); | |
1435 | script_go (client); | |
1436 | ||
02d9e453 TL |
1437 | client -> state = S_INIT; |
1438 | state_init (client); | |
cc26de46 TL |
1439 | return; |
1440 | } | |
1441 | ||
1442 | /* Do the exponential backoff... */ | |
02d9e453 TL |
1443 | if (!client -> interval) |
1444 | client -> interval = client -> config -> initial_interval; | |
34fdad6c | 1445 | else { |
02d9e453 TL |
1446 | client -> interval += ((random () >> 2) % |
1447 | (2 * client -> interval)); | |
34fdad6c TL |
1448 | } |
1449 | ||
1450 | /* Don't backoff past cutoff. */ | |
02d9e453 TL |
1451 | if (client -> interval > |
1452 | client -> config -> backoff_cutoff) | |
1453 | client -> interval = | |
1454 | ((client -> config -> backoff_cutoff / 2) | |
1455 | + ((random () >> 2) % client -> interval)); | |
cc26de46 TL |
1456 | |
1457 | /* If the backoff would take us to the expiry time, just set the | |
1458 | timeout to the expiry time. */ | |
02d9e453 TL |
1459 | if (client -> state != S_REQUESTING && |
1460 | cur_time + client -> interval > client -> active -> expiry) | |
1461 | client -> interval = | |
1462 | client -> active -> expiry - cur_time + 1; | |
cc26de46 TL |
1463 | |
1464 | /* If the lease T2 time has elapsed, or if we're not yet bound, | |
1465 | broadcast the DHCPREQUEST rather than unicasting. */ | |
02d9e453 | 1466 | if (client -> state == S_REQUESTING || |
62d0cb47 | 1467 | client -> state == S_REBOOTING || |
02d9e453 | 1468 | cur_time > client -> active -> rebind) |
3020a2c1 | 1469 | destination.sin_addr = sockaddr_broadcast.sin_addr; |
cc26de46 TL |
1470 | else |
1471 | memcpy (&destination.sin_addr.s_addr, | |
02d9e453 | 1472 | client -> destination.iabuf, |
cc26de46 TL |
1473 | sizeof destination.sin_addr.s_addr); |
1474 | destination.sin_port = remote_port; | |
b00d3884 TL |
1475 | destination.sin_family = AF_INET; |
1476 | #ifdef HAVE_SA_LEN | |
1477 | destination.sin_len = sizeof destination; | |
1478 | #endif | |
cc26de46 | 1479 | |
7d3bc735 TL |
1480 | if (client -> state == S_RENEWING || |
1481 | client -> state == S_REBINDING) | |
02d9e453 | 1482 | memcpy (&from, client -> active -> address.iabuf, |
cc26de46 TL |
1483 | sizeof from); |
1484 | else | |
1485 | from.s_addr = INADDR_ANY; | |
1486 | ||
34fdad6c | 1487 | /* Record the number of seconds since we started sending. */ |
af6df788 TL |
1488 | if (client -> state == S_REQUESTING) |
1489 | client -> packet.secs = client -> secs; | |
1490 | else { | |
1491 | if (interval < 65536) | |
1492 | client -> packet.secs = htons (interval); | |
1493 | else | |
1494 | client -> packet.secs = htons (65535); | |
1495 | } | |
34fdad6c | 1496 | |
8ae2d595 | 1497 | log_info ("DHCPREQUEST on %s to %s port %d", |
02d9e453 | 1498 | client -> name ? client -> name : client -> interface -> name, |
cc26de46 TL |
1499 | inet_ntoa (destination.sin_addr), |
1500 | ntohs (destination.sin_port)); | |
1501 | ||
62d0cb47 TL |
1502 | if (destination.sin_addr.s_addr != INADDR_BROADCAST && |
1503 | fallback_interface) | |
1504 | result = send_packet (fallback_interface, | |
1505 | (struct packet *)0, | |
1506 | &client -> packet, | |
1507 | client -> packet_length, | |
1508 | from, &destination, | |
1509 | (struct hardware *)0); | |
cc26de46 | 1510 | else |
cc26de46 | 1511 | /* Send out a packet. */ |
02d9e453 TL |
1512 | result = send_packet (client -> interface, (struct packet *)0, |
1513 | &client -> packet, | |
1514 | client -> packet_length, | |
cc26de46 TL |
1515 | from, &destination, |
1516 | (struct hardware *)0); | |
469cf3a4 | 1517 | |
02d9e453 TL |
1518 | add_timeout (cur_time + client -> interval, |
1519 | send_request, client); | |
469cf3a4 TL |
1520 | } |
1521 | ||
02d9e453 TL |
1522 | void send_decline (cpp) |
1523 | void *cpp; | |
b00d3884 | 1524 | { |
02d9e453 | 1525 | struct client_state *client = cpp; |
84c4adde | 1526 | |
b00d3884 TL |
1527 | int result; |
1528 | ||
8ae2d595 | 1529 | log_info ("DHCPDECLINE on %s to %s port %d", |
02d9e453 | 1530 | client -> name ? client -> name : client -> interface -> name, |
b00d3884 TL |
1531 | inet_ntoa (sockaddr_broadcast.sin_addr), |
1532 | ntohs (sockaddr_broadcast.sin_port)); | |
1533 | ||
1534 | /* Send out a packet. */ | |
02d9e453 TL |
1535 | result = send_packet (client -> interface, (struct packet *)0, |
1536 | &client -> packet, | |
1537 | client -> packet_length, | |
b00d3884 TL |
1538 | inaddr_any, &sockaddr_broadcast, |
1539 | (struct hardware *)0); | |
b00d3884 TL |
1540 | } |
1541 | ||
02d9e453 TL |
1542 | void send_release (cpp) |
1543 | void *cpp; | |
b00d3884 | 1544 | { |
02d9e453 | 1545 | struct client_state *client = cpp; |
84c4adde | 1546 | |
b00d3884 TL |
1547 | int result; |
1548 | ||
8ae2d595 | 1549 | log_info ("DHCPRELEASE on %s to %s port %d", |
02d9e453 | 1550 | client -> name ? client -> name : client -> interface -> name, |
b00d3884 TL |
1551 | inet_ntoa (sockaddr_broadcast.sin_addr), |
1552 | ntohs (sockaddr_broadcast.sin_port)); | |
1553 | ||
1554 | /* Send out a packet. */ | |
02d9e453 TL |
1555 | result = send_packet (client -> interface, (struct packet *)0, |
1556 | &client -> packet, | |
1557 | client -> packet_length, | |
b00d3884 TL |
1558 | inaddr_any, &sockaddr_broadcast, |
1559 | (struct hardware *)0); | |
b00d3884 TL |
1560 | } |
1561 | ||
230e73e4 | 1562 | void make_client_options (client, lease, type, sid, rip, prl, op) |
02d9e453 | 1563 | struct client_state *client; |
cc26de46 | 1564 | struct client_lease *lease; |
02a015fb TL |
1565 | u_int8_t *type; |
1566 | struct option_cache *sid; | |
1567 | struct iaddr *rip; | |
1568 | u_int32_t *prl; | |
230e73e4 | 1569 | struct option_state **op; |
469cf3a4 | 1570 | { |
b1b7b521 | 1571 | unsigned i; |
02a015fb TL |
1572 | struct option_cache *oc; |
1573 | struct buffer *bp = (struct buffer *)0; | |
1574 | ||
230e73e4 | 1575 | /* Allocate space for options. */ |
cf78bf20 | 1576 | option_state_allocate (op, MDL); |
02a015fb TL |
1577 | |
1578 | /* Send the server identifier if provided. */ | |
1579 | if (sid) | |
230e73e4 | 1580 | save_option (&dhcp_universe, *op, sid); |
02a015fb | 1581 | |
2455808f TL |
1582 | oc = (struct option_cache *)0; |
1583 | ||
02a015fb TL |
1584 | /* Send the requested address if provided. */ |
1585 | if (rip) { | |
02d9e453 | 1586 | client -> requested_address = *rip; |
02a015fb | 1587 | if (!(make_const_option_cache |
cf78bf20 TL |
1588 | (&oc, (struct buffer **)0, rip -> iabuf, rip -> len, |
1589 | &dhcp_options [DHO_DHCP_REQUESTED_ADDRESS], MDL))) | |
230e73e4 | 1590 | log_error ("can't make requested address cache."); |
02a015fb | 1591 | else { |
230e73e4 | 1592 | save_option (&dhcp_universe, *op, oc); |
cf78bf20 | 1593 | option_cache_dereference (&oc, MDL); |
02a015fb | 1594 | } |
b00d3884 | 1595 | } else { |
02d9e453 | 1596 | client -> requested_address.len = 0; |
cc26de46 | 1597 | } |
c1503c57 | 1598 | |
02a015fb TL |
1599 | if (!(make_const_option_cache |
1600 | (&oc, (struct buffer **)0, | |
cf78bf20 | 1601 | type, 1, &dhcp_options [DHO_DHCP_MESSAGE_TYPE], MDL))) |
8ae2d595 | 1602 | log_error ("can't make message type."); |
02a015fb | 1603 | else { |
230e73e4 | 1604 | save_option (&dhcp_universe, *op, oc); |
cf78bf20 | 1605 | option_cache_dereference (&oc, MDL); |
02a015fb TL |
1606 | } |
1607 | ||
1608 | if (prl) { | |
1609 | /* Figure out how many parameters were requested. */ | |
1610 | for (i = 0; prl [i]; i++) | |
1611 | ; | |
cf78bf20 | 1612 | if (!buffer_allocate (&bp, i, MDL)) |
230e73e4 | 1613 | log_error ("can't make parameter list buffer."); |
02a015fb TL |
1614 | else { |
1615 | for (i = 0; prl [i]; i++) | |
1616 | bp -> data [i] = prl [i]; | |
1617 | if (!(make_const_option_cache | |
1618 | (&oc, &bp, (u_int8_t *)0, i, | |
1619 | &dhcp_options [DHO_DHCP_PARAMETER_REQUEST_LIST], | |
cf78bf20 | 1620 | MDL))) |
8ae2d595 | 1621 | log_error ("can't make option cache"); |
02a015fb | 1622 | else { |
230e73e4 | 1623 | save_option (&dhcp_universe, *op, oc); |
cf78bf20 | 1624 | option_cache_dereference (&oc, MDL); |
02a015fb | 1625 | } |
deff2d59 TL |
1626 | } |
1627 | } | |
1628 | ||
02a015fb | 1629 | /* Run statements that need to be run on transmission. */ |
73530742 TL |
1630 | if (client -> config -> on_transmission) |
1631 | execute_statements_in_scope | |
da38df14 TL |
1632 | ((struct packet *)0, (struct lease *)0, |
1633 | (lease ? lease -> options : (struct option_state *)0), | |
cf78bf20 TL |
1634 | *op, &global_scope, |
1635 | client -> config -> on_transmission, | |
73530742 | 1636 | (struct group *)0); |
02a015fb TL |
1637 | } |
1638 | ||
02d9e453 TL |
1639 | void make_discover (client, lease) |
1640 | struct client_state *client; | |
02a015fb TL |
1641 | struct client_lease *lease; |
1642 | { | |
1643 | struct dhcp_packet *raw; | |
1644 | unsigned char discover = DHCPDISCOVER; | |
1645 | int i; | |
230e73e4 | 1646 | struct option_state *options = (struct option_state *)0; |
02a015fb | 1647 | |
02d9e453 | 1648 | memset (&client -> packet, 0, sizeof (client -> packet)); |
02a015fb | 1649 | |
02d9e453 TL |
1650 | make_client_options (client, |
1651 | lease, &discover, (struct option_cache *)0, | |
02a015fb | 1652 | lease ? &lease -> address : (struct iaddr *)0, |
02d9e453 | 1653 | client -> config -> requested_options, |
02a015fb TL |
1654 | &options); |
1655 | ||
c1503c57 | 1656 | /* Set up the option buffer... */ |
02d9e453 | 1657 | client -> packet_length = |
da38df14 TL |
1658 | cons_options ((struct packet *)0, &client -> packet, |
1659 | (struct lease *)0, 0, | |
0852a27f | 1660 | (struct option_state *)0, options, |
cf78bf20 | 1661 | &global_scope, 0, 0, 0, (struct data_string *)0); |
02d9e453 TL |
1662 | if (client -> packet_length < BOOTP_MIN_LEN) |
1663 | client -> packet_length = BOOTP_MIN_LEN; | |
1664 | ||
1665 | client -> packet.op = BOOTREQUEST; | |
31730f17 TL |
1666 | client -> packet.htype = client -> interface -> hw_address.hbuf [0]; |
1667 | client -> packet.hlen = client -> interface -> hw_address.hlen - 1; | |
02d9e453 TL |
1668 | client -> packet.hops = 0; |
1669 | client -> packet.xid = random (); | |
1670 | client -> packet.secs = 0; /* filled in by send_discover. */ | |
f345b36d TL |
1671 | |
1672 | if (can_receive_unicast_unconfigured (client -> interface)) | |
1673 | client -> packet.flags = 0; | |
1674 | else | |
1675 | client -> packet.flags = htons (BOOTP_BROADCAST); | |
1676 | ||
02d9e453 TL |
1677 | memset (&(client -> packet.ciaddr), |
1678 | 0, sizeof client -> packet.ciaddr); | |
1679 | memset (&(client -> packet.yiaddr), | |
1680 | 0, sizeof client -> packet.yiaddr); | |
1681 | memset (&(client -> packet.siaddr), | |
1682 | 0, sizeof client -> packet.siaddr); | |
11373fb6 | 1683 | client -> packet.giaddr = giaddr; |
31730f17 TL |
1684 | if (client -> interface -> hw_address.hlen > 0) |
1685 | memcpy (client -> packet.chaddr, | |
1686 | &client -> interface -> hw_address.hbuf [1], | |
1687 | (unsigned)(client -> interface -> hw_address.hlen - 1)); | |
c1503c57 TL |
1688 | |
1689 | #ifdef DEBUG_PACKET | |
469cf3a4 | 1690 | dump_packet (sendpkt); |
02d9e453 | 1691 | dump_raw ((unsigned char *)client -> packet, |
cc26de46 | 1692 | sendpkt->packet_length); |
c1503c57 | 1693 | #endif |
c1503c57 TL |
1694 | } |
1695 | ||
469cf3a4 | 1696 | |
02d9e453 TL |
1697 | void make_request (client, lease) |
1698 | struct client_state *client; | |
cc26de46 | 1699 | struct client_lease *lease; |
c1503c57 | 1700 | { |
c1503c57 | 1701 | unsigned char request = DHCPREQUEST; |
ce0ec46d TL |
1702 | int i, j; |
1703 | unsigned char *tmp, *digest; | |
1704 | unsigned char *old_digest_loc; | |
230e73e4 | 1705 | struct option_state *options = (struct option_state *)0; |
02a015fb | 1706 | struct option_cache *oc; |
c1503c57 | 1707 | |
02d9e453 | 1708 | memset (&client -> packet, 0, sizeof (client -> packet)); |
469cf3a4 | 1709 | |
02d9e453 | 1710 | if (client -> state == S_REQUESTING) |
230e73e4 | 1711 | oc = lookup_option (&dhcp_universe, lease -> options, |
02a015fb TL |
1712 | DHO_DHCP_SERVER_IDENTIFIER); |
1713 | else | |
1714 | oc = (struct option_cache *)0; | |
8dba80a6 | 1715 | |
02d9e453 TL |
1716 | make_client_options (client, lease, &request, oc, |
1717 | ((client -> state == S_REQUESTING || | |
1718 | client -> state == S_REBOOTING) | |
02a015fb TL |
1719 | ? &lease -> address |
1720 | : (struct iaddr *)0), | |
02d9e453 | 1721 | client -> config -> requested_options, |
02a015fb | 1722 | &options); |
deff2d59 | 1723 | |
c1503c57 | 1724 | /* Set up the option buffer... */ |
02d9e453 | 1725 | client -> packet_length = |
da38df14 TL |
1726 | cons_options ((struct packet *)0, &client -> packet, |
1727 | (struct lease *)0, 0, | |
0852a27f | 1728 | (struct option_state *)0, options, |
cf78bf20 | 1729 | &global_scope, 0, 0, 0, (struct data_string *)0); |
02d9e453 TL |
1730 | if (client -> packet_length < BOOTP_MIN_LEN) |
1731 | client -> packet_length = BOOTP_MIN_LEN; | |
cc26de46 | 1732 | |
02d9e453 | 1733 | client -> packet.op = BOOTREQUEST; |
31730f17 TL |
1734 | client -> packet.htype = client -> interface -> hw_address.hbuf [0]; |
1735 | client -> packet.hlen = client -> interface -> hw_address.hlen - 1; | |
02d9e453 TL |
1736 | client -> packet.hops = 0; |
1737 | client -> packet.xid = client -> xid; | |
1738 | client -> packet.secs = 0; /* Filled in by send_request. */ | |
cc26de46 TL |
1739 | |
1740 | /* If we own the address we're requesting, put it in ciaddr; | |
1741 | otherwise set ciaddr to zero. */ | |
02d9e453 TL |
1742 | if (client -> state == S_BOUND || |
1743 | client -> state == S_RENEWING || | |
62d0cb47 | 1744 | client -> state == S_REBINDING) { |
02d9e453 | 1745 | memcpy (&client -> packet.ciaddr, |
cc26de46 | 1746 | lease -> address.iabuf, lease -> address.len); |
339b0231 | 1747 | client -> packet.flags = 0; |
62d0cb47 | 1748 | } else { |
02d9e453 TL |
1749 | memset (&client -> packet.ciaddr, 0, |
1750 | sizeof client -> packet.ciaddr); | |
f345b36d TL |
1751 | if (can_receive_unicast_unconfigured (client -> interface)) |
1752 | client -> packet.flags = 0; | |
1753 | else | |
1754 | client -> packet.flags = htons (BOOTP_BROADCAST); | |
62d0cb47 | 1755 | } |
02d9e453 TL |
1756 | |
1757 | memset (&client -> packet.yiaddr, 0, | |
1758 | sizeof client -> packet.yiaddr); | |
1759 | memset (&client -> packet.siaddr, 0, | |
1760 | sizeof client -> packet.siaddr); | |
11373fb6 | 1761 | client -> packet.giaddr = giaddr; |
31730f17 TL |
1762 | if (client -> interface -> hw_address.hlen > 0) |
1763 | memcpy (client -> packet.chaddr, | |
1764 | &client -> interface -> hw_address.hbuf [1], | |
1765 | (unsigned)(client -> interface -> hw_address.hlen - 1)); | |
c1503c57 TL |
1766 | |
1767 | #ifdef DEBUG_PACKET | |
469cf3a4 | 1768 | dump_packet (sendpkt); |
02d9e453 | 1769 | dump_raw ((unsigned char *)client -> packet, sendpkt->packet_length); |
c1503c57 | 1770 | #endif |
469cf3a4 | 1771 | } |
c1503c57 | 1772 | |
02d9e453 TL |
1773 | void make_decline (client, lease) |
1774 | struct client_state *client; | |
cc26de46 | 1775 | struct client_lease *lease; |
469cf3a4 | 1776 | { |
cc26de46 | 1777 | unsigned char decline = DHCPDECLINE; |
deff2d59 | 1778 | int i; |
02a015fb | 1779 | struct option_cache *oc; |
c1503c57 | 1780 | |
230e73e4 | 1781 | struct option_state *options = (struct option_state *)0; |
c1503c57 | 1782 | |
230e73e4 | 1783 | oc = lookup_option (&dhcp_universe, lease -> options, |
02a015fb | 1784 | DHO_DHCP_SERVER_IDENTIFIER); |
02d9e453 | 1785 | make_client_options (client, lease, &decline, oc, |
230e73e4 | 1786 | &lease -> address, (u_int32_t *)0, &options); |
ca25f4ab | 1787 | |
cc26de46 | 1788 | /* Set up the option buffer... */ |
230e73e4 | 1789 | memset (&client -> packet, 0, sizeof (client -> packet)); |
02d9e453 | 1790 | client -> packet_length = |
da38df14 TL |
1791 | cons_options ((struct packet *)0, &client -> packet, |
1792 | (struct lease *)0, 0, | |
0852a27f | 1793 | (struct option_state *)0, options, |
cf78bf20 | 1794 | &global_scope, 0, 0, 0, (struct data_string *)0); |
02d9e453 TL |
1795 | if (client -> packet_length < BOOTP_MIN_LEN) |
1796 | client -> packet_length = BOOTP_MIN_LEN; | |
cf78bf20 | 1797 | option_state_dereference (&options, MDL); |
cc26de46 | 1798 | |
02d9e453 | 1799 | client -> packet.op = BOOTREQUEST; |
31730f17 TL |
1800 | client -> packet.htype = client -> interface -> hw_address.hbuf [0]; |
1801 | client -> packet.hlen = client -> interface -> hw_address.hlen - 1; | |
02d9e453 TL |
1802 | client -> packet.hops = 0; |
1803 | client -> packet.xid = client -> xid; | |
1804 | client -> packet.secs = 0; /* Filled in by send_request. */ | |
7d3bc735 TL |
1805 | if (can_receive_unicast_unconfigured (client -> interface)) |
1806 | client -> packet.flags = 0; | |
1807 | else | |
1808 | client -> packet.flags = htons (BOOTP_BROADCAST); | |
cc26de46 TL |
1809 | |
1810 | /* ciaddr must always be zero. */ | |
02d9e453 TL |
1811 | memset (&client -> packet.ciaddr, 0, |
1812 | sizeof client -> packet.ciaddr); | |
1813 | memset (&client -> packet.yiaddr, 0, | |
1814 | sizeof client -> packet.yiaddr); | |
1815 | memset (&client -> packet.siaddr, 0, | |
1816 | sizeof client -> packet.siaddr); | |
11373fb6 | 1817 | client -> packet.giaddr = giaddr; |
02d9e453 | 1818 | memcpy (client -> packet.chaddr, |
31730f17 | 1819 | &client -> interface -> hw_address.hbuf [1], |
02d9e453 | 1820 | client -> interface -> hw_address.hlen); |
cc26de46 TL |
1821 | |
1822 | #ifdef DEBUG_PACKET | |
1823 | dump_packet (sendpkt); | |
02d9e453 | 1824 | dump_raw ((unsigned char *)client -> packet, sendpkt->packet_length); |
cc26de46 TL |
1825 | #endif |
1826 | } | |
1827 | ||
02d9e453 TL |
1828 | void make_release (client, lease) |
1829 | struct client_state *client; | |
cc26de46 TL |
1830 | struct client_lease *lease; |
1831 | { | |
1832 | unsigned char request = DHCPRELEASE; | |
deff2d59 | 1833 | int i; |
02a015fb | 1834 | struct option_cache *oc; |
cc26de46 | 1835 | |
230e73e4 | 1836 | struct option_state *options = (struct option_state *)0; |
cc26de46 | 1837 | |
02d9e453 | 1838 | memset (&client -> packet, 0, sizeof (client -> packet)); |
469cf3a4 | 1839 | |
230e73e4 | 1840 | oc = lookup_option (&dhcp_universe, lease -> options, |
02a015fb | 1841 | DHO_DHCP_SERVER_IDENTIFIER); |
02d9e453 | 1842 | make_client_options (client, lease, &request, oc, |
347de8bd | 1843 | (struct iaddr *)0, (u_int32_t *)0, |
73530742 | 1844 | &options); |
469cf3a4 TL |
1845 | |
1846 | /* Set up the option buffer... */ | |
02d9e453 | 1847 | client -> packet_length = |
da38df14 TL |
1848 | cons_options ((struct packet *)0, &client -> packet, |
1849 | (struct lease *)0, 0, | |
0852a27f | 1850 | (struct option_state *)0, options, |
cf78bf20 | 1851 | &global_scope, 0, 0, 0, (struct data_string *)0); |
02d9e453 TL |
1852 | if (client -> packet_length < BOOTP_MIN_LEN) |
1853 | client -> packet_length = BOOTP_MIN_LEN; | |
cf78bf20 | 1854 | option_state_dereference (&options, MDL); |
02d9e453 TL |
1855 | |
1856 | client -> packet.op = BOOTREQUEST; | |
31730f17 TL |
1857 | client -> packet.htype = client -> interface -> hw_address.hbuf [0]; |
1858 | client -> packet.hlen = client -> interface -> hw_address.hlen - 1; | |
02d9e453 | 1859 | client -> packet.hops = 0; |
74f45f96 | 1860 | client -> packet.xid = random (); |
02d9e453 TL |
1861 | client -> packet.secs = 0; |
1862 | client -> packet.flags = 0; | |
1863 | memcpy (&client -> packet.ciaddr, | |
cc26de46 | 1864 | lease -> address.iabuf, lease -> address.len); |
02d9e453 TL |
1865 | memset (&client -> packet.yiaddr, 0, |
1866 | sizeof client -> packet.yiaddr); | |
1867 | memset (&client -> packet.siaddr, 0, | |
1868 | sizeof client -> packet.siaddr); | |
11373fb6 | 1869 | client -> packet.giaddr = giaddr; |
02d9e453 | 1870 | memcpy (client -> packet.chaddr, |
bdcaf7b9 | 1871 | &client -> interface -> hw_address.hbuf [1], |
02d9e453 | 1872 | client -> interface -> hw_address.hlen); |
469cf3a4 TL |
1873 | |
1874 | #ifdef DEBUG_PACKET | |
1875 | dump_packet (sendpkt); | |
02d9e453 TL |
1876 | dump_raw ((unsigned char *)client -> packet, |
1877 | client -> packet_length); | |
469cf3a4 | 1878 | #endif |
e581d615 | 1879 | } |
cc26de46 | 1880 | |
02a015fb | 1881 | void destroy_client_lease (lease) |
cc26de46 TL |
1882 | struct client_lease *lease; |
1883 | { | |
1884 | int i; | |
1885 | ||
1886 | if (lease -> server_name) | |
cf78bf20 | 1887 | dfree (lease -> server_name, MDL); |
cc26de46 | 1888 | if (lease -> filename) |
cf78bf20 TL |
1889 | dfree (lease -> filename, MDL); |
1890 | option_state_dereference (&lease -> options, MDL); | |
1891 | free_client_lease (lease, MDL); | |
cc26de46 TL |
1892 | } |
1893 | ||
1894 | FILE *leaseFile; | |
1895 | ||
1896 | void rewrite_client_leases () | |
1897 | { | |
1898 | struct interface_info *ip; | |
02d9e453 | 1899 | struct client_state *client; |
cc26de46 TL |
1900 | struct client_lease *lp; |
1901 | ||
1902 | if (leaseFile) | |
1903 | fclose (leaseFile); | |
1904 | leaseFile = fopen (path_dhclient_db, "w"); | |
1905 | if (!leaseFile) | |
8ae2d595 | 1906 | log_fatal ("can't create %s: %m", path_dhclient_db); |
b00d3884 TL |
1907 | |
1908 | /* Write out all the leases attached to configured interfaces that | |
1909 | we know about. */ | |
cc26de46 | 1910 | for (ip = interfaces; ip; ip = ip -> next) { |
02d9e453 TL |
1911 | for (client = ip -> client; client; client = client -> next) { |
1912 | for (lp = client -> leases; lp; lp = lp -> next) { | |
1913 | write_client_lease (client, lp, 1); | |
1914 | } | |
1915 | if (client -> active) | |
1916 | write_client_lease (client, | |
1917 | client -> active, 1); | |
cc26de46 | 1918 | } |
cc26de46 | 1919 | } |
b00d3884 TL |
1920 | |
1921 | /* Write out any leases that are attached to interfaces that aren't | |
1922 | currently configured. */ | |
1923 | for (ip = dummy_interfaces; ip; ip = ip -> next) { | |
02d9e453 TL |
1924 | for (client = ip -> client; client; client = client -> next) { |
1925 | for (lp = client -> leases; lp; lp = lp -> next) { | |
1926 | write_client_lease (client, lp, 1); | |
1927 | } | |
1928 | if (client -> active) | |
1929 | write_client_lease (client, | |
1930 | client -> active, 1); | |
b00d3884 | 1931 | } |
b00d3884 | 1932 | } |
cc26de46 TL |
1933 | fflush (leaseFile); |
1934 | } | |
1935 | ||
02d9e453 TL |
1936 | void write_client_lease (client, lease, rewrite) |
1937 | struct client_state *client; | |
cc26de46 | 1938 | struct client_lease *lease; |
5c2f78b4 | 1939 | int rewrite; |
cc26de46 TL |
1940 | { |
1941 | int i; | |
1942 | struct tm *t; | |
5c2f78b4 | 1943 | static int leases_written; |
02a015fb TL |
1944 | struct option_cache *oc; |
1945 | struct data_string ds; | |
230e73e4 | 1946 | pair *hash; |
5c2f78b4 TL |
1947 | |
1948 | if (!rewrite) { | |
1949 | if (leases_written++ > 20) { | |
1950 | rewrite_client_leases (); | |
1951 | leases_written = 0; | |
1952 | } | |
1953 | } | |
cc26de46 | 1954 | |
b00d3884 TL |
1955 | /* If the lease came from the config file, we don't need to stash |
1956 | a copy in the lease database. */ | |
1957 | if (lease -> is_static) | |
1958 | return; | |
1959 | ||
cc26de46 TL |
1960 | if (!leaseFile) { /* XXX */ |
1961 | leaseFile = fopen (path_dhclient_db, "w"); | |
1962 | if (!leaseFile) | |
8ae2d595 | 1963 | log_fatal ("can't create %s: %m", path_dhclient_db); |
cc26de46 TL |
1964 | } |
1965 | ||
1966 | fprintf (leaseFile, "lease {\n"); | |
66f973e4 TL |
1967 | if (lease -> is_bootp) |
1968 | fprintf (leaseFile, " bootp;\n"); | |
02d9e453 TL |
1969 | fprintf (leaseFile, " interface \"%s\";\n", |
1970 | client -> interface -> name); | |
1971 | if (client -> name) | |
1972 | fprintf (leaseFile, " name \"%s\";\n", client -> name); | |
cc26de46 TL |
1973 | fprintf (leaseFile, " fixed-address %s;\n", |
1974 | piaddr (lease -> address)); | |
1975 | if (lease -> filename) | |
1976 | fprintf (leaseFile, " filename \"%s\";\n", | |
1977 | lease -> filename); | |
1978 | if (lease -> server_name) | |
1979 | fprintf (leaseFile, " server-name \"%s\";\n", | |
0852a27f | 1980 | lease -> server_name); |
9bdb9271 TL |
1981 | if (lease -> medium) |
1982 | fprintf (leaseFile, " medium \"%s\";\n", | |
1983 | lease -> medium -> string); | |
2455808f TL |
1984 | |
1985 | memset (&ds, 0, sizeof ds); | |
230e73e4 TL |
1986 | |
1987 | hash = lease -> options -> universes [dhcp_universe.index]; | |
02a015fb TL |
1988 | for (i = 0; i < OPTION_HASH_SIZE; i++) { |
1989 | pair p; | |
230e73e4 TL |
1990 | /* XXX save _all_ options! XXX */ |
1991 | for (p = hash [i]; p; p = p -> cdr) { | |
2455808f | 1992 | oc = (struct option_cache *)p -> car; |
02a015fb | 1993 | if (evaluate_option_cache (&ds, (struct packet *)0, |
0852a27f TL |
1994 | (struct lease *)0, |
1995 | (struct option_state *)0, | |
da38df14 | 1996 | lease -> options, |
cf78bf20 | 1997 | &global_scope, oc, MDL)) { |
02a015fb TL |
1998 | fprintf (leaseFile, |
1999 | " option %s %s;\n", | |
2455808f | 2000 | oc -> option -> name, |
02a015fb | 2001 | pretty_print_option |
2455808f TL |
2002 | (oc -> option -> code, |
2003 | ds.data, ds.len, 1, 1)); | |
cf78bf20 | 2004 | data_string_forget (&ds, MDL); |
02a015fb | 2005 | } |
cc26de46 TL |
2006 | } |
2007 | } | |
5c2f78b4 TL |
2008 | |
2009 | /* Note: the following is not a Y2K bug - it's a Y1.9K bug. Until | |
2010 | somebody invents a time machine, I think we can safely disregard | |
2011 | it. */ | |
cc26de46 TL |
2012 | t = gmtime (&lease -> renewal); |
2013 | fprintf (leaseFile, | |
2014 | " renew %d %d/%d/%d %02d:%02d:%02d;\n", | |
2015 | t -> tm_wday, t -> tm_year + 1900, | |
2016 | t -> tm_mon + 1, t -> tm_mday, | |
2017 | t -> tm_hour, t -> tm_min, t -> tm_sec); | |
2018 | t = gmtime (&lease -> rebind); | |
2019 | fprintf (leaseFile, | |
2020 | " rebind %d %d/%d/%d %02d:%02d:%02d;\n", | |
2021 | t -> tm_wday, t -> tm_year + 1900, | |
2022 | t -> tm_mon + 1, t -> tm_mday, | |
2023 | t -> tm_hour, t -> tm_min, t -> tm_sec); | |
2024 | t = gmtime (&lease -> expiry); | |
2025 | fprintf (leaseFile, | |
2026 | " expire %d %d/%d/%d %02d:%02d:%02d;\n", | |
2027 | t -> tm_wday, t -> tm_year + 1900, | |
2028 | t -> tm_mon + 1, t -> tm_mday, | |
2029 | t -> tm_hour, t -> tm_min, t -> tm_sec); | |
2030 | fprintf (leaseFile, "}\n"); | |
2031 | fflush (leaseFile); | |
2032 | } | |
2033 | ||
2034 | /* Variables holding name of script and file pointer for writing to | |
2035 | script. Needless to say, this is not reentrant - only one script | |
2036 | can be invoked at a time. */ | |
2037 | char scriptName [256]; | |
2038 | FILE *scriptFile; | |
2039 | ||
02d9e453 TL |
2040 | void script_init (client, reason, medium) |
2041 | struct client_state *client; | |
b1b7b521 | 2042 | const char *reason; |
9bdb9271 | 2043 | struct string_list *medium; |
cc26de46 | 2044 | { |
b8cf055d TL |
2045 | int fd; |
2046 | #ifndef HAVE_MKSTEMP | |
cc26de46 | 2047 | |
b8cf055d TL |
2048 | do { |
2049 | #endif | |
2050 | strcpy (scriptName, "/tmp/dcsXXXXXX"); | |
2051 | #ifdef HAVE_MKSTEMP | |
2052 | fd = mkstemp (scriptName); | |
2053 | #else | |
8c135c14 | 2054 | if (!mktemp (scriptName)) |
6767b592 TL |
2055 | log_fatal ("can't create temporary script %s: %m", |
2056 | scriptName); | |
d9eefc5d TL |
2057 | fd = open (scriptName, O_EXCL | O_CREAT | O_WRONLY, 0600); |
2058 | } while (fd < 0 && errno == EEXIST); | |
b8cf055d | 2059 | #endif |
6767b592 TL |
2060 | if (fd < 0) |
2061 | log_fatal ("can't create temporary script %s: %m", scriptName); | |
2062 | ||
b8cf055d TL |
2063 | |
2064 | scriptFile = fdopen (fd, "w"); | |
cc26de46 | 2065 | if (!scriptFile) |
8ae2d595 | 2066 | log_fatal ("can't write script file: %m"); |
cc26de46 | 2067 | fprintf (scriptFile, "#!/bin/sh\n\n"); |
02d9e453 TL |
2068 | if (client) { |
2069 | if (client -> interface) { | |
2070 | fprintf (scriptFile, "interface=\"%s\"\n", | |
2071 | client -> interface -> name); | |
2072 | fprintf (scriptFile, "export interface\n"); | |
2073 | } | |
2074 | if (client -> name) | |
2075 | fprintf (scriptFile, "client=\"%s\"\n", | |
2076 | client -> name); | |
2077 | fprintf (scriptFile, "export client\n"); | |
cc26de46 | 2078 | } |
9bdb9271 | 2079 | if (medium) { |
d13e8f28 | 2080 | fprintf (scriptFile, "medium=\"%s\"\n", medium -> string); |
9bdb9271 TL |
2081 | fprintf (scriptFile, "export medium\n"); |
2082 | } | |
cc26de46 TL |
2083 | fprintf (scriptFile, "reason=\"%s\"\n", reason); |
2084 | fprintf (scriptFile, "export reason\n"); | |
2085 | } | |
2086 | ||
02d9e453 TL |
2087 | void script_write_params (client, prefix, lease) |
2088 | struct client_state *client; | |
b1b7b521 | 2089 | const char *prefix; |
cc26de46 TL |
2090 | struct client_lease *lease; |
2091 | { | |
2092 | int i; | |
02a015fb TL |
2093 | struct data_string data; |
2094 | struct option_cache *oc; | |
230e73e4 | 2095 | pair *hash; |
cc26de46 TL |
2096 | |
2097 | fprintf (scriptFile, "%sip_address=\"%s\"\n", | |
2098 | prefix, piaddr (lease -> address)); | |
2099 | fprintf (scriptFile, "export %sip_address\n", prefix); | |
c6abd205 TL |
2100 | |
2101 | /* For the benefit of Linux (and operating systems which may | |
2102 | have similar needs), compute the network address based on | |
2103 | the supplied ip address and netmask, if provided. Also | |
2104 | compute the broadcast address (the host address all ones | |
2105 | broadcast address, not the host address all zeroes | |
2106 | broadcast address). */ | |
2107 | ||
02a015fb | 2108 | memset (&data, 0, sizeof data); |
230e73e4 | 2109 | oc = lookup_option (&dhcp_universe, lease -> options, DHO_SUBNET_MASK); |
0852a27f TL |
2110 | if (oc && evaluate_option_cache (&data, |
2111 | (struct packet *)0, | |
2112 | (struct lease *)0, | |
2113 | (struct option_state *)0, | |
da38df14 | 2114 | lease -> options, |
cf78bf20 | 2115 | &global_scope, oc, MDL)) { |
02a015fb TL |
2116 | if (data.len > 3) { |
2117 | struct iaddr netmask, subnet, broadcast; | |
2118 | ||
2119 | memcpy (netmask.iabuf, data.data, data.len); | |
2120 | netmask.len = data.len; | |
cf78bf20 | 2121 | data_string_forget (&data, MDL); |
02a015fb TL |
2122 | |
2123 | subnet = subnet_number (lease -> address, netmask); | |
2124 | if (subnet.len) { | |
2125 | fprintf (scriptFile, | |
2126 | "%snetwork_number=\"%s\";\n", | |
2127 | prefix, piaddr (subnet)); | |
2128 | fprintf (scriptFile, | |
2129 | "export %snetwork_number\n", prefix); | |
2130 | ||
230e73e4 TL |
2131 | oc = lookup_option (&dhcp_universe, |
2132 | lease -> options, | |
02a015fb TL |
2133 | DHO_BROADCAST_ADDRESS); |
2134 | if (!oc || | |
0852a27f TL |
2135 | !(evaluate_option_cache |
2136 | (&data, (struct packet *)0, | |
2137 | (struct lease *)0, | |
2138 | (struct option_state *)0, | |
cf78bf20 TL |
2139 | lease -> options, |
2140 | &global_scope, oc, MDL))) { | |
02a015fb TL |
2141 | broadcast = broadcast_addr (subnet, |
2142 | netmask); | |
2143 | if (broadcast.len) { | |
2144 | fprintf (scriptFile, | |
2145 | "%s%s=\"%s\";\n", | |
2146 | prefix, | |
2147 | "broadcast_address", | |
2148 | piaddr (broadcast)); | |
2149 | fprintf (scriptFile, | |
2150 | "export %s%s\n", | |
2151 | prefix, | |
2152 | "broadcast_address"); | |
2153 | } | |
c6abd205 TL |
2154 | } |
2155 | } | |
2156 | } | |
cf78bf20 | 2157 | data_string_forget (&data, MDL); |
c6abd205 TL |
2158 | } |
2159 | ||
cc26de46 TL |
2160 | if (lease -> filename) { |
2161 | fprintf (scriptFile, "%sfilename=\"%s\";\n", | |
2162 | prefix, lease -> filename); | |
2163 | fprintf (scriptFile, "export %sfilename\n", prefix); | |
2164 | } | |
2165 | if (lease -> server_name) { | |
2166 | fprintf (scriptFile, "%sserver_name=\"%s\";\n", | |
2167 | prefix, lease -> server_name); | |
2168 | fprintf (scriptFile, "export %sserver_name\n", prefix); | |
2169 | } | |
02a015fb | 2170 | |
da38df14 TL |
2171 | execute_statements_in_scope ((struct packet *)0, |
2172 | (struct lease *)0, lease -> options, | |
cf78bf20 | 2173 | lease -> options, &global_scope, |
73530742 TL |
2174 | client -> config -> on_receipt, |
2175 | (struct group *)0); | |
02a015fb | 2176 | |
230e73e4 | 2177 | hash = lease -> options -> universes [dhcp_universe.index]; |
02a015fb TL |
2178 | for (i = 0; i < OPTION_HASH_SIZE; i++) { |
2179 | pair hp; | |
2180 | ||
230e73e4 | 2181 | for (hp = hash [i]; hp; hp = hp -> cdr) { |
02a015fb TL |
2182 | oc = (struct option_cache *)hp -> car; |
2183 | ||
0852a27f TL |
2184 | if (evaluate_option_cache (&data, |
2185 | (struct packet *)0, | |
2186 | (struct lease *)0, | |
2187 | (struct option_state *)0, | |
da38df14 | 2188 | lease -> options, |
cf78bf20 | 2189 | &global_scope, oc, MDL)) { |
02a015fb TL |
2190 | |
2191 | if (data.len) { | |
2192 | char *s = (dhcp_option_ev_name | |
2193 | (oc -> option)); | |
2194 | ||
2195 | fprintf (scriptFile, | |
2196 | "%s%s=\"%s\"\n", prefix, s, | |
2197 | (pretty_print_option | |
2455808f TL |
2198 | (oc -> option -> code, |
2199 | data.data, data.len, | |
02a015fb TL |
2200 | 0, 0))); |
2201 | fprintf (scriptFile, | |
2202 | "export %s%s\n", prefix, s); | |
66f973e4 | 2203 | } |
cf78bf20 | 2204 | data_string_forget (&data, MDL); |
66f973e4 | 2205 | } |
cc26de46 TL |
2206 | } |
2207 | } | |
2208 | fprintf (scriptFile, "%sexpiry=\"%d\"\n", | |
2209 | prefix, (int)lease -> expiry); /* XXX */ | |
2210 | fprintf (scriptFile, "export %sexpiry\n", prefix); | |
2211 | } | |
2212 | ||
02d9e453 TL |
2213 | int script_go (client) |
2214 | struct client_state *client; | |
cc26de46 TL |
2215 | { |
2216 | int rval; | |
2217 | ||
02d9e453 | 2218 | if (client) |
cc26de46 | 2219 | fprintf (scriptFile, "%s\n", |
02d9e453 | 2220 | client -> config -> script_name); |
cc26de46 TL |
2221 | else |
2222 | fprintf (scriptFile, "%s\n", | |
2223 | top_level_config.script_name); | |
2224 | fprintf (scriptFile, "exit $?\n"); | |
2225 | fclose (scriptFile); | |
2226 | chmod (scriptName, 0700); | |
2227 | rval = system (scriptName); | |
126965a9 TL |
2228 | if (!save_scripts) |
2229 | unlink (scriptName); | |
cc26de46 TL |
2230 | return rval; |
2231 | } | |
2232 | ||
2233 | char *dhcp_option_ev_name (option) | |
2234 | struct option *option; | |
2235 | { | |
2236 | static char evbuf [256]; | |
2237 | int i; | |
2238 | ||
2239 | if (strlen (option -> name) + 1 > sizeof evbuf) | |
ab58ff49 TL |
2240 | log_fatal ("option %s name is larger than static buffer.", |
2241 | option -> name); | |
cc26de46 TL |
2242 | for (i = 0; option -> name [i]; i++) { |
2243 | if (option -> name [i] == '-') | |
2244 | evbuf [i] = '_'; | |
2245 | else | |
2246 | evbuf [i] = option -> name [i]; | |
2247 | } | |
2248 | ||
2249 | evbuf [i] = 0; | |
2250 | return evbuf; | |
2251 | } | |
b00d3884 TL |
2252 | |
2253 | void go_daemon () | |
2254 | { | |
2255 | static int state = 0; | |
2256 | int pid; | |
2257 | ||
2258 | /* Don't become a daemon if the user requested otherwise. */ | |
b8cf055d TL |
2259 | if (no_daemon) { |
2260 | write_client_pid_file (); | |
b00d3884 | 2261 | return; |
b8cf055d | 2262 | } |
b00d3884 TL |
2263 | |
2264 | /* Only do it once. */ | |
2265 | if (state) | |
2266 | return; | |
2267 | state = 1; | |
2268 | ||
2269 | /* Stop logging to stderr... */ | |
2270 | log_perror = 0; | |
2271 | ||
2272 | /* Become a daemon... */ | |
2273 | if ((pid = fork ()) < 0) | |
8ae2d595 | 2274 | log_fatal ("Can't fork daemon: %m"); |
b00d3884 TL |
2275 | else if (pid) |
2276 | exit (0); | |
2277 | /* Become session leader and get pid... */ | |
2278 | pid = setsid (); | |
b8cf055d | 2279 | |
62d0cb47 TL |
2280 | /* Close standard I/O descriptors. */ |
2281 | close(0); | |
2282 | close(1); | |
2283 | close(2); | |
2284 | ||
b8cf055d TL |
2285 | write_client_pid_file (); |
2286 | } | |
2287 | ||
2288 | void write_client_pid_file () | |
2289 | { | |
2290 | FILE *pf; | |
2291 | int pfdesc; | |
2292 | ||
2293 | pfdesc = open (path_dhclient_pid, O_CREAT | O_TRUNC | O_WRONLY, 0644); | |
2294 | ||
2295 | if (pfdesc < 0) { | |
8ae2d595 | 2296 | log_error ("Can't create %s: %m", path_dhclient_pid); |
b8cf055d TL |
2297 | return; |
2298 | } | |
2299 | ||
2300 | pf = fdopen (pfdesc, "w"); | |
2301 | if (!pf) | |
8ae2d595 | 2302 | log_error ("Can't fdopen %s: %m", path_dhclient_pid); |
b8cf055d | 2303 | else { |
19ea90f7 | 2304 | fprintf (pf, "%ld\n", (long)getpid ()); |
b8cf055d TL |
2305 | fclose (pf); |
2306 | } | |
2307 | } | |
2308 | ||
b8cf055d TL |
2309 | void client_location_changed () |
2310 | { | |
2311 | struct interface_info *ip; | |
02d9e453 | 2312 | struct client_state *client; |
b8cf055d TL |
2313 | |
2314 | for (ip = interfaces; ip; ip = ip -> next) { | |
02d9e453 TL |
2315 | for (client = ip -> client; client; client = client -> next) { |
2316 | switch (client -> state) { | |
2317 | case S_SELECTING: | |
2318 | cancel_timeout (send_discover, client); | |
2319 | break; | |
b8cf055d | 2320 | |
02d9e453 TL |
2321 | case S_BOUND: |
2322 | cancel_timeout (state_bound, client); | |
2323 | break; | |
b8cf055d | 2324 | |
02d9e453 TL |
2325 | case S_REBOOTING: |
2326 | case S_REQUESTING: | |
2327 | case S_RENEWING: | |
2328 | cancel_timeout (send_request, client); | |
2329 | break; | |
b8cf055d | 2330 | |
02d9e453 TL |
2331 | case S_INIT: |
2332 | case S_REBINDING: | |
2333 | break; | |
2334 | } | |
2335 | client -> state = S_INIT; | |
2336 | state_reboot (client); | |
b8cf055d | 2337 | } |
b8cf055d TL |
2338 | } |
2339 | } | |
6767b592 | 2340 | |
347de8bd TL |
2341 | void do_release(client) |
2342 | struct client_state *client; | |
2343 | { | |
2344 | /* make_request doesn't initialize xid because it normally comes | |
2345 | from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER, | |
2346 | so pick an xid now. */ | |
2347 | client -> xid = random (); | |
2348 | ||
bdcaf7b9 TL |
2349 | /* is there even a lease to release? */ |
2350 | if (client -> active) { | |
2351 | /* Make a DHCPRELEASE packet, and set appropriate per-interface | |
2352 | flags. */ | |
2353 | make_release (client, client -> active); | |
2354 | client -> destination = iaddr_broadcast; | |
2355 | client -> first_sending = cur_time; | |
2356 | client -> interval = client -> config -> initial_interval; | |
2357 | ||
2358 | /* Zap the medium list... */ | |
2359 | client -> medium = (struct string_list *)0; | |
2360 | ||
2361 | /* Send out the first and only DHCPRELEASE packet. */ | |
2362 | send_release (client); | |
2363 | } | |
347de8bd | 2364 | |
bdcaf7b9 TL |
2365 | /* remove the timeouts for this client */ |
2366 | cancel_timeout (NULL, client); | |
347de8bd | 2367 | |
bdcaf7b9 TL |
2368 | /* if there was no lease, nothing to "do" */ |
2369 | if (client -> active) { | |
2370 | script_init (client, | |
2371 | "RELEASE", (struct string_list *)0); | |
2372 | if (client -> alias) | |
2373 | script_write_params (client, "alias_", | |
2374 | client -> alias); | |
2375 | script_go (client); | |
2376 | } | |
347de8bd TL |
2377 | } |
2378 | ||
2379 | ||
2380 | ||
6767b592 TL |
2381 | /* The client should never receive a relay agent information option, |
2382 | so if it does, log it and discard it. */ | |
2383 | ||
2384 | int parse_agent_information_option (packet, len, data) | |
2385 | struct packet *packet; | |
2386 | int len; | |
2387 | u_int8_t *data; | |
2388 | { | |
2389 | log_info ("relay agent information option received."); | |
2390 | return 1; | |
2391 | } | |
2392 | ||
2393 | /* The client never sends relay agent information options. */ | |
2394 | ||
2395 | unsigned cons_agent_information_options (cfg_options, outpacket, | |
2396 | agentix, length) | |
2397 | struct option_state *cfg_options; | |
2398 | struct dhcp_packet *outpacket; | |
2399 | unsigned agentix; | |
2400 | unsigned length; | |
2401 | { | |
2402 | return length; | |
2403 | } |