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