]>
Commit | Line | Data |
---|---|---|
469cf3a4 | 1 | /* dhclient.c |
95821729 | 2 | |
9bdb9271 | 3 | DHCP Client. */ |
95821729 TL |
4 | |
5 | /* | |
ae566556 | 6 | * Copyright (c) 2004-2009 by Internet Systems Consortium, Inc. ("ISC") |
98311e4b | 7 | * Copyright (c) 1995-2003 by Internet Software Consortium |
95821729 | 8 | * |
98311e4b DH |
9 | * Permission to use, copy, modify, and distribute this software for any |
10 | * purpose with or without fee is hereby granted, provided that the above | |
11 | * copyright notice and this permission notice appear in all copies. | |
95821729 | 12 | * |
98311e4b DH |
13 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES |
14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR | |
16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
19 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
95821729 | 20 | * |
98311e4b DH |
21 | * Internet Systems Consortium, Inc. |
22 | * 950 Charter Street | |
23 | * Redwood City, CA 94063 | |
24 | * <info@isc.org> | |
2c85ac9b | 25 | * https://www.isc.org/ |
31730f17 | 26 | * |
3b106356 TL |
27 | * This code is based on the original client state machine that was |
28 | * written by Elliot Poger. The code has been extensively hacked on | |
29 | * by Ted Lemon since then, so any mistakes you find are probably his | |
30 | * fault and not Elliot's. | |
95821729 TL |
31 | */ |
32 | ||
c1503c57 | 33 | #include "dhcpd.h" |
fe5b0fdd DH |
34 | #include <syslog.h> |
35 | #include <signal.h> | |
36 | #include <errno.h> | |
be62cf06 | 37 | #include <sys/time.h> |
fe5b0fdd | 38 | #include <sys/wait.h> |
e847f983 | 39 | #include <limits.h> |
98bf1607 | 40 | #include <dns/result.h> |
e581d615 | 41 | |
4a0d788a TL |
42 | TIME default_lease_time = 43200; /* 12 hours... */ |
43 | TIME max_lease_time = 86400; /* 24 hours... */ | |
44 | ||
b1b7b521 | 45 | const char *path_dhclient_conf = _PATH_DHCLIENT_CONF; |
98bd7ca0 DH |
46 | const char *path_dhclient_db = NULL; |
47 | const char *path_dhclient_pid = NULL; | |
420d8b3f | 48 | static char path_dhclient_script_array[] = _PATH_DHCLIENT_SCRIPT; |
c08637bb | 49 | char *path_dhclient_script = path_dhclient_script_array; |
cc26de46 | 50 | |
36f5cb7c TL |
51 | int dhcp_max_agent_option_packet_length = 0; |
52 | ||
b8cf055d TL |
53 | int interfaces_requested = 0; |
54 | ||
cc26de46 TL |
55 | struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } }; |
56 | struct iaddr iaddr_any = { 4, { 0, 0, 0, 0 } }; | |
48d68880 | 57 | struct in_addr inaddr_any; |
cc26de46 | 58 | struct sockaddr_in sockaddr_broadcast; |
11373fb6 | 59 | struct in_addr giaddr; |
98bd7ca0 | 60 | struct data_string default_duid; |
a41d7a25 | 61 | int duid_type = 0; |
469cf3a4 | 62 | |
cc26de46 TL |
63 | /* ASSERT_STATE() does nothing now; it used to be |
64 | assert (state_is == state_shouldbe). */ | |
65 | #define ASSERT_STATE(state_is, state_shouldbe) {} | |
469cf3a4 | 66 | |
ae566556 SR |
67 | static const char copyright[] = "Copyright 2004-2009 Internet Systems Consortium."; |
68 | static const char arr [] = "All rights reserved."; | |
69 | static const char message [] = "Internet Systems Consortium DHCP Client"; | |
2c85ac9b | 70 | static const char url [] = "For info, please visit https://www.isc.org/software/dhcp/"; |
c1503c57 | 71 | |
420d8b3f FD |
72 | u_int16_t local_port = 0; |
73 | u_int16_t remote_port = 0; | |
74 | int no_daemon = 0; | |
75 | struct string_list *client_env = NULL; | |
76 | int client_env_count = 0; | |
77 | int onetry = 0; | |
78 | int quiet = 1; | |
79 | int nowait = 0; | |
80 | int stateless = 0; | |
81 | int wanted_ia_na = -1; /* the absolute value is the real one. */ | |
82 | int wanted_ia_ta = 0; | |
83 | int wanted_ia_pd = 0; | |
98bd7ca0 | 84 | char *mockup_relay = NULL; |
c1503c57 | 85 | |
3dbe2246 FD |
86 | void run_stateless(int exit_mode); |
87 | ||
420d8b3f | 88 | static void usage(void); |
95821729 | 89 | |
98bd7ca0 DH |
90 | static isc_result_t write_duid(struct data_string *duid); |
91 | ||
3dbe2246 | 92 | int |
98bd7ca0 | 93 | main(int argc, char **argv) { |
185d16f9 | 94 | int fd; |
c1503c57 | 95 | int i; |
cc26de46 | 96 | struct interface_info *ip; |
02d9e453 | 97 | struct client_state *client; |
b1b7b521 | 98 | unsigned seed; |
420d8b3f | 99 | char *server = NULL; |
628beb0e | 100 | isc_result_t status; |
3dbe2246 FD |
101 | int exit_mode = 0; |
102 | int release_mode = 0; | |
be62cf06 | 103 | struct timeval tv; |
347de8bd TL |
104 | omapi_object_t *listener; |
105 | isc_result_t result; | |
134ceef8 | 106 | int persist = 0; |
cfa7212d TL |
107 | int no_dhclient_conf = 0; |
108 | int no_dhclient_db = 0; | |
109 | int no_dhclient_pid = 0; | |
c08637bb | 110 | int no_dhclient_script = 0; |
c998be87 | 111 | #ifdef DHCPv6 |
98bd7ca0 | 112 | int local_family_set = 0; |
c998be87 | 113 | #endif /* DHCPv6 */ |
cfa7212d | 114 | char *s; |
6c6d5928 | 115 | |
98bd7ca0 DH |
116 | /* Initialize client globals. */ |
117 | memset(&default_duid, 0, sizeof(default_duid)); | |
118 | ||
3dbe2246 FD |
119 | /* Make sure that file descriptors 0 (stdin), 1, (stdout), and |
120 | 2 (stderr) are open. To do this, we assume that when we | |
121 | open a file the lowest available file descriptor is used. */ | |
122 | fd = open("/dev/null", O_RDWR); | |
123 | if (fd == 0) | |
124 | fd = open("/dev/null", O_RDWR); | |
125 | if (fd == 1) | |
126 | fd = open("/dev/null", O_RDWR); | |
127 | if (fd == 2) | |
128 | log_perror = 0; /* No sense logging to /dev/null. */ | |
129 | else if (fd != -1) | |
130 | close(fd); | |
ee8a3653 | 131 | |
420d8b3f | 132 | openlog("dhclient", LOG_NDELAY, LOG_DAEMON); |
95821729 | 133 | |
420d8b3f FD |
134 | #if !(defined(DEBUG) || defined(__CYGWIN32__)) |
135 | setlogmask(LOG_UPTO(LOG_INFO)); | |
3dbe2246 | 136 | #endif |
c1503c57 | 137 | |
98bf1607 SR |
138 | /* Set up the isc and dns library managers */ |
139 | status = dhcp_context_create(); | |
140 | if (status != ISC_R_SUCCESS) | |
141 | log_fatal("Can't initialize context: %s", | |
142 | isc_result_totext(status)); | |
143 | ||
67569fa6 | 144 | /* Set up the OMAPI. */ |
420d8b3f | 145 | status = omapi_init(); |
67569fa6 | 146 | if (status != ISC_R_SUCCESS) |
420d8b3f FD |
147 | log_fatal("Can't initialize OMAPI: %s", |
148 | isc_result_totext(status)); | |
67569fa6 TL |
149 | |
150 | /* Set up the OMAPI wrappers for various server database internal | |
151 | objects. */ | |
420d8b3f | 152 | dhcp_common_objects_setup(); |
67569fa6 TL |
153 | |
154 | dhcp_interface_discovery_hook = dhclient_interface_discovery_hook; | |
155 | dhcp_interface_shutdown_hook = dhclient_interface_shutdown_hook; | |
57710b89 | 156 | dhcp_interface_startup_hook = dhclient_interface_startup_hook; |
67569fa6 | 157 | |
c1503c57 | 158 | for (i = 1; i < argc; i++) { |
3dbe2246 | 159 | if (!strcmp(argv[i], "-r")) { |
182b187e EH |
160 | release_mode = 1; |
161 | no_daemon = 1; | |
162 | #ifdef DHCPv6 | |
163 | } else if (!strcmp(argv[i], "-4")) { | |
98bd7ca0 DH |
164 | if (local_family_set && local_family != AF_INET) |
165 | log_fatal("Client can only do v4 or v6, not " | |
166 | "both."); | |
167 | local_family_set = 1; | |
168 | local_family = AF_INET; | |
169 | } else if (!strcmp(argv[i], "-6")) { | |
170 | if (local_family_set && local_family != AF_INET6) | |
171 | log_fatal("Client can only do v4 or v6, not " | |
172 | "both."); | |
173 | local_family_set = 1; | |
174 | local_family = AF_INET6; | |
182b187e | 175 | #endif /* DHCPv6 */ |
420d8b3f | 176 | } else if (!strcmp(argv[i], "-x")) { /* eXit, no release */ |
3dbe2246 FD |
177 | release_mode = 0; |
178 | no_daemon = 0; | |
179 | exit_mode = 1; | |
420d8b3f | 180 | } else if (!strcmp(argv[i], "-p")) { |
c1503c57 | 181 | if (++i == argc) |
420d8b3f | 182 | usage(); |
59112e84 | 183 | local_port = validate_port(argv[i]); |
420d8b3f FD |
184 | log_debug("binding to user-specified port %d", |
185 | ntohs(local_port)); | |
186 | } else if (!strcmp(argv[i], "-d")) { | |
b00d3884 | 187 | no_daemon = 1; |
8ea19a71 | 188 | quiet = 0; |
420d8b3f | 189 | } else if (!strcmp(argv[i], "-pf")) { |
3dbe2246 | 190 | if (++i == argc) |
420d8b3f FD |
191 | usage(); |
192 | path_dhclient_pid = argv[i]; | |
cfa7212d | 193 | no_dhclient_pid = 1; |
420d8b3f | 194 | } else if (!strcmp(argv[i], "-cf")) { |
3dbe2246 | 195 | if (++i == argc) |
420d8b3f FD |
196 | usage(); |
197 | path_dhclient_conf = argv[i]; | |
cfa7212d | 198 | no_dhclient_conf = 1; |
420d8b3f | 199 | } else if (!strcmp(argv[i], "-lf")) { |
3dbe2246 | 200 | if (++i == argc) |
420d8b3f FD |
201 | usage(); |
202 | path_dhclient_db = argv[i]; | |
cfa7212d | 203 | no_dhclient_db = 1; |
420d8b3f | 204 | } else if (!strcmp(argv[i], "-sf")) { |
c08637bb | 205 | if (++i == argc) |
420d8b3f FD |
206 | usage(); |
207 | path_dhclient_script = argv[i]; | |
c08637bb | 208 | no_dhclient_script = 1; |
420d8b3f | 209 | } else if (!strcmp(argv[i], "-1")) { |
c08637bb | 210 | onetry = 1; |
420d8b3f | 211 | } else if (!strcmp(argv[i], "-q")) { |
62d0cb47 | 212 | quiet = 1; |
420d8b3f | 213 | } else if (!strcmp(argv[i], "-s")) { |
3020a2c1 | 214 | if (++i == argc) |
420d8b3f FD |
215 | usage(); |
216 | server = argv[i]; | |
217 | } else if (!strcmp(argv[i], "-g")) { | |
11373fb6 | 218 | if (++i == argc) |
420d8b3f FD |
219 | usage(); |
220 | mockup_relay = argv[i]; | |
221 | } else if (!strcmp(argv[i], "-nw")) { | |
98311e4b | 222 | nowait = 1; |
420d8b3f | 223 | } else if (!strcmp(argv[i], "-n")) { |
347de8bd | 224 | /* do not start up any interfaces */ |
3dbe2246 | 225 | interfaces_requested = -1; |
420d8b3f | 226 | } else if (!strcmp(argv[i], "-w")) { |
134ceef8 TL |
227 | /* do not exit if there are no broadcast interfaces. */ |
228 | persist = 1; | |
420d8b3f | 229 | } else if (!strcmp(argv[i], "-e")) { |
b1423aed TL |
230 | struct string_list *tmp; |
231 | if (++i == argc) | |
420d8b3f FD |
232 | usage(); |
233 | tmp = dmalloc(strlen(argv[i]) + sizeof *tmp, MDL); | |
b1423aed | 234 | if (!tmp) |
420d8b3f FD |
235 | log_fatal("No memory for %s", argv[i]); |
236 | strcpy(tmp->string, argv[i]); | |
237 | tmp->next = client_env; | |
b1423aed TL |
238 | client_env = tmp; |
239 | client_env_count++; | |
420d8b3f | 240 | #ifdef DHCPv6 |
3dbe2246 FD |
241 | } else if (!strcmp(argv[i], "-S")) { |
242 | if (local_family_set && (local_family == AF_INET)) { | |
420d8b3f | 243 | usage(); |
3dbe2246 FD |
244 | } |
245 | local_family_set = 1; | |
246 | local_family = AF_INET6; | |
420d8b3f | 247 | wanted_ia_na = 0; |
3dbe2246 | 248 | stateless = 1; |
420d8b3f FD |
249 | } else if (!strcmp(argv[i], "-N")) { |
250 | if (local_family_set && (local_family == AF_INET)) { | |
251 | usage(); | |
252 | } | |
253 | local_family_set = 1; | |
254 | local_family = AF_INET6; | |
255 | if (wanted_ia_na < 0) { | |
256 | wanted_ia_na = 0; | |
257 | } | |
258 | wanted_ia_na++; | |
259 | } else if (!strcmp(argv[i], "-T")) { | |
260 | if (local_family_set && (local_family == AF_INET)) { | |
261 | usage(); | |
262 | } | |
263 | local_family_set = 1; | |
264 | local_family = AF_INET6; | |
265 | if (wanted_ia_na < 0) { | |
266 | wanted_ia_na = 0; | |
267 | } | |
268 | wanted_ia_ta++; | |
269 | } else if (!strcmp(argv[i], "-P")) { | |
270 | if (local_family_set && (local_family == AF_INET)) { | |
271 | usage(); | |
272 | } | |
273 | local_family_set = 1; | |
274 | local_family = AF_INET6; | |
275 | if (wanted_ia_na < 0) { | |
276 | wanted_ia_na = 0; | |
277 | } | |
278 | wanted_ia_pd++; | |
a41d7a25 PS |
279 | } else if (!strcmp(argv[i], "-D")) { |
280 | if (local_family_set && (local_family == AF_INET)) { | |
281 | usage(); | |
282 | } | |
283 | local_family_set = 1; | |
284 | local_family = AF_INET6; | |
285 | if (++i == argc) | |
286 | usage(); | |
287 | if (!strcasecmp(argv[i], "LL")) { | |
288 | duid_type = DUID_LL; | |
289 | } else if (!strcasecmp(argv[i], "LLT")) { | |
290 | duid_type = DUID_LLT; | |
291 | } else { | |
292 | usage(); | |
293 | } | |
420d8b3f | 294 | #endif /* DHCPv6 */ |
8ea19a71 DH |
295 | } else if (!strcmp(argv[i], "-v")) { |
296 | quiet = 0; | |
420d8b3f FD |
297 | } else if (!strcmp(argv[i], "--version")) { |
298 | log_info("isc-dhclient-%s", PACKAGE_VERSION); | |
299 | exit(0); | |
300 | } else if (argv[i][0] == '-') { | |
301 | usage(); | |
3dbe2246 | 302 | } else if (interfaces_requested < 0) { |
420d8b3f | 303 | usage(); |
98311e4b | 304 | } else { |
420d8b3f FD |
305 | struct interface_info *tmp = NULL; |
306 | ||
307 | status = interface_allocate(&tmp, MDL); | |
3dbe2246 | 308 | if (status != ISC_R_SUCCESS) |
420d8b3f FD |
309 | log_fatal("Can't record interface %s:%s", |
310 | argv[i], isc_result_totext(status)); | |
88cd8aca DH |
311 | if (strlen(argv[i]) >= sizeof(tmp->name)) |
312 | log_fatal("%s: interface name too long (is %ld)", | |
420d8b3f | 313 | argv[i], (long)strlen(argv[i])); |
88cd8aca | 314 | strcpy(tmp->name, argv[i]); |
20916cae | 315 | if (interfaces) { |
420d8b3f FD |
316 | interface_reference(&tmp->next, |
317 | interfaces, MDL); | |
318 | interface_dereference(&interfaces, MDL); | |
20916cae | 319 | } |
420d8b3f FD |
320 | interface_reference(&interfaces, tmp, MDL); |
321 | tmp->flags = INTERFACE_REQUESTED; | |
3dbe2246 FD |
322 | interfaces_requested++; |
323 | } | |
c1503c57 | 324 | } |
62d0cb47 | 325 | |
420d8b3f FD |
326 | if (wanted_ia_na < 0) { |
327 | wanted_ia_na = 1; | |
328 | } | |
329 | ||
330 | /* Support only one (requested) interface for Prefix Delegation. */ | |
331 | if (wanted_ia_pd && (interfaces_requested != 1)) { | |
332 | usage(); | |
333 | } | |
334 | ||
335 | if (!no_dhclient_conf && (s = getenv("PATH_DHCLIENT_CONF"))) { | |
cfa7212d TL |
336 | path_dhclient_conf = s; |
337 | } | |
420d8b3f | 338 | if (!no_dhclient_db && (s = getenv("PATH_DHCLIENT_DB"))) { |
cfa7212d TL |
339 | path_dhclient_db = s; |
340 | } | |
420d8b3f | 341 | if (!no_dhclient_pid && (s = getenv("PATH_DHCLIENT_PID"))) { |
cfa7212d TL |
342 | path_dhclient_pid = s; |
343 | } | |
420d8b3f | 344 | if (!no_dhclient_script && (s = getenv("PATH_DHCLIENT_SCRIPT"))) { |
c08637bb TL |
345 | path_dhclient_script = s; |
346 | } | |
cfa7212d | 347 | |
98bd7ca0 DH |
348 | /* Set up the initial dhcp option universe. */ |
349 | initialize_common_option_spaces(); | |
350 | ||
351 | /* Assign v4 or v6 specific running parameters. */ | |
352 | if (local_family == AF_INET) | |
353 | dhcpv4_client_assignments(); | |
fe5b0fdd | 354 | #ifdef DHCPv6 |
98bd7ca0 DH |
355 | else if (local_family == AF_INET6) |
356 | dhcpv6_client_assignments(); | |
fe5b0fdd | 357 | #endif /* DHCPv6 */ |
98bd7ca0 DH |
358 | else |
359 | log_fatal("Impossible condition at %s:%d.", MDL); | |
360 | ||
a546f2a7 EH |
361 | /* |
362 | * convert relative path names to absolute, for files that need | |
363 | * to be reopened after chdir() has been called | |
364 | */ | |
365 | if (path_dhclient_db[0] != '/') { | |
366 | char *path = dmalloc(PATH_MAX, MDL); | |
367 | if (path == NULL) | |
368 | log_fatal("No memory for filename\n"); | |
420d8b3f | 369 | path_dhclient_db = realpath(path_dhclient_db, path); |
a546f2a7 EH |
370 | if (path_dhclient_db == NULL) |
371 | log_fatal("%s: %s", path, strerror(errno)); | |
372 | } | |
3dbe2246 | 373 | |
a546f2a7 EH |
374 | if (path_dhclient_script[0] != '/') { |
375 | char *path = dmalloc(PATH_MAX, MDL); | |
376 | if (path == NULL) | |
377 | log_fatal("No memory for filename\n"); | |
420d8b3f | 378 | path_dhclient_script = realpath(path_dhclient_script, path); |
a546f2a7 EH |
379 | if (path_dhclient_script == NULL) |
380 | log_fatal("%s: %s", path, strerror(errno)); | |
381 | } | |
3dbe2246 | 382 | |
a546f2a7 | 383 | /* first kill off any currently running client */ |
af5fa176 | 384 | if (release_mode || exit_mode) { |
98311e4b DH |
385 | FILE *pidfd; |
386 | pid_t oldpid; | |
387 | long temp; | |
388 | int e; | |
389 | ||
390 | oldpid = 0; | |
391 | if ((pidfd = fopen(path_dhclient_pid, "r")) != NULL) { | |
392 | e = fscanf(pidfd, "%ld\n", &temp); | |
393 | oldpid = (pid_t)temp; | |
394 | ||
395 | if (e != 0 && e != EOF) { | |
182b187e EH |
396 | if (oldpid) |
397 | kill(oldpid, SIGTERM); | |
98311e4b DH |
398 | } |
399 | fclose(pidfd); | |
400 | } | |
347de8bd TL |
401 | } |
402 | ||
62d0cb47 | 403 | if (!quiet) { |
420d8b3f FD |
404 | log_info("%s %s", message, PACKAGE_VERSION); |
405 | log_info(copyright); | |
406 | log_info(arr); | |
407 | log_info(url); | |
408 | log_info("%s", ""); | |
8ea19a71 | 409 | } else { |
78a33b82 | 410 | log_perror = 0; |
8ea19a71 DH |
411 | quiet_interface_discovery = 1; |
412 | } | |
62d0cb47 | 413 | |
11373fb6 TL |
414 | /* If we're given a relay agent address to insert, for testing |
415 | purposes, figure out what it is. */ | |
98bd7ca0 | 416 | if (mockup_relay) { |
420d8b3f | 417 | if (!inet_aton(mockup_relay, &giaddr)) { |
11373fb6 | 418 | struct hostent *he; |
420d8b3f | 419 | he = gethostbyname(mockup_relay); |
11373fb6 | 420 | if (he) { |
420d8b3f FD |
421 | memcpy(&giaddr, he->h_addr_list[0], |
422 | sizeof giaddr); | |
11373fb6 | 423 | } else { |
420d8b3f | 424 | log_fatal("%s: no such host", mockup_relay); |
11373fb6 TL |
425 | } |
426 | } | |
427 | } | |
428 | ||
95821729 | 429 | /* Get the current time... */ |
be62cf06 | 430 | gettimeofday(&cur_tv, NULL); |
95821729 | 431 | |
b00d3884 | 432 | sockaddr_broadcast.sin_family = AF_INET; |
cc26de46 | 433 | sockaddr_broadcast.sin_port = remote_port; |
3020a2c1 | 434 | if (server) { |
420d8b3f | 435 | if (!inet_aton(server, &sockaddr_broadcast.sin_addr)) { |
3020a2c1 | 436 | struct hostent *he; |
420d8b3f | 437 | he = gethostbyname(server); |
3020a2c1 | 438 | if (he) { |
420d8b3f FD |
439 | memcpy(&sockaddr_broadcast.sin_addr, |
440 | he->h_addr_list[0], | |
441 | sizeof sockaddr_broadcast.sin_addr); | |
3020a2c1 TL |
442 | } else |
443 | sockaddr_broadcast.sin_addr.s_addr = | |
444 | INADDR_BROADCAST; | |
445 | } | |
446 | } else { | |
447 | sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST; | |
448 | } | |
11373fb6 | 449 | |
48d68880 | 450 | inaddr_any.s_addr = INADDR_ANY; |
469cf3a4 | 451 | |
3dbe2246 FD |
452 | /* Stateless special case. */ |
453 | if (stateless) { | |
420d8b3f FD |
454 | if (release_mode || (wanted_ia_na > 0) || |
455 | wanted_ia_ta || wanted_ia_pd || | |
456 | (interfaces_requested != 1)) { | |
457 | usage(); | |
3dbe2246 | 458 | } |
420d8b3f | 459 | run_stateless(exit_mode); |
3dbe2246 FD |
460 | return 0; |
461 | } | |
462 | ||
cc26de46 | 463 | /* Discover all the network interfaces. */ |
420d8b3f | 464 | discover_interfaces(DISCOVER_UNCONFIGURED); |
469cf3a4 | 465 | |
cc26de46 | 466 | /* Parse the dhclient.conf file. */ |
420d8b3f | 467 | read_client_conf(); |
469cf3a4 | 468 | |
cc26de46 | 469 | /* Parse the lease database. */ |
420d8b3f | 470 | read_client_leases(); |
469cf3a4 | 471 | |
cc26de46 | 472 | /* Rewrite the lease database... */ |
420d8b3f | 473 | rewrite_client_leases(); |
469cf3a4 | 474 | |
ce0ec46d TL |
475 | /* XXX */ |
476 | /* config_counter(&snd_counter, &rcv_counter); */ | |
477 | ||
420d8b3f FD |
478 | /* |
479 | * If no broadcast interfaces were discovered, call the script | |
480 | * and tell it so. | |
481 | */ | |
cc26de46 | 482 | if (!interfaces) { |
420d8b3f FD |
483 | /* |
484 | * Call dhclient-script with the NBI flag, | |
485 | * in case somebody cares. | |
486 | */ | |
487 | script_init(NULL, "NBI", NULL); | |
488 | script_go(NULL); | |
489 | ||
490 | /* | |
491 | * If we haven't been asked to persist, waiting for new | |
492 | * interfaces, then just exit. | |
493 | */ | |
134ceef8 TL |
494 | if (!persist) { |
495 | /* Nothing more to do. */ | |
420d8b3f FD |
496 | log_info("No broadcast interfaces found - exiting."); |
497 | exit(0); | |
134ceef8 | 498 | } |
af5fa176 | 499 | } else if (!release_mode && !exit_mode) { |
cc26de46 | 500 | /* Call the script with the list of interfaces. */ |
420d8b3f FD |
501 | for (ip = interfaces; ip; ip = ip->next) { |
502 | /* | |
503 | * If interfaces were specified, don't configure | |
504 | * interfaces that weren't specified! | |
505 | */ | |
3dbe2246 | 506 | if ((interfaces_requested > 0) && |
420d8b3f FD |
507 | ((ip->flags & (INTERFACE_REQUESTED | |
508 | INTERFACE_AUTOMATIC)) != | |
62d0cb47 | 509 | INTERFACE_REQUESTED)) |
5c2f78b4 | 510 | continue; |
98bd7ca0 DH |
511 | |
512 | if (local_family == AF_INET6) { | |
513 | script_init(ip->client, "PREINIT6", NULL); | |
514 | } else { | |
515 | script_init(ip->client, "PREINIT", NULL); | |
516 | if (ip->client->alias != NULL) | |
517 | script_write_params(ip->client, | |
518 | "alias_", | |
519 | ip->client->alias); | |
520 | } | |
420d8b3f | 521 | script_go(ip->client); |
cc26de46 TL |
522 | } |
523 | } | |
469cf3a4 | 524 | |
cc26de46 TL |
525 | /* At this point, all the interfaces that the script thinks |
526 | are relevant should be running, so now we once again call | |
527 | discover_interfaces(), and this time ask it to actually set | |
528 | up the interfaces. */ | |
420d8b3f FD |
529 | discover_interfaces(interfaces_requested != 0 |
530 | ? DISCOVER_REQUESTED | |
531 | : DISCOVER_RUNNING); | |
cc26de46 | 532 | |
66f973e4 TL |
533 | /* Make up a seed for the random number generator from current |
534 | time plus the sum of the last four bytes of each | |
535 | interface's hardware address interpreted as an integer. | |
536 | Not much entropy, but we're booting, so we're not likely to | |
537 | find anything better. */ | |
02d9e453 | 538 | seed = 0; |
420d8b3f | 539 | for (ip = interfaces; ip; ip = ip->next) { |
66f973e4 | 540 | int junk; |
420d8b3f FD |
541 | memcpy(&junk, |
542 | &ip->hw_address.hbuf[ip->hw_address.hlen - | |
543 | sizeof seed], sizeof seed); | |
66f973e4 TL |
544 | seed += junk; |
545 | } | |
420d8b3f | 546 | srandom(seed + cur_time); |
66f973e4 | 547 | |
cc26de46 | 548 | /* Start a configuration state machine for each interface. */ |
fe5b0fdd | 549 | #ifdef DHCPv6 |
98bd7ca0 | 550 | if (local_family == AF_INET6) { |
98bd7ca0 DH |
551 | /* Establish a default DUID. This may be moved to the |
552 | * DHCPv4 area later. | |
553 | */ | |
554 | if (default_duid.len == 0) { | |
555 | if (default_duid.buffer != NULL) | |
556 | data_string_forget(&default_duid, MDL); | |
557 | ||
558 | form_duid(&default_duid, MDL); | |
559 | write_duid(&default_duid); | |
560 | } | |
561 | ||
562 | for (ip = interfaces ; ip != NULL ; ip = ip->next) { | |
563 | for (client = ip->client ; client != NULL ; | |
564 | client = client->next) { | |
3dbe2246 FD |
565 | if (release_mode) { |
566 | start_release6(client); | |
567 | continue; | |
568 | } else if (exit_mode) { | |
569 | unconfigure6(client, "STOP6"); | |
570 | continue; | |
571 | } | |
af5fa176 | 572 | |
98bd7ca0 DH |
573 | /* If we have a previous binding, Confirm |
574 | * that we can (or can't) still use it. | |
575 | */ | |
cabdb9b1 FD |
576 | if ((client->active_lease != NULL) && |
577 | !client->active_lease->released) | |
98bd7ca0 DH |
578 | start_confirm6(client); |
579 | else | |
580 | start_init6(client); | |
581 | } | |
582 | } | |
3dbe2246 | 583 | } else |
fe5b0fdd DH |
584 | #endif /* DHCPv6 */ |
585 | { | |
98bd7ca0 DH |
586 | for (ip = interfaces ; ip ; ip = ip->next) { |
587 | ip->flags |= INTERFACE_RUNNING; | |
588 | for (client = ip->client ; client ; | |
589 | client = client->next) { | |
3dbe2246 FD |
590 | if (exit_mode) |
591 | state_stop(client); | |
592 | else if (release_mode) | |
98bd7ca0 DH |
593 | do_release(client); |
594 | else { | |
595 | client->state = S_INIT; | |
596 | /* Set up a timeout to start the | |
597 | * initialization process. | |
598 | */ | |
be62cf06 FD |
599 | tv.tv_sec = cur_time + random() % 5; |
600 | tv.tv_usec = 0; | |
601 | add_timeout(&tv, state_reboot, | |
602 | client, 0, 0); | |
98bd7ca0 | 603 | } |
347de8bd | 604 | } |
02d9e453 | 605 | } |
cc26de46 | 606 | } |
469cf3a4 | 607 | |
87202683 | 608 | if (exit_mode) |
347de8bd | 609 | return 0; |
87202683 FD |
610 | if (release_mode) { |
611 | #ifndef DHCPv6 | |
612 | return 0; | |
613 | #else | |
614 | if (local_family == AF_INET6) { | |
615 | if (onetry) | |
616 | return 0; | |
617 | } else | |
618 | return 0; | |
619 | #endif /* DHCPv6 */ | |
620 | } | |
347de8bd TL |
621 | |
622 | /* Start up a listener for the object management API protocol. */ | |
40a59753 | 623 | if (top_level_config.omapi_port != -1) { |
420d8b3f FD |
624 | listener = NULL; |
625 | result = omapi_generic_new(&listener, MDL); | |
40a59753 | 626 | if (result != ISC_R_SUCCESS) |
420d8b3f FD |
627 | log_fatal("Can't allocate new generic object: %s\n", |
628 | isc_result_totext(result)); | |
629 | result = omapi_protocol_listen(listener, | |
630 | (unsigned) | |
631 | top_level_config.omapi_port, | |
632 | 1); | |
40a59753 | 633 | if (result != ISC_R_SUCCESS) |
420d8b3f FD |
634 | log_fatal("Can't start OMAPI protocol: %s", |
635 | isc_result_totext (result)); | |
40a59753 | 636 | } |
347de8bd | 637 | |
84c4adde TL |
638 | /* Set up the bootp packet handler... */ |
639 | bootp_packet_handler = do_packet; | |
fe5b0fdd | 640 | #ifdef DHCPv6 |
98bd7ca0 | 641 | dhcpv6_packet_handler = do_packet6; |
fe5b0fdd | 642 | #endif /* DHCPv6 */ |
84c4adde | 643 | |
420d8b3f FD |
644 | #if defined(DEBUG_MEMORY_LEAKAGE) || defined(DEBUG_MALLOC_POOL) || \ |
645 | defined(DEBUG_MEMORY_LEAKAGE_ON_EXIT) | |
1c44f6bd TL |
646 | dmalloc_cutoff_generation = dmalloc_generation; |
647 | dmalloc_longterm = dmalloc_outstanding; | |
648 | dmalloc_outstanding = 0; | |
649 | #endif | |
650 | ||
37c1f13f TL |
651 | /* If we're not supposed to wait before getting the address, |
652 | don't. */ | |
653 | if (nowait) | |
420d8b3f | 654 | go_daemon(); |
37c1f13f | 655 | |
b1423aed TL |
656 | /* If we're not going to daemonize, write the pid file |
657 | now. */ | |
37c1f13f | 658 | if (no_daemon || nowait) |
420d8b3f | 659 | write_client_pid_file(); |
b1423aed | 660 | |
cc26de46 | 661 | /* Start dispatching packets and timeouts... */ |
420d8b3f | 662 | dispatch(); |
84c4adde | 663 | |
cc26de46 TL |
664 | /*NOTREACHED*/ |
665 | return 0; | |
469cf3a4 TL |
666 | } |
667 | ||
420d8b3f | 668 | static void usage() |
c1503c57 | 669 | { |
420d8b3f FD |
670 | log_info("%s %s", message, PACKAGE_VERSION); |
671 | log_info(copyright); | |
672 | log_info(arr); | |
673 | log_info(url); | |
89c425dd | 674 | |
420d8b3f | 675 | log_error("Usage: dhclient %s %s", |
182b187e | 676 | #ifdef DHCPv6 |
a41d7a25 | 677 | "[-4|-6] [-SNTP1dvrx] [-nw] [-p <port>] [-D LL|LLT]", |
182b187e | 678 | #else /* DHCPv6 */ |
420d8b3f | 679 | "[-1dvrx] [-nw] [-p <port>]", |
182b187e | 680 | #endif /* DHCPv6 */ |
420d8b3f FD |
681 | "[-s server]"); |
682 | log_error(" [-cf config-file] [-lf lease-file]%s", | |
683 | "[-pf pid-file] [-e VAR=val]"); | |
684 | log_fatal(" [-sf script-file] [interface]"); | |
c1503c57 TL |
685 | } |
686 | ||
3dbe2246 FD |
687 | void run_stateless(int exit_mode) |
688 | { | |
420d8b3f | 689 | #ifdef DHCPv6 |
3dbe2246 FD |
690 | struct client_state *client; |
691 | omapi_object_t *listener; | |
692 | isc_result_t result; | |
693 | ||
694 | /* Discover the network interface. */ | |
695 | discover_interfaces(DISCOVER_REQUESTED); | |
696 | ||
697 | if (!interfaces) | |
698 | usage(); | |
699 | ||
700 | /* Parse the dhclient.conf file. */ | |
701 | read_client_conf(); | |
702 | ||
703 | /* Parse the lease database. */ | |
704 | read_client_leases(); | |
705 | ||
706 | /* Establish a default DUID. */ | |
707 | if (default_duid.len == 0) { | |
708 | if (default_duid.buffer != NULL) | |
709 | data_string_forget(&default_duid, MDL); | |
710 | ||
711 | form_duid(&default_duid, MDL); | |
712 | } | |
713 | ||
714 | /* Start a configuration state machine. */ | |
715 | for (client = interfaces->client ; | |
716 | client != NULL ; | |
717 | client = client->next) { | |
718 | if (exit_mode) { | |
719 | unconfigure6(client, "STOP6"); | |
720 | continue; | |
721 | } | |
722 | start_info_request6(client); | |
723 | } | |
724 | if (exit_mode) | |
725 | return; | |
726 | ||
727 | /* Start up a listener for the object management API protocol. */ | |
728 | if (top_level_config.omapi_port != -1) { | |
420d8b3f | 729 | listener = NULL; |
3dbe2246 FD |
730 | result = omapi_generic_new(&listener, MDL); |
731 | if (result != ISC_R_SUCCESS) | |
732 | log_fatal("Can't allocate new generic object: %s\n", | |
733 | isc_result_totext(result)); | |
734 | result = omapi_protocol_listen(listener, | |
735 | (unsigned) | |
736 | top_level_config.omapi_port, | |
737 | 1); | |
738 | if (result != ISC_R_SUCCESS) | |
739 | log_fatal("Can't start OMAPI protocol: %s", | |
740 | isc_result_totext(result)); | |
741 | } | |
742 | ||
743 | /* Set up the packet handler... */ | |
744 | dhcpv6_packet_handler = do_packet6; | |
745 | ||
420d8b3f FD |
746 | #if defined(DEBUG_MEMORY_LEAKAGE) || defined(DEBUG_MALLOC_POOL) || \ |
747 | defined(DEBUG_MEMORY_LEAKAGE_ON_EXIT) | |
3dbe2246 FD |
748 | dmalloc_cutoff_generation = dmalloc_generation; |
749 | dmalloc_longterm = dmalloc_outstanding; | |
750 | dmalloc_outstanding = 0; | |
751 | #endif | |
752 | ||
753 | /* If we're not supposed to wait before getting the address, | |
754 | don't. */ | |
755 | if (nowait) | |
756 | go_daemon(); | |
757 | ||
758 | /* If we're not going to daemonize, write the pid file | |
759 | now. */ | |
760 | if (no_daemon || nowait) | |
761 | write_client_pid_file(); | |
762 | ||
763 | /* Start dispatching packets and timeouts... */ | |
764 | dispatch(); | |
765 | ||
766 | /*NOTREACHED*/ | |
420d8b3f | 767 | #endif /* DHCPv6 */ |
3dbe2246 FD |
768 | return; |
769 | } | |
770 | ||
20916cae TL |
771 | isc_result_t find_class (struct class **c, |
772 | const char *s, const char *file, int line) | |
02a015fb | 773 | { |
20916cae | 774 | return 0; |
02a015fb TL |
775 | } |
776 | ||
da38df14 | 777 | int check_collection (packet, lease, collection) |
02a015fb | 778 | struct packet *packet; |
da38df14 | 779 | struct lease *lease; |
02a015fb TL |
780 | struct collection *collection; |
781 | { | |
782 | return 0; | |
783 | } | |
784 | ||
785 | void classify (packet, class) | |
786 | struct packet *packet; | |
787 | struct class *class; | |
788 | { | |
789 | } | |
790 | ||
73530742 TL |
791 | int unbill_class (lease, class) |
792 | struct lease *lease; | |
793 | struct class *class; | |
794 | { | |
795 | return 0; | |
796 | } | |
797 | ||
20916cae TL |
798 | int find_subnet (struct subnet **sp, |
799 | struct iaddr addr, const char *file, int line) | |
bfdb842e | 800 | { |
20916cae | 801 | return 0; |
bfdb842e TL |
802 | } |
803 | ||
469cf3a4 | 804 | /* Individual States: |
3dbe2246 | 805 | * |
469cf3a4 TL |
806 | * Each routine is called from the dhclient_state_machine() in one of |
807 | * these conditions: | |
808 | * -> entering INIT state | |
809 | * -> recvpacket_flag == 0: timeout in this state | |
810 | * -> otherwise: received a packet in this state | |
811 | * | |
812 | * Return conditions as handled by dhclient_state_machine(): | |
813 | * Returns 1, sendpacket_flag = 1: send packet, reset timer. | |
814 | * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone). | |
815 | * Returns 0: finish the nap which was interrupted for no good reason. | |
816 | * | |
cc26de46 TL |
817 | * Several per-interface variables are used to keep track of the process: |
818 | * active_lease: the lease that is being used on the interface | |
819 | * (null pointer if not configured yet). | |
820 | * offered_leases: leases corresponding to DHCPOFFER messages that have | |
821 | * been sent to us by DHCP servers. | |
822 | * acked_leases: leases corresponding to DHCPACK messages that have been | |
823 | * sent to us by DHCP servers. | |
824 | * sendpacket: DHCP packet we're trying to send. | |
469cf3a4 | 825 | * destination: IP address to send sendpacket to |
cc26de46 | 826 | * In addition, there are several relevant per-lease variables. |
469cf3a4 | 827 | * T1_expiry, T2_expiry, lease_expiry: lease milestones |
cc26de46 TL |
828 | * In the active lease, these control the process of renewing the lease; |
829 | * In leases on the acked_leases list, this simply determines when we | |
830 | * can no longer legitimately use the lease. | |
469cf3a4 TL |
831 | */ |
832 | ||
02d9e453 TL |
833 | void state_reboot (cpp) |
834 | void *cpp; | |
deff2d59 | 835 | { |
02d9e453 | 836 | struct client_state *client = cpp; |
84c4adde | 837 | |
deff2d59 | 838 | /* If we don't remember an active lease, go straight to INIT. */ |
02d9e453 | 839 | if (!client -> active || |
d758ad8c TL |
840 | client -> active -> is_bootp || |
841 | client -> active -> expiry <= cur_time) { | |
02d9e453 | 842 | state_init (client); |
deff2d59 TL |
843 | return; |
844 | } | |
845 | ||
846 | /* We are in the rebooting state. */ | |
02d9e453 | 847 | client -> state = S_REBOOTING; |
deff2d59 | 848 | |
420d8b3f FD |
849 | /* |
850 | * make_request doesn't initialize xid because it normally comes | |
851 | * from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER, | |
852 | * so pick an xid now. | |
853 | */ | |
02d9e453 | 854 | client -> xid = random (); |
5c2f78b4 | 855 | |
420d8b3f FD |
856 | /* |
857 | * Make a DHCPREQUEST packet, and set | |
858 | * appropriate per-interface flags. | |
859 | */ | |
02d9e453 TL |
860 | make_request (client, client -> active); |
861 | client -> destination = iaddr_broadcast; | |
862 | client -> first_sending = cur_time; | |
863 | client -> interval = client -> config -> initial_interval; | |
deff2d59 | 864 | |
b8cf055d | 865 | /* Zap the medium list... */ |
420d8b3f | 866 | client -> medium = NULL; |
b8cf055d TL |
867 | |
868 | /* Send out the first DHCPREQUEST packet. */ | |
02d9e453 | 869 | send_request (client); |
deff2d59 TL |
870 | } |
871 | ||
872 | /* Called when a lease has completely expired and we've been unable to | |
873 | renew it. */ | |
cc26de46 | 874 | |
02d9e453 TL |
875 | void state_init (cpp) |
876 | void *cpp; | |
469cf3a4 | 877 | { |
02d9e453 | 878 | struct client_state *client = cpp; |
84c4adde | 879 | |
469cf3a4 TL |
880 | ASSERT_STATE(state, S_INIT); |
881 | ||
cc26de46 TL |
882 | /* Make a DHCPDISCOVER packet, and set appropriate per-interface |
883 | flags. */ | |
02d9e453 TL |
884 | make_discover (client, client -> active); |
885 | client -> xid = client -> packet.xid; | |
886 | client -> destination = iaddr_broadcast; | |
887 | client -> state = S_SELECTING; | |
888 | client -> first_sending = cur_time; | |
889 | client -> interval = client -> config -> initial_interval; | |
cc26de46 TL |
890 | |
891 | /* Add an immediate timeout to cause the first DHCPDISCOVER packet | |
892 | to go out. */ | |
02d9e453 | 893 | send_discover (client); |
469cf3a4 TL |
894 | } |
895 | ||
420d8b3f FD |
896 | /* |
897 | * state_selecting is called when one or more DHCPOFFER packets have been | |
898 | * received and a configurable period of time has passed. | |
899 | */ | |
cc26de46 | 900 | |
02d9e453 TL |
901 | void state_selecting (cpp) |
902 | void *cpp; | |
469cf3a4 | 903 | { |
02d9e453 | 904 | struct client_state *client = cpp; |
b00d3884 TL |
905 | struct client_lease *lp, *next, *picked; |
906 | ||
02d9e453 | 907 | |
469cf3a4 TL |
908 | ASSERT_STATE(state, S_SELECTING); |
909 | ||
420d8b3f FD |
910 | /* |
911 | * Cancel state_selecting and send_discover timeouts, since either | |
912 | * one could have got us here. | |
913 | */ | |
02d9e453 TL |
914 | cancel_timeout (state_selecting, client); |
915 | cancel_timeout (send_discover, client); | |
cc26de46 | 916 | |
420d8b3f FD |
917 | /* |
918 | * We have received one or more DHCPOFFER packets. Currently, | |
919 | * the only criterion by which we judge leases is whether or | |
920 | * not we get a response when we arp for them. | |
921 | */ | |
922 | picked = NULL; | |
02d9e453 | 923 | for (lp = client -> offered_leases; lp; lp = next) { |
b00d3884 TL |
924 | next = lp -> next; |
925 | ||
420d8b3f FD |
926 | /* |
927 | * Check to see if we got an ARPREPLY for the address | |
928 | * in this particular lease. | |
929 | */ | |
b00d3884 | 930 | if (!picked) { |
b00d3884 | 931 | picked = lp; |
420d8b3f | 932 | picked -> next = NULL; |
b00d3884 | 933 | } else { |
02a015fb | 934 | destroy_client_lease (lp); |
b00d3884 TL |
935 | } |
936 | } | |
420d8b3f | 937 | client -> offered_leases = NULL; |
b00d3884 | 938 | |
420d8b3f FD |
939 | /* |
940 | * If we just tossed all the leases we were offered, go back | |
941 | * to square one. | |
942 | */ | |
b00d3884 | 943 | if (!picked) { |
02d9e453 TL |
944 | client -> state = S_INIT; |
945 | state_init (client); | |
b00d3884 TL |
946 | return; |
947 | } | |
948 | ||
66f973e4 | 949 | /* If it was a BOOTREPLY, we can just take the address right now. */ |
2455808f | 950 | if (picked -> is_bootp) { |
02d9e453 | 951 | client -> new = picked; |
66f973e4 TL |
952 | |
953 | /* Make up some lease expiry times | |
954 | XXX these should be configurable. */ | |
02d9e453 TL |
955 | client -> new -> expiry = cur_time + 12000; |
956 | client -> new -> renewal += cur_time + 8000; | |
957 | client -> new -> rebind += cur_time + 10000; | |
66f973e4 | 958 | |
02d9e453 | 959 | client -> state = S_REQUESTING; |
66f973e4 TL |
960 | |
961 | /* Bind to the address we received. */ | |
02d9e453 | 962 | bind_lease (client); |
66f973e4 TL |
963 | return; |
964 | } | |
965 | ||
b00d3884 | 966 | /* Go to the REQUESTING state. */ |
02d9e453 TL |
967 | client -> destination = iaddr_broadcast; |
968 | client -> state = S_REQUESTING; | |
969 | client -> first_sending = cur_time; | |
970 | client -> interval = client -> config -> initial_interval; | |
cc26de46 | 971 | |
b00d3884 | 972 | /* Make a DHCPREQUEST packet from the lease we picked. */ |
02d9e453 TL |
973 | make_request (client, picked); |
974 | client -> xid = client -> packet.xid; | |
469cf3a4 | 975 | |
b00d3884 | 976 | /* Toss the lease we picked - we'll get it back in a DHCPACK. */ |
02a015fb | 977 | destroy_client_lease (picked); |
b00d3884 | 978 | |
cc26de46 | 979 | /* Add an immediate timeout to send the first DHCPREQUEST packet. */ |
02d9e453 | 980 | send_request (client); |
3dbe2246 | 981 | } |
469cf3a4 | 982 | |
cc26de46 TL |
983 | /* state_requesting is called when we receive a DHCPACK message after |
984 | having sent out one or more DHCPREQUEST packets. */ | |
469cf3a4 | 985 | |
cc26de46 TL |
986 | void dhcpack (packet) |
987 | struct packet *packet; | |
469cf3a4 | 988 | { |
cc26de46 | 989 | struct interface_info *ip = packet -> interface; |
02d9e453 | 990 | struct client_state *client; |
cc26de46 | 991 | struct client_lease *lease; |
02a015fb TL |
992 | struct option_cache *oc; |
993 | struct data_string ds; | |
3dbe2246 | 994 | |
cc26de46 TL |
995 | /* If we're not receptive to an offer right now, or if the offer |
996 | has an unrecognizable transaction id, then just drop it. */ | |
02d9e453 TL |
997 | for (client = ip -> client; client; client = client -> next) { |
998 | if (client -> xid == packet -> raw -> xid) | |
999 | break; | |
1000 | } | |
1001 | if (!client || | |
31730f17 | 1002 | (packet -> interface -> hw_address.hlen - 1 != |
5c2f78b4 | 1003 | packet -> raw -> hlen) || |
31730f17 | 1004 | (memcmp (&packet -> interface -> hw_address.hbuf [1], |
fcaec4ef | 1005 | packet -> raw -> chaddr, packet -> raw -> hlen))) { |
2d1b06e0 | 1006 | #if defined (DEBUG) |
8ae2d595 | 1007 | log_debug ("DHCPACK in wrong transaction."); |
2d1b06e0 | 1008 | #endif |
cc26de46 | 1009 | return; |
469cf3a4 | 1010 | } |
469cf3a4 | 1011 | |
02d9e453 TL |
1012 | if (client -> state != S_REBOOTING && |
1013 | client -> state != S_REQUESTING && | |
1014 | client -> state != S_RENEWING && | |
1015 | client -> state != S_REBINDING) { | |
2d1b06e0 | 1016 | #if defined (DEBUG) |
8ae2d595 | 1017 | log_debug ("DHCPACK in wrong state."); |
2d1b06e0 | 1018 | #endif |
cc26de46 | 1019 | return; |
469cf3a4 TL |
1020 | } |
1021 | ||
8ae2d595 | 1022 | log_info ("DHCPACK from %s", piaddr (packet -> client_addr)); |
34fdad6c | 1023 | |
6ceb9118 | 1024 | lease = packet_to_lease (packet, client); |
cc26de46 | 1025 | if (!lease) { |
8ae2d595 | 1026 | log_info ("packet_to_lease failed."); |
cc26de46 | 1027 | return; |
469cf3a4 TL |
1028 | } |
1029 | ||
02d9e453 | 1030 | client -> new = lease; |
cc26de46 TL |
1031 | |
1032 | /* Stop resending DHCPREQUEST. */ | |
02d9e453 | 1033 | cancel_timeout (send_request, client); |
cc26de46 TL |
1034 | |
1035 | /* Figure out the lease time. */ | |
230e73e4 | 1036 | oc = lookup_option (&dhcp_universe, client -> new -> options, |
02a015fb TL |
1037 | DHO_DHCP_LEASE_TIME); |
1038 | memset (&ds, 0, sizeof ds); | |
1039 | if (oc && | |
66e9cecf | 1040 | evaluate_option_cache (&ds, packet, (struct lease *)0, client, |
0852a27f | 1041 | packet -> options, client -> new -> options, |
cf78bf20 | 1042 | &global_scope, oc, MDL)) { |
02a015fb | 1043 | if (ds.len > 3) |
02d9e453 | 1044 | client -> new -> expiry = getULong (ds.data); |
02a015fb | 1045 | else |
02d9e453 | 1046 | client -> new -> expiry = 0; |
cf78bf20 | 1047 | data_string_forget (&ds, MDL); |
02a015fb | 1048 | } else |
02d9e453 | 1049 | client -> new -> expiry = 0; |
02a015fb | 1050 | |
02d9e453 | 1051 | if (!client -> new -> expiry) { |
8ae2d595 | 1052 | log_error ("no expiry time on offered lease."); |
02a015fb | 1053 | /* XXX this is going to be bad - if this _does_ |
3dbe2246 | 1054 | XXX happen, we should probably dynamically |
02a015fb TL |
1055 | XXX disqualify the DHCP server that gave us the |
1056 | XXX bad packet from future selections and | |
1057 | XXX then go back into the init state. */ | |
02d9e453 | 1058 | state_init (client); |
02a015fb TL |
1059 | return; |
1060 | } | |
cc26de46 | 1061 | |
62d0cb47 TL |
1062 | /* A number that looks negative here is really just very large, |
1063 | because the lease expiry offset is unsigned. */ | |
1064 | if (client -> new -> expiry < 0) | |
1065 | client -> new -> expiry = TIME_MAX; | |
02a015fb | 1066 | /* Take the server-provided renewal time if there is one. */ |
230e73e4 | 1067 | oc = lookup_option (&dhcp_universe, client -> new -> options, |
02a015fb TL |
1068 | DHO_DHCP_RENEWAL_TIME); |
1069 | if (oc && | |
66e9cecf | 1070 | evaluate_option_cache (&ds, packet, (struct lease *)0, client, |
0852a27f | 1071 | packet -> options, client -> new -> options, |
cf78bf20 | 1072 | &global_scope, oc, MDL)) { |
02a015fb | 1073 | if (ds.len > 3) |
02d9e453 | 1074 | client -> new -> renewal = getULong (ds.data); |
02a015fb | 1075 | else |
02d9e453 | 1076 | client -> new -> renewal = 0; |
cf78bf20 | 1077 | data_string_forget (&ds, MDL); |
02a015fb | 1078 | } else |
02d9e453 | 1079 | client -> new -> renewal = 0; |
02a015fb TL |
1080 | |
1081 | /* If it wasn't specified by the server, calculate it. */ | |
02d9e453 | 1082 | if (!client -> new -> renewal) |
98311e4b DH |
1083 | client -> new -> renewal = client -> new -> expiry / 2 + 1; |
1084 | ||
1085 | if (client -> new -> renewal <= 0) | |
1086 | client -> new -> renewal = TIME_MAX; | |
cc26de46 | 1087 | |
11373fb6 | 1088 | /* Now introduce some randomness to the renewal time: */ |
88cd8aca DH |
1089 | if (client->new->renewal <= ((TIME_MAX / 3) - 3)) |
1090 | client->new->renewal = (((client->new->renewal * 3) + 3) / 4) + | |
1091 | (((random() % client->new->renewal) + 3) / 4); | |
11373fb6 | 1092 | |
cc26de46 | 1093 | /* Same deal with the rebind time. */ |
230e73e4 | 1094 | oc = lookup_option (&dhcp_universe, client -> new -> options, |
02a015fb TL |
1095 | DHO_DHCP_REBINDING_TIME); |
1096 | if (oc && | |
66e9cecf | 1097 | evaluate_option_cache (&ds, packet, (struct lease *)0, client, |
0852a27f | 1098 | packet -> options, client -> new -> options, |
cf78bf20 | 1099 | &global_scope, oc, MDL)) { |
02a015fb | 1100 | if (ds.len > 3) |
02d9e453 | 1101 | client -> new -> rebind = getULong (ds.data); |
02a015fb | 1102 | else |
02d9e453 | 1103 | client -> new -> rebind = 0; |
cf78bf20 | 1104 | data_string_forget (&ds, MDL); |
02a015fb | 1105 | } else |
02d9e453 | 1106 | client -> new -> rebind = 0; |
02a015fb | 1107 | |
98311e4b DH |
1108 | if (client -> new -> rebind <= 0) { |
1109 | if (client -> new -> expiry <= TIME_MAX / 7) | |
1110 | client -> new -> rebind = | |
1111 | client -> new -> expiry * 7 / 8; | |
1112 | else | |
1113 | client -> new -> rebind = | |
1114 | client -> new -> expiry / 8 * 7; | |
1115 | } | |
11373fb6 TL |
1116 | |
1117 | /* Make sure our randomness didn't run the renewal time past the | |
1118 | rebind time. */ | |
98311e4b DH |
1119 | if (client -> new -> renewal > client -> new -> rebind) { |
1120 | if (client -> new -> rebind <= TIME_MAX / 3) | |
1121 | client -> new -> renewal = | |
1122 | client -> new -> rebind * 3 / 4; | |
1123 | else | |
1124 | client -> new -> renewal = | |
1125 | client -> new -> rebind / 4 * 3; | |
1126 | } | |
cc26de46 | 1127 | |
02d9e453 | 1128 | client -> new -> expiry += cur_time; |
62d0cb47 TL |
1129 | /* Lease lengths can never be negative. */ |
1130 | if (client -> new -> expiry < cur_time) | |
1131 | client -> new -> expiry = TIME_MAX; | |
02d9e453 | 1132 | client -> new -> renewal += cur_time; |
62d0cb47 TL |
1133 | if (client -> new -> renewal < cur_time) |
1134 | client -> new -> renewal = TIME_MAX; | |
02d9e453 | 1135 | client -> new -> rebind += cur_time; |
62d0cb47 TL |
1136 | if (client -> new -> rebind < cur_time) |
1137 | client -> new -> rebind = TIME_MAX; | |
cc26de46 | 1138 | |
02d9e453 | 1139 | bind_lease (client); |
66f973e4 TL |
1140 | } |
1141 | ||
02d9e453 TL |
1142 | void bind_lease (client) |
1143 | struct client_state *client; | |
66f973e4 | 1144 | { |
be62cf06 FD |
1145 | struct timeval tv; |
1146 | ||
9bdb9271 | 1147 | /* Remember the medium. */ |
02d9e453 | 1148 | client -> new -> medium = client -> medium; |
9bdb9271 | 1149 | |
cc26de46 | 1150 | /* Run the client script with the new parameters. */ |
02d9e453 | 1151 | script_init (client, (client -> state == S_REQUESTING |
cc26de46 | 1152 | ? "BOUND" |
02d9e453 | 1153 | : (client -> state == S_RENEWING |
cc26de46 | 1154 | ? "RENEW" |
02d9e453 | 1155 | : (client -> state == S_REBOOTING |
77967680 | 1156 | ? "REBOOT" : "REBIND"))), |
02d9e453 TL |
1157 | client -> new -> medium); |
1158 | if (client -> active && client -> state != S_REBOOTING) | |
1159 | script_write_params (client, "old_", client -> active); | |
1160 | script_write_params (client, "new_", client -> new); | |
1161 | if (client -> alias) | |
1162 | script_write_params (client, "alias_", client -> alias); | |
e16f4c3e TL |
1163 | |
1164 | /* If the BOUND/RENEW code detects another machine using the | |
1165 | offered address, it exits nonzero. We need to send a | |
1166 | DHCPDECLINE and toss the lease. */ | |
1167 | if (script_go (client)) { | |
1168 | make_decline (client, client -> new); | |
1169 | send_decline (client); | |
1170 | destroy_client_lease (client -> new); | |
7d3bc735 | 1171 | client -> new = (struct client_lease *)0; |
e16f4c3e TL |
1172 | state_init (client); |
1173 | return; | |
1174 | } | |
1175 | ||
1176 | /* Write out the new lease. */ | |
007e3ee4 | 1177 | write_client_lease (client, client -> new, 0, 0); |
cc26de46 TL |
1178 | |
1179 | /* Replace the old active lease with the new one. */ | |
02d9e453 TL |
1180 | if (client -> active) |
1181 | destroy_client_lease (client -> active); | |
1182 | client -> active = client -> new; | |
1183 | client -> new = (struct client_lease *)0; | |
cc26de46 TL |
1184 | |
1185 | /* Set up a timeout to start the renewal process. */ | |
be62cf06 FD |
1186 | tv . tv_sec = client -> active -> renewal; |
1187 | tv . tv_usec = 0; | |
1188 | add_timeout (&tv, state_bound, client, 0, 0); | |
cc26de46 | 1189 | |
ab58ff49 | 1190 | log_info ("bound to %s -- renewal in %ld seconds.", |
02d9e453 | 1191 | piaddr (client -> active -> address), |
ab58ff49 | 1192 | (long)(client -> active -> renewal - cur_time)); |
02d9e453 | 1193 | client -> state = S_BOUND; |
b00d3884 TL |
1194 | reinitialize_interfaces (); |
1195 | go_daemon (); | |
98bf1607 | 1196 | #if defined (NSUPDATE) |
98bd7ca0 | 1197 | if (client->config->do_forward_update) |
98bf1607 SR |
1198 | dhclient_schedule_updates(client, &client->active->address, 1); |
1199 | #endif | |
3dbe2246 | 1200 | } |
469cf3a4 | 1201 | |
cc26de46 TL |
1202 | /* state_bound is called when we've successfully bound to a particular |
1203 | lease, but the renewal time on that lease has expired. We are | |
1204 | expected to unicast a DHCPREQUEST to the server that gave us our | |
1205 | original lease. */ | |
469cf3a4 | 1206 | |
02d9e453 TL |
1207 | void state_bound (cpp) |
1208 | void *cpp; | |
469cf3a4 | 1209 | { |
02d9e453 | 1210 | struct client_state *client = cpp; |
02a015fb TL |
1211 | struct option_cache *oc; |
1212 | struct data_string ds; | |
84c4adde | 1213 | |
cc26de46 | 1214 | ASSERT_STATE(state, S_BOUND); |
469cf3a4 | 1215 | |
cc26de46 | 1216 | /* T1 has expired. */ |
02d9e453 TL |
1217 | make_request (client, client -> active); |
1218 | client -> xid = client -> packet.xid; | |
469cf3a4 | 1219 | |
02a015fb | 1220 | memset (&ds, 0, sizeof ds); |
230e73e4 | 1221 | oc = lookup_option (&dhcp_universe, client -> active -> options, |
02a015fb TL |
1222 | DHO_DHCP_SERVER_IDENTIFIER); |
1223 | if (oc && | |
0852a27f | 1224 | evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0, |
66e9cecf | 1225 | client, (struct option_state *)0, |
da38df14 | 1226 | client -> active -> options, |
cf78bf20 | 1227 | &global_scope, oc, MDL)) { |
02a015fb | 1228 | if (ds.len > 3) { |
02d9e453 TL |
1229 | memcpy (client -> destination.iabuf, ds.data, 4); |
1230 | client -> destination.len = 4; | |
02a015fb | 1231 | } else |
02d9e453 | 1232 | client -> destination = iaddr_broadcast; |
98311e4b DH |
1233 | |
1234 | data_string_forget (&ds, MDL); | |
cc26de46 | 1235 | } else |
02d9e453 | 1236 | client -> destination = iaddr_broadcast; |
469cf3a4 | 1237 | |
02d9e453 TL |
1238 | client -> first_sending = cur_time; |
1239 | client -> interval = client -> config -> initial_interval; | |
1240 | client -> state = S_RENEWING; | |
cc26de46 TL |
1241 | |
1242 | /* Send the first packet immediately. */ | |
02d9e453 | 1243 | send_request (client); |
3dbe2246 | 1244 | } |
469cf3a4 | 1245 | |
d758ad8c TL |
1246 | /* state_stop is called when we've been told to shut down. We unconfigure |
1247 | the interfaces, and then stop operating until told otherwise. */ | |
1248 | ||
1249 | void state_stop (cpp) | |
1250 | void *cpp; | |
1251 | { | |
1252 | struct client_state *client = cpp; | |
d758ad8c TL |
1253 | |
1254 | /* Cancel all timeouts. */ | |
af5fa176 EH |
1255 | cancel_timeout(state_selecting, client); |
1256 | cancel_timeout(send_discover, client); | |
1257 | cancel_timeout(send_request, client); | |
1258 | cancel_timeout(state_bound, client); | |
d758ad8c TL |
1259 | |
1260 | /* If we have an address, unconfigure it. */ | |
af5fa176 EH |
1261 | if (client->active) { |
1262 | script_init(client, "STOP", client->active->medium); | |
1263 | script_write_params(client, "old_", client->active); | |
1264 | if (client->alias) | |
1265 | script_write_params(client, "alias_", client->alias); | |
1266 | script_go(client); | |
d758ad8c | 1267 | } |
3dbe2246 | 1268 | } |
d758ad8c | 1269 | |
c1503c57 TL |
1270 | int commit_leases () |
1271 | { | |
1272 | return 0; | |
1273 | } | |
1274 | ||
1275 | int write_lease (lease) | |
1276 | struct lease *lease; | |
1277 | { | |
1278 | return 0; | |
95821729 TL |
1279 | } |
1280 | ||
628beb0e TL |
1281 | int write_host (host) |
1282 | struct host_decl *host; | |
1283 | { | |
1284 | return 0; | |
1285 | } | |
1286 | ||
7d7a35fa TL |
1287 | void db_startup (testp) |
1288 | int testp; | |
c1503c57 TL |
1289 | { |
1290 | } | |
1291 | ||
1292 | void bootp (packet) | |
1293 | struct packet *packet; | |
1294 | { | |
febbd402 DH |
1295 | struct iaddrmatchlist *ap; |
1296 | char addrbuf[4*16]; | |
1297 | char maskbuf[4*16]; | |
fa2b3e59 TL |
1298 | |
1299 | if (packet -> raw -> op != BOOTREPLY) | |
1300 | return; | |
1301 | ||
1302 | /* If there's a reject list, make sure this packet's sender isn't | |
1303 | on it. */ | |
1304 | for (ap = packet -> interface -> client -> config -> reject_list; | |
1305 | ap; ap = ap -> next) { | |
febbd402 DH |
1306 | if (addr_match(&packet->client_addr, &ap->match)) { |
1307 | ||
1308 | /* piaddr() returns its result in a static | |
1309 | buffer sized 4*16 (see common/inet.c). */ | |
1310 | ||
1311 | strcpy(addrbuf, piaddr(ap->match.addr)); | |
1312 | strcpy(maskbuf, piaddr(ap->match.mask)); | |
1313 | ||
1314 | log_info("BOOTREPLY from %s rejected by rule %s " | |
1315 | "mask %s.", piaddr(packet->client_addr), | |
1316 | addrbuf, maskbuf); | |
fa2b3e59 TL |
1317 | return; |
1318 | } | |
1319 | } | |
3dbe2246 | 1320 | |
fa2b3e59 | 1321 | dhcpoffer (packet); |
66f973e4 | 1322 | |
c1503c57 | 1323 | } |
95821729 | 1324 | |
c1503c57 TL |
1325 | void dhcp (packet) |
1326 | struct packet *packet; | |
95821729 | 1327 | { |
febbd402 | 1328 | struct iaddrmatchlist *ap; |
ea65d407 | 1329 | void (*handler) PROTO ((struct packet *)); |
b1b7b521 | 1330 | const char *type; |
febbd402 DH |
1331 | char addrbuf[4*16]; |
1332 | char maskbuf[4*16]; | |
fa2b3e59 | 1333 | |
c1503c57 TL |
1334 | switch (packet -> packet_type) { |
1335 | case DHCPOFFER: | |
fa2b3e59 TL |
1336 | handler = dhcpoffer; |
1337 | type = "DHCPOFFER"; | |
95821729 | 1338 | break; |
c1503c57 TL |
1339 | |
1340 | case DHCPNAK: | |
fa2b3e59 TL |
1341 | handler = dhcpnak; |
1342 | type = "DHCPNACK"; | |
c1503c57 TL |
1343 | break; |
1344 | ||
1345 | case DHCPACK: | |
fa2b3e59 TL |
1346 | handler = dhcpack; |
1347 | type = "DHCPACK"; | |
c1503c57 TL |
1348 | break; |
1349 | ||
95821729 | 1350 | default: |
fa2b3e59 TL |
1351 | return; |
1352 | } | |
1353 | ||
1354 | /* If there's a reject list, make sure this packet's sender isn't | |
1355 | on it. */ | |
1356 | for (ap = packet -> interface -> client -> config -> reject_list; | |
1357 | ap; ap = ap -> next) { | |
febbd402 DH |
1358 | if (addr_match(&packet->client_addr, &ap->match)) { |
1359 | ||
1360 | /* piaddr() returns its result in a static | |
1361 | buffer sized 4*16 (see common/inet.c). */ | |
1362 | ||
1363 | strcpy(addrbuf, piaddr(ap->match.addr)); | |
1364 | strcpy(maskbuf, piaddr(ap->match.mask)); | |
1365 | ||
1366 | log_info("%s from %s rejected by rule %s mask %s.", | |
1367 | type, piaddr(packet->client_addr), | |
1368 | addrbuf, maskbuf); | |
fa2b3e59 TL |
1369 | return; |
1370 | } | |
95821729 | 1371 | } |
fa2b3e59 | 1372 | (*handler) (packet); |
95821729 TL |
1373 | } |
1374 | ||
fe5b0fdd | 1375 | #ifdef DHCPv6 |
3dbe2246 | 1376 | void |
98bd7ca0 DH |
1377 | dhcpv6(struct packet *packet) { |
1378 | struct iaddrmatchlist *ap; | |
1379 | struct client_state *client; | |
1380 | char addrbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")]; | |
1381 | ||
1382 | /* Silently drop bogus messages. */ | |
1383 | if (packet->dhcpv6_msg_type >= dhcpv6_type_name_max) | |
1384 | return; | |
1385 | ||
1386 | /* Discard, with log, packets from quenched sources. */ | |
1387 | for (ap = packet->interface->client->config->reject_list ; | |
1388 | ap ; ap = ap->next) { | |
1389 | if (addr_match(&packet->client_addr, &ap->match)) { | |
1390 | strcpy(addrbuf, piaddr(packet->client_addr)); | |
1391 | log_info("%s from %s rejected by rule %s", | |
1392 | dhcpv6_type_names[packet->dhcpv6_msg_type], | |
1393 | addrbuf, | |
1394 | piaddrmask(&ap->match.addr, &ap->match.mask)); | |
1395 | return; | |
1396 | } | |
1397 | } | |
1398 | ||
1399 | /* Screen out nonsensical messages. */ | |
1400 | switch(packet->dhcpv6_msg_type) { | |
1401 | case DHCPV6_ADVERTISE: | |
98bd7ca0 | 1402 | case DHCPV6_RECONFIGURE: |
3dbe2246 FD |
1403 | if (stateless) |
1404 | return; | |
1405 | /* Falls through */ | |
1406 | case DHCPV6_REPLY: | |
98bd7ca0 DH |
1407 | log_info("RCV: %s message on %s from %s.", |
1408 | dhcpv6_type_names[packet->dhcpv6_msg_type], | |
1409 | packet->interface->name, piaddr(packet->client_addr)); | |
1410 | break; | |
1411 | ||
1412 | default: | |
1413 | return; | |
1414 | } | |
1415 | ||
1416 | /* Find a client state that matches the incoming XID. */ | |
1417 | for (client = packet->interface->client ; client ; | |
1418 | client = client->next) { | |
1419 | if (memcmp(&client->dhcpv6_transaction_id, | |
1420 | packet->dhcpv6_transaction_id, 3) == 0) { | |
1421 | client->v6_handler(packet, client); | |
1422 | return; | |
1423 | } | |
1424 | } | |
1425 | ||
20ae1aff | 1426 | /* XXX: temporary log for debugging */ |
98bd7ca0 DH |
1427 | log_info("Packet received, but nothing done with it."); |
1428 | } | |
fe5b0fdd | 1429 | #endif /* DHCPv6 */ |
98bd7ca0 | 1430 | |
c1503c57 TL |
1431 | void dhcpoffer (packet) |
1432 | struct packet *packet; | |
95821729 | 1433 | { |
cc26de46 | 1434 | struct interface_info *ip = packet -> interface; |
02d9e453 | 1435 | struct client_state *client; |
b00d3884 | 1436 | struct client_lease *lease, *lp; |
0c20eab3 | 1437 | struct option **req; |
cc26de46 | 1438 | int i; |
e16f4c3e | 1439 | int stop_selecting; |
b1b7b521 | 1440 | const char *name = packet -> packet_type ? "DHCPOFFER" : "BOOTREPLY"; |
d758ad8c | 1441 | char obuf [1024]; |
be62cf06 | 1442 | struct timeval tv; |
3dbe2246 | 1443 | |
cc26de46 | 1444 | #ifdef DEBUG_PACKET |
c1503c57 | 1445 | dump_packet (packet); |
3dbe2246 | 1446 | #endif |
cc26de46 | 1447 | |
02d9e453 TL |
1448 | /* Find a client state that matches the xid... */ |
1449 | for (client = ip -> client; client; client = client -> next) | |
1450 | if (client -> xid == packet -> raw -> xid) | |
1451 | break; | |
1452 | ||
cc26de46 TL |
1453 | /* If we're not receptive to an offer right now, or if the offer |
1454 | has an unrecognizable transaction id, then just drop it. */ | |
02d9e453 | 1455 | if (!client || |
73530742 | 1456 | client -> state != S_SELECTING || |
31730f17 | 1457 | (packet -> interface -> hw_address.hlen - 1 != |
fcaec4ef | 1458 | packet -> raw -> hlen) || |
31730f17 | 1459 | (memcmp (&packet -> interface -> hw_address.hbuf [1], |
fcaec4ef | 1460 | packet -> raw -> chaddr, packet -> raw -> hlen))) { |
2d1b06e0 | 1461 | #if defined (DEBUG) |
8ae2d595 | 1462 | log_debug ("%s in wrong transaction.", name); |
2d1b06e0 | 1463 | #endif |
cc26de46 TL |
1464 | return; |
1465 | } | |
1466 | ||
d758ad8c | 1467 | sprintf (obuf, "%s from %s", name, piaddr (packet -> client_addr)); |
34fdad6c | 1468 | |
b8cf055d | 1469 | |
0c20eab3 DH |
1470 | /* If this lease doesn't supply the minimum required DHCPv4 parameters, |
1471 | * ignore it. | |
1472 | */ | |
1473 | req = client->config->required_options; | |
1474 | if (req != NULL) { | |
1475 | for (i = 0 ; req[i] != NULL ; i++) { | |
1476 | if ((req[i]->universe == &dhcp_universe) && | |
1477 | !lookup_option(&dhcp_universe, packet->options, | |
1478 | req[i]->code)) { | |
1479 | struct option *option = NULL; | |
1480 | unsigned code = req[i]->code; | |
1481 | ||
1482 | option_code_hash_lookup(&option, | |
1483 | dhcp_universe.code_hash, | |
1484 | &code, 0, MDL); | |
1485 | ||
1486 | if (option) | |
1487 | log_info("%s: no %s option.", obuf, | |
1488 | option->name); | |
1489 | else | |
1490 | log_info("%s: no unknown-%u option.", | |
1491 | obuf, code); | |
f7fdb216 | 1492 | |
0c20eab3 | 1493 | option_dereference(&option, MDL); |
f7fdb216 | 1494 | |
0c20eab3 DH |
1495 | return; |
1496 | } | |
cc26de46 TL |
1497 | } |
1498 | } | |
1499 | ||
1500 | /* If we've already seen this lease, don't record it again. */ | |
02d9e453 | 1501 | for (lease = client -> offered_leases; lease; lease = lease -> next) { |
cc26de46 TL |
1502 | if (lease -> address.len == sizeof packet -> raw -> yiaddr && |
1503 | !memcmp (lease -> address.iabuf, | |
1504 | &packet -> raw -> yiaddr, lease -> address.len)) { | |
d758ad8c | 1505 | log_debug ("%s: already seen.", obuf); |
cc26de46 TL |
1506 | return; |
1507 | } | |
1508 | } | |
1509 | ||
6ceb9118 | 1510 | lease = packet_to_lease (packet, client); |
cc26de46 | 1511 | if (!lease) { |
d758ad8c | 1512 | log_info ("%s: packet_to_lease failed.", obuf); |
cc26de46 TL |
1513 | return; |
1514 | } | |
1515 | ||
66f973e4 TL |
1516 | /* If this lease was acquired through a BOOTREPLY, record that |
1517 | fact. */ | |
02a015fb | 1518 | if (!packet -> options_valid || !packet -> packet_type) |
66f973e4 TL |
1519 | lease -> is_bootp = 1; |
1520 | ||
9bdb9271 | 1521 | /* Record the medium under which this lease was offered. */ |
02d9e453 | 1522 | lease -> medium = client -> medium; |
9bdb9271 | 1523 | |
b00d3884 | 1524 | /* Figure out when we're supposed to stop selecting. */ |
02d9e453 TL |
1525 | stop_selecting = (client -> first_sending + |
1526 | client -> config -> select_interval); | |
b00d3884 TL |
1527 | |
1528 | /* If this is the lease we asked for, put it at the head of the | |
1529 | list, and don't mess with the arp request timeout. */ | |
02d9e453 | 1530 | if (lease -> address.len == client -> requested_address.len && |
b00d3884 | 1531 | !memcmp (lease -> address.iabuf, |
02d9e453 TL |
1532 | client -> requested_address.iabuf, |
1533 | client -> requested_address.len)) { | |
1534 | lease -> next = client -> offered_leases; | |
1535 | client -> offered_leases = lease; | |
b00d3884 | 1536 | } else { |
b00d3884 TL |
1537 | /* Put the lease at the end of the list. */ |
1538 | lease -> next = (struct client_lease *)0; | |
02d9e453 TL |
1539 | if (!client -> offered_leases) |
1540 | client -> offered_leases = lease; | |
b00d3884 | 1541 | else { |
02d9e453 | 1542 | for (lp = client -> offered_leases; lp -> next; |
b00d3884 TL |
1543 | lp = lp -> next) |
1544 | ; | |
1545 | lp -> next = lease; | |
1546 | } | |
1547 | } | |
1548 | ||
cc26de46 TL |
1549 | /* If the selecting interval has expired, go immediately to |
1550 | state_selecting(). Otherwise, time out into | |
1551 | state_selecting at the select interval. */ | |
b00d3884 | 1552 | if (stop_selecting <= 0) |
d758ad8c | 1553 | state_selecting (client); |
84c4adde | 1554 | else { |
be62cf06 FD |
1555 | tv . tv_sec = stop_selecting; |
1556 | tv . tv_usec = 0; | |
1557 | add_timeout (&tv, state_selecting, client, 0, 0); | |
02d9e453 | 1558 | cancel_timeout (send_discover, client); |
84c4adde | 1559 | } |
d758ad8c | 1560 | log_info ("%s", obuf); |
95821729 | 1561 | } |
e581d615 | 1562 | |
cc26de46 TL |
1563 | /* Allocate a client_lease structure and initialize it from the parameters |
1564 | in the specified packet. */ | |
1565 | ||
6ceb9118 | 1566 | struct client_lease *packet_to_lease (packet, client) |
c1503c57 | 1567 | struct packet *packet; |
6ceb9118 | 1568 | struct client_state *client; |
e581d615 | 1569 | { |
cc26de46 | 1570 | struct client_lease *lease; |
b5f904a9 | 1571 | unsigned i; |
02a015fb | 1572 | struct option_cache *oc; |
f7fdb216 | 1573 | struct option *option = NULL; |
02a015fb | 1574 | struct data_string data; |
cc26de46 | 1575 | |
cf78bf20 | 1576 | lease = (struct client_lease *)new_client_lease (MDL); |
cc26de46 TL |
1577 | |
1578 | if (!lease) { | |
89c425dd | 1579 | log_error ("packet_to_lease: no memory to record lease.\n"); |
cc26de46 TL |
1580 | return (struct client_lease *)0; |
1581 | } | |
1582 | ||
1583 | memset (lease, 0, sizeof *lease); | |
1584 | ||
1585 | /* Copy the lease options. */ | |
cf78bf20 | 1586 | option_state_reference (&lease -> options, packet -> options, MDL); |
cc26de46 TL |
1587 | |
1588 | lease -> address.len = sizeof (packet -> raw -> yiaddr); | |
1589 | memcpy (lease -> address.iabuf, &packet -> raw -> yiaddr, | |
1590 | lease -> address.len); | |
1591 | ||
98311e4b DH |
1592 | memset (&data, 0, sizeof data); |
1593 | ||
b5f904a9 TL |
1594 | if (client -> config -> vendor_space_name) { |
1595 | i = DHO_VENDOR_ENCAPSULATED_OPTIONS; | |
1596 | ||
1597 | /* See if there was a vendor encapsulation option. */ | |
1598 | oc = lookup_option (&dhcp_universe, lease -> options, i); | |
b5f904a9 TL |
1599 | if (oc && |
1600 | client -> config -> vendor_space_name && | |
66e9cecf TL |
1601 | evaluate_option_cache (&data, packet, |
1602 | (struct lease *)0, client, | |
b5f904a9 TL |
1603 | packet -> options, lease -> options, |
1604 | &global_scope, oc, MDL)) { | |
1605 | if (data.len) { | |
f7fdb216 DH |
1606 | if (!option_code_hash_lookup(&option, |
1607 | dhcp_universe.code_hash, | |
1608 | &i, 0, MDL)) | |
1609 | log_fatal("Unable to find VENDOR " | |
1610 | "option (%s:%d).", MDL); | |
b5f904a9 | 1611 | parse_encapsulated_suboptions |
f7fdb216 | 1612 | (packet -> options, option, |
b5f904a9 TL |
1613 | data.data, data.len, &dhcp_universe, |
1614 | client -> config -> vendor_space_name | |
1615 | ); | |
f7fdb216 DH |
1616 | |
1617 | option_dereference(&option, MDL); | |
b5f904a9 TL |
1618 | } |
1619 | data_string_forget (&data, MDL); | |
1620 | } | |
1621 | } else | |
1622 | i = 0; | |
1623 | ||
02a015fb | 1624 | /* Figure out the overload flag. */ |
230e73e4 | 1625 | oc = lookup_option (&dhcp_universe, lease -> options, |
02a015fb | 1626 | DHO_DHCP_OPTION_OVERLOAD); |
02a015fb | 1627 | if (oc && |
66e9cecf | 1628 | evaluate_option_cache (&data, packet, (struct lease *)0, client, |
0852a27f | 1629 | packet -> options, lease -> options, |
cf78bf20 | 1630 | &global_scope, oc, MDL)) { |
02a015fb TL |
1631 | if (data.len > 0) |
1632 | i = data.data [0]; | |
1633 | else | |
1634 | i = 0; | |
cf78bf20 | 1635 | data_string_forget (&data, MDL); |
02a015fb TL |
1636 | } else |
1637 | i = 0; | |
1638 | ||
cc26de46 | 1639 | /* If the server name was filled out, copy it. */ |
02a015fb | 1640 | if (!(i & 2) && packet -> raw -> sname [0]) { |
b1b7b521 | 1641 | unsigned len; |
cc26de46 | 1642 | /* Don't count on the NUL terminator. */ |
e2624b82 | 1643 | for (len = 0; len < DHCP_SNAME_LEN; len++) |
cc26de46 TL |
1644 | if (!packet -> raw -> sname [len]) |
1645 | break; | |
cf78bf20 | 1646 | lease -> server_name = dmalloc (len + 1, MDL); |
cc26de46 | 1647 | if (!lease -> server_name) { |
98311e4b | 1648 | log_error ("dhcpoffer: no memory for server name.\n"); |
02a015fb | 1649 | destroy_client_lease (lease); |
cc26de46 TL |
1650 | return (struct client_lease *)0; |
1651 | } else { | |
1652 | memcpy (lease -> server_name, | |
1653 | packet -> raw -> sname, len); | |
1654 | lease -> server_name [len] = 0; | |
1655 | } | |
1656 | } | |
1657 | ||
1658 | /* Ditto for the filename. */ | |
0852a27f | 1659 | if (!(i & 1) && packet -> raw -> file [0]) { |
b1b7b521 | 1660 | unsigned len; |
cc26de46 | 1661 | /* Don't count on the NUL terminator. */ |
e2624b82 | 1662 | for (len = 0; len < DHCP_FILE_LEN; len++) |
cc26de46 TL |
1663 | if (!packet -> raw -> file [len]) |
1664 | break; | |
cf78bf20 | 1665 | lease -> filename = dmalloc (len + 1, MDL); |
cc26de46 | 1666 | if (!lease -> filename) { |
8ae2d595 | 1667 | log_error ("dhcpoffer: no memory for filename.\n"); |
02a015fb | 1668 | destroy_client_lease (lease); |
cc26de46 TL |
1669 | return (struct client_lease *)0; |
1670 | } else { | |
1671 | memcpy (lease -> filename, | |
1672 | packet -> raw -> file, len); | |
1673 | lease -> filename [len] = 0; | |
1674 | } | |
1675 | } | |
89c425dd TL |
1676 | |
1677 | execute_statements_in_scope ((struct binding_value **)0, | |
1678 | (struct packet *)packet, | |
66e9cecf TL |
1679 | (struct lease *)0, client, |
1680 | lease -> options, lease -> options, | |
1681 | &global_scope, | |
89c425dd TL |
1682 | client -> config -> on_receipt, |
1683 | (struct group *)0); | |
1684 | ||
cc26de46 | 1685 | return lease; |
3dbe2246 | 1686 | } |
e581d615 | 1687 | |
c1503c57 TL |
1688 | void dhcpnak (packet) |
1689 | struct packet *packet; | |
1690 | { | |
deff2d59 | 1691 | struct interface_info *ip = packet -> interface; |
02d9e453 TL |
1692 | struct client_state *client; |
1693 | ||
1694 | /* Find a client state that matches the xid... */ | |
1695 | for (client = ip -> client; client; client = client -> next) | |
1696 | if (client -> xid == packet -> raw -> xid) | |
1697 | break; | |
deff2d59 | 1698 | |
deff2d59 TL |
1699 | /* If we're not receptive to an offer right now, or if the offer |
1700 | has an unrecognizable transaction id, then just drop it. */ | |
02d9e453 | 1701 | if (!client || |
31730f17 | 1702 | (packet -> interface -> hw_address.hlen - 1 != |
fcaec4ef | 1703 | packet -> raw -> hlen) || |
31730f17 | 1704 | (memcmp (&packet -> interface -> hw_address.hbuf [1], |
fcaec4ef | 1705 | packet -> raw -> chaddr, packet -> raw -> hlen))) { |
2d1b06e0 | 1706 | #if defined (DEBUG) |
8ae2d595 | 1707 | log_debug ("DHCPNAK in wrong transaction."); |
2d1b06e0 | 1708 | #endif |
deff2d59 TL |
1709 | return; |
1710 | } | |
1711 | ||
02d9e453 TL |
1712 | if (client -> state != S_REBOOTING && |
1713 | client -> state != S_REQUESTING && | |
1714 | client -> state != S_RENEWING && | |
1715 | client -> state != S_REBINDING) { | |
2d1b06e0 | 1716 | #if defined (DEBUG) |
8ae2d595 | 1717 | log_debug ("DHCPNAK in wrong state."); |
2d1b06e0 | 1718 | #endif |
deff2d59 TL |
1719 | return; |
1720 | } | |
1721 | ||
8ae2d595 | 1722 | log_info ("DHCPNAK from %s", piaddr (packet -> client_addr)); |
34fdad6c | 1723 | |
02d9e453 | 1724 | if (!client -> active) { |
2d1b06e0 | 1725 | #if defined (DEBUG) |
8ae2d595 | 1726 | log_info ("DHCPNAK with no active lease.\n"); |
2d1b06e0 | 1727 | #endif |
deff2d59 TL |
1728 | return; |
1729 | } | |
1730 | ||
6cbc6629 DH |
1731 | /* If we get a DHCPNAK, we use the EXPIRE dhclient-script state |
1732 | * to indicate that we want all old bindings to be removed. (It | |
1733 | * is possible that we may get a NAK while in the RENEW state, | |
1734 | * so we might have bindings active at that time) | |
1735 | */ | |
1736 | script_init(client, "EXPIRE", NULL); | |
1737 | script_write_params(client, "old_", client->active); | |
1738 | if (client->alias) | |
1739 | script_write_params(client, "alias_", client->alias); | |
1740 | script_go(client); | |
1741 | ||
02d9e453 TL |
1742 | destroy_client_lease (client -> active); |
1743 | client -> active = (struct client_lease *)0; | |
deff2d59 TL |
1744 | |
1745 | /* Stop sending DHCPREQUEST packets... */ | |
02d9e453 | 1746 | cancel_timeout (send_request, client); |
deff2d59 | 1747 | |
0a73b7b6 SK |
1748 | /* On some scripts, 'EXPIRE' causes the interface to be ifconfig'd |
1749 | * down (this expunges any routes and arp cache). This makes the | |
1750 | * interface unusable by state_init(), which we call next. So, we | |
1751 | * need to 'PREINIT' the interface to bring it back up. | |
1752 | */ | |
1753 | script_init(client, "PREINIT", NULL); | |
1754 | if (client->alias) | |
1755 | script_write_params(client, "alias_", client->alias); | |
1756 | script_go(client); | |
1757 | ||
02d9e453 TL |
1758 | client -> state = S_INIT; |
1759 | state_init (client); | |
e581d615 TL |
1760 | } |
1761 | ||
cc26de46 | 1762 | /* Send out a DHCPDISCOVER packet, and set a timeout to send out another |
66f973e4 TL |
1763 | one after the right interval has expired. If we don't get an offer by |
1764 | the time we reach the panic interval, call the panic function. */ | |
469cf3a4 | 1765 | |
02d9e453 TL |
1766 | void send_discover (cpp) |
1767 | void *cpp; | |
469cf3a4 | 1768 | { |
02d9e453 | 1769 | struct client_state *client = cpp; |
84c4adde | 1770 | |
cc26de46 TL |
1771 | int result; |
1772 | int interval; | |
9bdb9271 | 1773 | int increase = 1; |
be62cf06 | 1774 | struct timeval tv; |
cc26de46 TL |
1775 | |
1776 | /* Figure out how long it's been since we started transmitting. */ | |
02d9e453 | 1777 | interval = cur_time - client -> first_sending; |
cc26de46 TL |
1778 | |
1779 | /* If we're past the panic timeout, call the script and tell it | |
1780 | we haven't found anything for this interface yet. */ | |
02d9e453 TL |
1781 | if (interval > client -> config -> timeout) { |
1782 | state_panic (client); | |
b00d3884 | 1783 | return; |
cc26de46 | 1784 | } |
469cf3a4 | 1785 | |
9bdb9271 TL |
1786 | /* If we're selecting media, try the whole list before doing |
1787 | the exponential backoff, but if we've already received an | |
1788 | offer, stop looping, because we obviously have it right. */ | |
02d9e453 TL |
1789 | if (!client -> offered_leases && |
1790 | client -> config -> media) { | |
9bdb9271 TL |
1791 | int fail = 0; |
1792 | again: | |
02d9e453 TL |
1793 | if (client -> medium) { |
1794 | client -> medium = client -> medium -> next; | |
9bdb9271 | 1795 | increase = 0; |
3dbe2246 | 1796 | } |
02d9e453 | 1797 | if (!client -> medium) { |
9bdb9271 | 1798 | if (fail) |
8ae2d595 | 1799 | log_fatal ("No valid media types for %s!", |
02d9e453 TL |
1800 | client -> interface -> name); |
1801 | client -> medium = | |
1802 | client -> config -> media; | |
9bdb9271 TL |
1803 | increase = 1; |
1804 | } | |
3dbe2246 | 1805 | |
8ae2d595 | 1806 | log_info ("Trying medium \"%s\" %d", |
b1423aed | 1807 | client -> medium -> string, increase); |
02d9e453 TL |
1808 | script_init (client, "MEDIUM", client -> medium); |
1809 | if (script_go (client)) { | |
b1423aed | 1810 | fail = 1; |
9bdb9271 TL |
1811 | goto again; |
1812 | } | |
1813 | } | |
cc26de46 | 1814 | |
9bdb9271 TL |
1815 | /* If we're supposed to increase the interval, do so. If it's |
1816 | currently zero (i.e., we haven't sent any packets yet), set | |
98311e4b DH |
1817 | it to initial_interval; otherwise, add to it a random number |
1818 | between zero and two times itself. On average, this means | |
1819 | that it will double with every transmission. */ | |
9bdb9271 | 1820 | if (increase) { |
88cd8aca DH |
1821 | if (!client->interval) |
1822 | client->interval = client->config->initial_interval; | |
b1423aed | 1823 | else |
88cd8aca | 1824 | client->interval += random() % (2 * client->interval); |
cc26de46 | 1825 | |
34fdad6c | 1826 | /* Don't backoff past cutoff. */ |
88cd8aca DH |
1827 | if (client->interval > client->config->backoff_cutoff) |
1828 | client->interval = (client->config->backoff_cutoff / 2) | |
1829 | + (random() % client->config->backoff_cutoff); | |
1830 | } else if (!client->interval) | |
1831 | client->interval = client->config->initial_interval; | |
3dbe2246 | 1832 | |
cc26de46 TL |
1833 | /* If the backoff would take us to the panic timeout, just use that |
1834 | as the interval. */ | |
02d9e453 TL |
1835 | if (cur_time + client -> interval > |
1836 | client -> first_sending + client -> config -> timeout) | |
1837 | client -> interval = | |
1838 | (client -> first_sending + | |
1839 | client -> config -> timeout) - cur_time + 1; | |
cc26de46 | 1840 | |
34fdad6c | 1841 | /* Record the number of seconds since we started sending. */ |
af6df788 TL |
1842 | if (interval < 65536) |
1843 | client -> packet.secs = htons (interval); | |
34fdad6c | 1844 | else |
af6df788 TL |
1845 | client -> packet.secs = htons (65535); |
1846 | client -> secs = client -> packet.secs; | |
34fdad6c | 1847 | |
8ae2d595 | 1848 | log_info ("DHCPDISCOVER on %s to %s port %d interval %ld", |
02d9e453 | 1849 | client -> name ? client -> name : client -> interface -> name, |
66f973e4 | 1850 | inet_ntoa (sockaddr_broadcast.sin_addr), |
c4661845 | 1851 | ntohs (sockaddr_broadcast.sin_port), (long)(client -> interval)); |
66f973e4 | 1852 | |
cc26de46 | 1853 | /* Send out a packet. */ |
02d9e453 TL |
1854 | result = send_packet (client -> interface, (struct packet *)0, |
1855 | &client -> packet, | |
1856 | client -> packet_length, | |
cc26de46 TL |
1857 | inaddr_any, &sockaddr_broadcast, |
1858 | (struct hardware *)0); | |
469cf3a4 | 1859 | |
be62cf06 FD |
1860 | tv . tv_sec = cur_time + client -> interval; |
1861 | tv . tv_usec = 0; | |
1862 | add_timeout (&tv, send_discover, client, 0, 0); | |
469cf3a4 TL |
1863 | } |
1864 | ||
b00d3884 TL |
1865 | /* state_panic gets called if we haven't received any offers in a preset |
1866 | amount of time. When this happens, we try to use existing leases that | |
1867 | haven't yet expired, and failing that, we call the client script and | |
1868 | hope it can do something. */ | |
1869 | ||
02d9e453 TL |
1870 | void state_panic (cpp) |
1871 | void *cpp; | |
b00d3884 | 1872 | { |
02d9e453 TL |
1873 | struct client_state *client = cpp; |
1874 | struct client_lease *loop; | |
b00d3884 | 1875 | struct client_lease *lp; |
be62cf06 | 1876 | struct timeval tv; |
b00d3884 | 1877 | |
02d9e453 TL |
1878 | loop = lp = client -> active; |
1879 | ||
8ae2d595 | 1880 | log_info ("No DHCPOFFERS received."); |
b00d3884 | 1881 | |
deff2d59 TL |
1882 | /* We may not have an active lease, but we may have some |
1883 | predefined leases that we can try. */ | |
02d9e453 | 1884 | if (!client -> active && client -> leases) |
deff2d59 | 1885 | goto activate_next; |
deff2d59 | 1886 | |
b00d3884 | 1887 | /* Run through the list of leases and see if one can be used. */ |
02d9e453 TL |
1888 | while (client -> active) { |
1889 | if (client -> active -> expiry > cur_time) { | |
8ae2d595 | 1890 | log_info ("Trying recorded lease %s", |
02d9e453 | 1891 | piaddr (client -> active -> address)); |
b00d3884 TL |
1892 | /* Run the client script with the existing |
1893 | parameters. */ | |
02d9e453 TL |
1894 | script_init (client, "TIMEOUT", |
1895 | client -> active -> medium); | |
1896 | script_write_params (client, "new_", client -> active); | |
1897 | if (client -> alias) | |
1898 | script_write_params (client, "alias_", | |
1899 | client -> alias); | |
b00d3884 TL |
1900 | |
1901 | /* If the old lease is still good and doesn't | |
1902 | yet need renewal, go into BOUND state and | |
1903 | timeout at the renewal time. */ | |
02d9e453 | 1904 | if (!script_go (client)) { |
1c016679 TL |
1905 | if (cur_time < client -> active -> renewal) { |
1906 | client -> state = S_BOUND; | |
1907 | log_info ("bound: renewal in %ld %s.", | |
1908 | (long)(client -> active -> renewal - | |
1909 | cur_time), "seconds"); | |
be62cf06 FD |
1910 | tv . tv_sec = client -> active -> renewal; |
1911 | tv . tv_usec = 0; | |
1912 | add_timeout (&tv, state_bound, client, 0, 0); | |
1c016679 TL |
1913 | } else { |
1914 | client -> state = S_BOUND; | |
1915 | log_info ("bound: immediate renewal."); | |
1916 | state_bound (client); | |
1917 | } | |
1918 | reinitialize_interfaces (); | |
1919 | go_daemon (); | |
1920 | return; | |
b00d3884 TL |
1921 | } |
1922 | } | |
1923 | ||
1924 | /* If there are no other leases, give up. */ | |
02d9e453 TL |
1925 | if (!client -> leases) { |
1926 | client -> leases = client -> active; | |
1927 | client -> active = (struct client_lease *)0; | |
b00d3884 TL |
1928 | break; |
1929 | } | |
1930 | ||
deff2d59 | 1931 | activate_next: |
b00d3884 TL |
1932 | /* Otherwise, put the active lease at the end of the |
1933 | lease list, and try another lease.. */ | |
02d9e453 | 1934 | for (lp = client -> leases; lp -> next; lp = lp -> next) |
b00d3884 | 1935 | ; |
02d9e453 | 1936 | lp -> next = client -> active; |
34fdad6c TL |
1937 | if (lp -> next) { |
1938 | lp -> next -> next = (struct client_lease *)0; | |
1939 | } | |
02d9e453 TL |
1940 | client -> active = client -> leases; |
1941 | client -> leases = client -> leases -> next; | |
b00d3884 TL |
1942 | |
1943 | /* If we already tried this lease, we've exhausted the | |
1944 | set of leases, so we might as well give up for | |
1945 | now. */ | |
02d9e453 | 1946 | if (client -> active == loop) |
b00d3884 | 1947 | break; |
66f973e4 | 1948 | else if (!loop) |
02d9e453 | 1949 | loop = client -> active; |
b00d3884 TL |
1950 | } |
1951 | ||
1952 | /* No leases were available, or what was available didn't work, so | |
1953 | tell the shell script that we failed to allocate an address, | |
1954 | and try again later. */ | |
c08637bb TL |
1955 | if (onetry) { |
1956 | if (!quiet) | |
1957 | log_info ("Unable to obtain a lease on first try.%s", | |
1958 | " Exiting."); | |
1959 | exit (2); | |
1960 | } | |
1961 | ||
11373fb6 | 1962 | log_info ("No working leases in persistent database - sleeping."); |
02d9e453 TL |
1963 | script_init (client, "FAIL", (struct string_list *)0); |
1964 | if (client -> alias) | |
1965 | script_write_params (client, "alias_", client -> alias); | |
1966 | script_go (client); | |
1967 | client -> state = S_INIT; | |
be62cf06 | 1968 | tv . tv_sec = cur_time + |
11373fb6 | 1969 | ((client -> config -> retry_interval + 1) / 2 + |
be62cf06 FD |
1970 | (random () % client -> config -> retry_interval)); |
1971 | tv . tv_usec = 0; | |
1972 | add_timeout (&tv, state_init, client, 0, 0); | |
ae04fa03 | 1973 | go_daemon (); |
b00d3884 TL |
1974 | } |
1975 | ||
02d9e453 TL |
1976 | void send_request (cpp) |
1977 | void *cpp; | |
e581d615 | 1978 | { |
02d9e453 | 1979 | struct client_state *client = cpp; |
84c4adde | 1980 | |
c1503c57 | 1981 | int result; |
cc26de46 TL |
1982 | int interval; |
1983 | struct sockaddr_in destination; | |
1984 | struct in_addr from; | |
be62cf06 | 1985 | struct timeval tv; |
cc26de46 TL |
1986 | |
1987 | /* Figure out how long it's been since we started transmitting. */ | |
02d9e453 | 1988 | interval = cur_time - client -> first_sending; |
cc26de46 | 1989 | |
34fdad6c TL |
1990 | /* If we're in the INIT-REBOOT or REQUESTING state and we're |
1991 | past the reboot timeout, go to INIT and see if we can | |
1992 | DISCOVER an address... */ | |
1993 | /* XXX In the INIT-REBOOT state, if we don't get an ACK, it | |
1994 | means either that we're on a network with no DHCP server, | |
1995 | or that our server is down. In the latter case, assuming | |
1996 | that there is a backup DHCP server, DHCPDISCOVER will get | |
1997 | us a new address, but we could also have successfully | |
1998 | reused our old address. In the former case, we're hosed | |
1999 | anyway. This is not a win-prone situation. */ | |
02d9e453 TL |
2000 | if ((client -> state == S_REBOOTING || |
2001 | client -> state == S_REQUESTING) && | |
2002 | interval > client -> config -> reboot_timeout) { | |
b8cf055d | 2003 | cancel: |
02d9e453 TL |
2004 | client -> state = S_INIT; |
2005 | cancel_timeout (send_request, client); | |
2006 | state_init (client); | |
deff2d59 TL |
2007 | return; |
2008 | } | |
2009 | ||
b8cf055d TL |
2010 | /* If we're in the reboot state, make sure the media is set up |
2011 | correctly. */ | |
02d9e453 TL |
2012 | if (client -> state == S_REBOOTING && |
2013 | !client -> medium && | |
2014 | client -> active -> medium ) { | |
2015 | script_init (client, "MEDIUM", client -> active -> medium); | |
b8cf055d TL |
2016 | |
2017 | /* If the medium we chose won't fly, go to INIT state. */ | |
02d9e453 | 2018 | if (script_go (client)) |
b8cf055d TL |
2019 | goto cancel; |
2020 | ||
2021 | /* Record the medium. */ | |
02d9e453 | 2022 | client -> medium = client -> active -> medium; |
b8cf055d TL |
2023 | } |
2024 | ||
cc26de46 TL |
2025 | /* If the lease has expired, relinquish the address and go back |
2026 | to the INIT state. */ | |
02d9e453 TL |
2027 | if (client -> state != S_REQUESTING && |
2028 | cur_time > client -> active -> expiry) { | |
cc26de46 | 2029 | /* Run the client script with the new parameters. */ |
02d9e453 TL |
2030 | script_init (client, "EXPIRE", (struct string_list *)0); |
2031 | script_write_params (client, "old_", client -> active); | |
2032 | if (client -> alias) | |
2033 | script_write_params (client, "alias_", | |
2034 | client -> alias); | |
2035 | script_go (client); | |
2036 | ||
62d0cb47 TL |
2037 | /* Now do a preinit on the interface so that we can |
2038 | discover a new address. */ | |
2039 | script_init (client, "PREINIT", (struct string_list *)0); | |
2040 | if (client -> alias) | |
2041 | script_write_params (client, "alias_", | |
2042 | client -> alias); | |
2043 | script_go (client); | |
2044 | ||
02d9e453 TL |
2045 | client -> state = S_INIT; |
2046 | state_init (client); | |
cc26de46 TL |
2047 | return; |
2048 | } | |
2049 | ||
2050 | /* Do the exponential backoff... */ | |
02d9e453 TL |
2051 | if (!client -> interval) |
2052 | client -> interval = client -> config -> initial_interval; | |
34fdad6c | 2053 | else { |
02d9e453 TL |
2054 | client -> interval += ((random () >> 2) % |
2055 | (2 * client -> interval)); | |
34fdad6c | 2056 | } |
3dbe2246 | 2057 | |
34fdad6c | 2058 | /* Don't backoff past cutoff. */ |
02d9e453 TL |
2059 | if (client -> interval > |
2060 | client -> config -> backoff_cutoff) | |
2061 | client -> interval = | |
2062 | ((client -> config -> backoff_cutoff / 2) | |
98311e4b DH |
2063 | + ((random () >> 2) % |
2064 | client -> config -> backoff_cutoff)); | |
cc26de46 TL |
2065 | |
2066 | /* If the backoff would take us to the expiry time, just set the | |
2067 | timeout to the expiry time. */ | |
02d9e453 TL |
2068 | if (client -> state != S_REQUESTING && |
2069 | cur_time + client -> interval > client -> active -> expiry) | |
2070 | client -> interval = | |
2071 | client -> active -> expiry - cur_time + 1; | |
cc26de46 TL |
2072 | |
2073 | /* If the lease T2 time has elapsed, or if we're not yet bound, | |
2074 | broadcast the DHCPREQUEST rather than unicasting. */ | |
02d9e453 | 2075 | if (client -> state == S_REQUESTING || |
62d0cb47 | 2076 | client -> state == S_REBOOTING || |
02d9e453 | 2077 | cur_time > client -> active -> rebind) |
3020a2c1 | 2078 | destination.sin_addr = sockaddr_broadcast.sin_addr; |
cc26de46 TL |
2079 | else |
2080 | memcpy (&destination.sin_addr.s_addr, | |
02d9e453 | 2081 | client -> destination.iabuf, |
cc26de46 TL |
2082 | sizeof destination.sin_addr.s_addr); |
2083 | destination.sin_port = remote_port; | |
b00d3884 TL |
2084 | destination.sin_family = AF_INET; |
2085 | #ifdef HAVE_SA_LEN | |
2086 | destination.sin_len = sizeof destination; | |
2087 | #endif | |
cc26de46 | 2088 | |
7d3bc735 TL |
2089 | if (client -> state == S_RENEWING || |
2090 | client -> state == S_REBINDING) | |
02d9e453 | 2091 | memcpy (&from, client -> active -> address.iabuf, |
cc26de46 TL |
2092 | sizeof from); |
2093 | else | |
2094 | from.s_addr = INADDR_ANY; | |
2095 | ||
34fdad6c | 2096 | /* Record the number of seconds since we started sending. */ |
af6df788 TL |
2097 | if (client -> state == S_REQUESTING) |
2098 | client -> packet.secs = client -> secs; | |
2099 | else { | |
2100 | if (interval < 65536) | |
2101 | client -> packet.secs = htons (interval); | |
2102 | else | |
2103 | client -> packet.secs = htons (65535); | |
2104 | } | |
34fdad6c | 2105 | |
8ae2d595 | 2106 | log_info ("DHCPREQUEST on %s to %s port %d", |
02d9e453 | 2107 | client -> name ? client -> name : client -> interface -> name, |
cc26de46 TL |
2108 | inet_ntoa (destination.sin_addr), |
2109 | ntohs (destination.sin_port)); | |
2110 | ||
62d0cb47 TL |
2111 | if (destination.sin_addr.s_addr != INADDR_BROADCAST && |
2112 | fallback_interface) | |
2113 | result = send_packet (fallback_interface, | |
2114 | (struct packet *)0, | |
2115 | &client -> packet, | |
2116 | client -> packet_length, | |
2117 | from, &destination, | |
2118 | (struct hardware *)0); | |
cc26de46 | 2119 | else |
cc26de46 | 2120 | /* Send out a packet. */ |
02d9e453 TL |
2121 | result = send_packet (client -> interface, (struct packet *)0, |
2122 | &client -> packet, | |
2123 | client -> packet_length, | |
cc26de46 TL |
2124 | from, &destination, |
2125 | (struct hardware *)0); | |
469cf3a4 | 2126 | |
be62cf06 FD |
2127 | tv . tv_sec = cur_time + client -> interval; |
2128 | tv . tv_usec = 0; | |
2129 | add_timeout (&tv, send_request, client, 0, 0); | |
469cf3a4 TL |
2130 | } |
2131 | ||
02d9e453 TL |
2132 | void send_decline (cpp) |
2133 | void *cpp; | |
b00d3884 | 2134 | { |
02d9e453 | 2135 | struct client_state *client = cpp; |
84c4adde | 2136 | |
b00d3884 TL |
2137 | int result; |
2138 | ||
8ae2d595 | 2139 | log_info ("DHCPDECLINE on %s to %s port %d", |
02d9e453 | 2140 | client -> name ? client -> name : client -> interface -> name, |
b00d3884 TL |
2141 | inet_ntoa (sockaddr_broadcast.sin_addr), |
2142 | ntohs (sockaddr_broadcast.sin_port)); | |
2143 | ||
2144 | /* Send out a packet. */ | |
02d9e453 TL |
2145 | result = send_packet (client -> interface, (struct packet *)0, |
2146 | &client -> packet, | |
2147 | client -> packet_length, | |
b00d3884 TL |
2148 | inaddr_any, &sockaddr_broadcast, |
2149 | (struct hardware *)0); | |
b00d3884 TL |
2150 | } |
2151 | ||
02d9e453 TL |
2152 | void send_release (cpp) |
2153 | void *cpp; | |
b00d3884 | 2154 | { |
02d9e453 | 2155 | struct client_state *client = cpp; |
84c4adde | 2156 | |
b00d3884 | 2157 | int result; |
007e3ee4 TL |
2158 | struct sockaddr_in destination; |
2159 | struct in_addr from; | |
2160 | ||
2161 | memcpy (&from, client -> active -> address.iabuf, | |
2162 | sizeof from); | |
2163 | memcpy (&destination.sin_addr.s_addr, | |
2164 | client -> destination.iabuf, | |
2165 | sizeof destination.sin_addr.s_addr); | |
2166 | destination.sin_port = remote_port; | |
2167 | destination.sin_family = AF_INET; | |
2168 | #ifdef HAVE_SA_LEN | |
2169 | destination.sin_len = sizeof destination; | |
2170 | #endif | |
2171 | ||
007e3ee4 TL |
2172 | /* Set the lease to end now, so that we don't accidentally |
2173 | reuse it if we restart before the old expiry time. */ | |
2174 | client -> active -> expiry = | |
2175 | client -> active -> renewal = | |
2176 | client -> active -> rebind = cur_time; | |
2177 | if (!write_client_lease (client, client -> active, 1, 1)) { | |
2178 | log_error ("Can't release lease: lease write failed."); | |
2179 | return; | |
2180 | } | |
b00d3884 | 2181 | |
8ae2d595 | 2182 | log_info ("DHCPRELEASE on %s to %s port %d", |
02d9e453 | 2183 | client -> name ? client -> name : client -> interface -> name, |
007e3ee4 TL |
2184 | inet_ntoa (destination.sin_addr), |
2185 | ntohs (destination.sin_port)); | |
b00d3884 | 2186 | |
007e3ee4 TL |
2187 | if (fallback_interface) |
2188 | result = send_packet (fallback_interface, | |
2189 | (struct packet *)0, | |
2190 | &client -> packet, | |
2191 | client -> packet_length, | |
2192 | from, &destination, | |
2193 | (struct hardware *)0); | |
2194 | else | |
2195 | /* Send out a packet. */ | |
2196 | result = send_packet (client -> interface, (struct packet *)0, | |
2197 | &client -> packet, | |
2198 | client -> packet_length, | |
2199 | from, &destination, | |
2200 | (struct hardware *)0); | |
b00d3884 TL |
2201 | } |
2202 | ||
0c20eab3 DH |
2203 | void |
2204 | make_client_options(struct client_state *client, struct client_lease *lease, | |
2205 | u_int8_t *type, struct option_cache *sid, | |
2206 | struct iaddr *rip, struct option **prl, | |
2207 | struct option_state **op) | |
469cf3a4 | 2208 | { |
b1b7b521 | 2209 | unsigned i; |
02a015fb | 2210 | struct option_cache *oc; |
f7fdb216 | 2211 | struct option *option = NULL; |
02a015fb TL |
2212 | struct buffer *bp = (struct buffer *)0; |
2213 | ||
d758ad8c TL |
2214 | /* If there are any leftover options, get rid of them. */ |
2215 | if (*op) | |
2216 | option_state_dereference (op, MDL); | |
2217 | ||
230e73e4 | 2218 | /* Allocate space for options. */ |
cf78bf20 | 2219 | option_state_allocate (op, MDL); |
02a015fb TL |
2220 | |
2221 | /* Send the server identifier if provided. */ | |
2222 | if (sid) | |
230e73e4 | 2223 | save_option (&dhcp_universe, *op, sid); |
02a015fb | 2224 | |
2455808f TL |
2225 | oc = (struct option_cache *)0; |
2226 | ||
02a015fb TL |
2227 | /* Send the requested address if provided. */ |
2228 | if (rip) { | |
02d9e453 | 2229 | client -> requested_address = *rip; |
f7fdb216 DH |
2230 | i = DHO_DHCP_REQUESTED_ADDRESS; |
2231 | if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash, | |
2232 | &i, 0, MDL) && | |
2233 | make_const_option_cache(&oc, NULL, rip->iabuf, rip->len, | |
2234 | option, MDL))) | |
230e73e4 | 2235 | log_error ("can't make requested address cache."); |
02a015fb | 2236 | else { |
230e73e4 | 2237 | save_option (&dhcp_universe, *op, oc); |
cf78bf20 | 2238 | option_cache_dereference (&oc, MDL); |
02a015fb | 2239 | } |
f7fdb216 | 2240 | option_dereference(&option, MDL); |
b00d3884 | 2241 | } else { |
02d9e453 | 2242 | client -> requested_address.len = 0; |
cc26de46 | 2243 | } |
c1503c57 | 2244 | |
f7fdb216 DH |
2245 | i = DHO_DHCP_MESSAGE_TYPE; |
2246 | if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash, &i, 0, | |
2247 | MDL) && | |
2248 | make_const_option_cache(&oc, NULL, type, 1, option, MDL))) | |
8ae2d595 | 2249 | log_error ("can't make message type."); |
02a015fb | 2250 | else { |
230e73e4 | 2251 | save_option (&dhcp_universe, *op, oc); |
cf78bf20 | 2252 | option_cache_dereference (&oc, MDL); |
02a015fb | 2253 | } |
f7fdb216 | 2254 | option_dereference(&option, MDL); |
02a015fb TL |
2255 | |
2256 | if (prl) { | |
0c20eab3 DH |
2257 | int len; |
2258 | ||
2259 | /* Probe the length of the list. */ | |
2260 | len = 0; | |
2261 | for (i = 0 ; prl[i] != NULL ; i++) | |
2262 | if (prl[i]->universe == &dhcp_universe) | |
2263 | len++; | |
2264 | ||
2265 | if (!buffer_allocate (&bp, len, MDL)) | |
230e73e4 | 2266 | log_error ("can't make parameter list buffer."); |
02a015fb | 2267 | else { |
d6614ea2 DH |
2268 | unsigned code = DHO_DHCP_PARAMETER_REQUEST_LIST; |
2269 | ||
0c20eab3 DH |
2270 | len = 0; |
2271 | for (i = 0 ; prl[i] != NULL ; i++) | |
2272 | if (prl[i]->universe == &dhcp_universe) | |
2273 | bp->data[len++] = prl[i]->code; | |
2274 | ||
f7fdb216 DH |
2275 | if (!(option_code_hash_lookup(&option, |
2276 | dhcp_universe.code_hash, | |
d6614ea2 | 2277 | &code, 0, MDL) && |
0c20eab3 | 2278 | make_const_option_cache(&oc, &bp, NULL, len, |
f7fdb216 | 2279 | option, MDL))) |
8ae2d595 | 2280 | log_error ("can't make option cache"); |
02a015fb | 2281 | else { |
230e73e4 | 2282 | save_option (&dhcp_universe, *op, oc); |
cf78bf20 | 2283 | option_cache_dereference (&oc, MDL); |
02a015fb | 2284 | } |
f7fdb216 | 2285 | option_dereference(&option, MDL); |
deff2d59 TL |
2286 | } |
2287 | } | |
2288 | ||
02a015fb | 2289 | /* Run statements that need to be run on transmission. */ |
73530742 TL |
2290 | if (client -> config -> on_transmission) |
2291 | execute_statements_in_scope | |
1b234d44 | 2292 | ((struct binding_value **)0, |
66e9cecf | 2293 | (struct packet *)0, (struct lease *)0, client, |
da38df14 | 2294 | (lease ? lease -> options : (struct option_state *)0), |
cf78bf20 TL |
2295 | *op, &global_scope, |
2296 | client -> config -> on_transmission, | |
73530742 | 2297 | (struct group *)0); |
02a015fb TL |
2298 | } |
2299 | ||
02d9e453 TL |
2300 | void make_discover (client, lease) |
2301 | struct client_state *client; | |
02a015fb TL |
2302 | struct client_lease *lease; |
2303 | { | |
02a015fb | 2304 | unsigned char discover = DHCPDISCOVER; |
230e73e4 | 2305 | struct option_state *options = (struct option_state *)0; |
02a015fb | 2306 | |
02d9e453 | 2307 | memset (&client -> packet, 0, sizeof (client -> packet)); |
02a015fb | 2308 | |
02d9e453 TL |
2309 | make_client_options (client, |
2310 | lease, &discover, (struct option_cache *)0, | |
02a015fb | 2311 | lease ? &lease -> address : (struct iaddr *)0, |
02d9e453 | 2312 | client -> config -> requested_options, |
02a015fb TL |
2313 | &options); |
2314 | ||
c1503c57 | 2315 | /* Set up the option buffer... */ |
02d9e453 | 2316 | client -> packet_length = |
da38df14 | 2317 | cons_options ((struct packet *)0, &client -> packet, |
98311e4b DH |
2318 | (struct lease *)0, client, |
2319 | /* maximum packet size */1500, | |
2320 | (struct option_state *)0, | |
2321 | options, | |
2322 | /* scope */ &global_scope, | |
2323 | /* overload */ 0, | |
2324 | /* terminate */0, | |
2325 | /* bootpp */0, | |
2326 | (struct data_string *)0, | |
ce75142f | 2327 | client -> config -> vendor_space_name); |
98311e4b DH |
2328 | |
2329 | option_state_dereference (&options, MDL); | |
02d9e453 TL |
2330 | if (client -> packet_length < BOOTP_MIN_LEN) |
2331 | client -> packet_length = BOOTP_MIN_LEN; | |
2332 | ||
2333 | client -> packet.op = BOOTREQUEST; | |
31730f17 TL |
2334 | client -> packet.htype = client -> interface -> hw_address.hbuf [0]; |
2335 | client -> packet.hlen = client -> interface -> hw_address.hlen - 1; | |
02d9e453 TL |
2336 | client -> packet.hops = 0; |
2337 | client -> packet.xid = random (); | |
2338 | client -> packet.secs = 0; /* filled in by send_discover. */ | |
f345b36d TL |
2339 | |
2340 | if (can_receive_unicast_unconfigured (client -> interface)) | |
2341 | client -> packet.flags = 0; | |
2342 | else | |
2343 | client -> packet.flags = htons (BOOTP_BROADCAST); | |
2344 | ||
02d9e453 TL |
2345 | memset (&(client -> packet.ciaddr), |
2346 | 0, sizeof client -> packet.ciaddr); | |
2347 | memset (&(client -> packet.yiaddr), | |
2348 | 0, sizeof client -> packet.yiaddr); | |
2349 | memset (&(client -> packet.siaddr), | |
2350 | 0, sizeof client -> packet.siaddr); | |
11373fb6 | 2351 | client -> packet.giaddr = giaddr; |
31730f17 TL |
2352 | if (client -> interface -> hw_address.hlen > 0) |
2353 | memcpy (client -> packet.chaddr, | |
2354 | &client -> interface -> hw_address.hbuf [1], | |
2355 | (unsigned)(client -> interface -> hw_address.hlen - 1)); | |
c1503c57 TL |
2356 | |
2357 | #ifdef DEBUG_PACKET | |
01aeb18a | 2358 | dump_raw ((unsigned char *)&client -> packet, client -> packet_length); |
c1503c57 | 2359 | #endif |
c1503c57 TL |
2360 | } |
2361 | ||
469cf3a4 | 2362 | |
02d9e453 TL |
2363 | void make_request (client, lease) |
2364 | struct client_state *client; | |
cc26de46 | 2365 | struct client_lease *lease; |
c1503c57 | 2366 | { |
c1503c57 | 2367 | unsigned char request = DHCPREQUEST; |
02a015fb | 2368 | struct option_cache *oc; |
c1503c57 | 2369 | |
02d9e453 | 2370 | memset (&client -> packet, 0, sizeof (client -> packet)); |
469cf3a4 | 2371 | |
02d9e453 | 2372 | if (client -> state == S_REQUESTING) |
230e73e4 | 2373 | oc = lookup_option (&dhcp_universe, lease -> options, |
02a015fb TL |
2374 | DHO_DHCP_SERVER_IDENTIFIER); |
2375 | else | |
2376 | oc = (struct option_cache *)0; | |
8dba80a6 | 2377 | |
98311e4b DH |
2378 | if (client -> sent_options) |
2379 | option_state_dereference (&client -> sent_options, MDL); | |
2380 | ||
02d9e453 TL |
2381 | make_client_options (client, lease, &request, oc, |
2382 | ((client -> state == S_REQUESTING || | |
2383 | client -> state == S_REBOOTING) | |
02a015fb TL |
2384 | ? &lease -> address |
2385 | : (struct iaddr *)0), | |
02d9e453 | 2386 | client -> config -> requested_options, |
d758ad8c | 2387 | &client -> sent_options); |
deff2d59 | 2388 | |
c1503c57 | 2389 | /* Set up the option buffer... */ |
02d9e453 | 2390 | client -> packet_length = |
da38df14 | 2391 | cons_options ((struct packet *)0, &client -> packet, |
98311e4b DH |
2392 | (struct lease *)0, client, |
2393 | /* maximum packet size */1500, | |
2394 | (struct option_state *)0, | |
3dbe2246 | 2395 | client -> sent_options, |
98311e4b DH |
2396 | /* scope */ &global_scope, |
2397 | /* overload */ 0, | |
2398 | /* terminate */0, | |
2399 | /* bootpp */0, | |
2400 | (struct data_string *)0, | |
ce75142f | 2401 | client -> config -> vendor_space_name); |
98311e4b | 2402 | |
02d9e453 TL |
2403 | if (client -> packet_length < BOOTP_MIN_LEN) |
2404 | client -> packet_length = BOOTP_MIN_LEN; | |
cc26de46 | 2405 | |
02d9e453 | 2406 | client -> packet.op = BOOTREQUEST; |
31730f17 TL |
2407 | client -> packet.htype = client -> interface -> hw_address.hbuf [0]; |
2408 | client -> packet.hlen = client -> interface -> hw_address.hlen - 1; | |
02d9e453 TL |
2409 | client -> packet.hops = 0; |
2410 | client -> packet.xid = client -> xid; | |
2411 | client -> packet.secs = 0; /* Filled in by send_request. */ | |
cc26de46 TL |
2412 | |
2413 | /* If we own the address we're requesting, put it in ciaddr; | |
2414 | otherwise set ciaddr to zero. */ | |
02d9e453 TL |
2415 | if (client -> state == S_BOUND || |
2416 | client -> state == S_RENEWING || | |
62d0cb47 | 2417 | client -> state == S_REBINDING) { |
02d9e453 | 2418 | memcpy (&client -> packet.ciaddr, |
cc26de46 | 2419 | lease -> address.iabuf, lease -> address.len); |
339b0231 | 2420 | client -> packet.flags = 0; |
62d0cb47 | 2421 | } else { |
02d9e453 TL |
2422 | memset (&client -> packet.ciaddr, 0, |
2423 | sizeof client -> packet.ciaddr); | |
f345b36d TL |
2424 | if (can_receive_unicast_unconfigured (client -> interface)) |
2425 | client -> packet.flags = 0; | |
2426 | else | |
2427 | client -> packet.flags = htons (BOOTP_BROADCAST); | |
62d0cb47 | 2428 | } |
02d9e453 TL |
2429 | |
2430 | memset (&client -> packet.yiaddr, 0, | |
2431 | sizeof client -> packet.yiaddr); | |
2432 | memset (&client -> packet.siaddr, 0, | |
2433 | sizeof client -> packet.siaddr); | |
1c44f6bd TL |
2434 | if (client -> state != S_BOUND && |
2435 | client -> state != S_RENEWING) | |
2436 | client -> packet.giaddr = giaddr; | |
2437 | else | |
2438 | memset (&client -> packet.giaddr, 0, | |
2439 | sizeof client -> packet.giaddr); | |
31730f17 TL |
2440 | if (client -> interface -> hw_address.hlen > 0) |
2441 | memcpy (client -> packet.chaddr, | |
2442 | &client -> interface -> hw_address.hbuf [1], | |
2443 | (unsigned)(client -> interface -> hw_address.hlen - 1)); | |
c1503c57 TL |
2444 | |
2445 | #ifdef DEBUG_PACKET | |
01aeb18a | 2446 | dump_raw ((unsigned char *)&client -> packet, client -> packet_length); |
c1503c57 | 2447 | #endif |
469cf3a4 | 2448 | } |
c1503c57 | 2449 | |
02d9e453 TL |
2450 | void make_decline (client, lease) |
2451 | struct client_state *client; | |
cc26de46 | 2452 | struct client_lease *lease; |
469cf3a4 | 2453 | { |
cc26de46 | 2454 | unsigned char decline = DHCPDECLINE; |
02a015fb | 2455 | struct option_cache *oc; |
c1503c57 | 2456 | |
230e73e4 | 2457 | struct option_state *options = (struct option_state *)0; |
c1503c57 | 2458 | |
40ec5f38 | 2459 | /* Create the options cache. */ |
230e73e4 | 2460 | oc = lookup_option (&dhcp_universe, lease -> options, |
02a015fb | 2461 | DHO_DHCP_SERVER_IDENTIFIER); |
0c20eab3 DH |
2462 | make_client_options(client, lease, &decline, oc, &lease->address, |
2463 | NULL, &options); | |
ca25f4ab | 2464 | |
40ec5f38 | 2465 | /* Consume the options cache into the option buffer. */ |
230e73e4 | 2466 | memset (&client -> packet, 0, sizeof (client -> packet)); |
02d9e453 | 2467 | client -> packet_length = |
da38df14 | 2468 | cons_options ((struct packet *)0, &client -> packet, |
66e9cecf | 2469 | (struct lease *)0, client, 0, |
0852a27f | 2470 | (struct option_state *)0, options, |
ce75142f TL |
2471 | &global_scope, 0, 0, 0, (struct data_string *)0, |
2472 | client -> config -> vendor_space_name); | |
40ec5f38 DH |
2473 | |
2474 | /* Destroy the options cache. */ | |
98311e4b | 2475 | option_state_dereference (&options, MDL); |
40ec5f38 | 2476 | |
02d9e453 TL |
2477 | if (client -> packet_length < BOOTP_MIN_LEN) |
2478 | client -> packet_length = BOOTP_MIN_LEN; | |
cc26de46 | 2479 | |
02d9e453 | 2480 | client -> packet.op = BOOTREQUEST; |
31730f17 TL |
2481 | client -> packet.htype = client -> interface -> hw_address.hbuf [0]; |
2482 | client -> packet.hlen = client -> interface -> hw_address.hlen - 1; | |
02d9e453 TL |
2483 | client -> packet.hops = 0; |
2484 | client -> packet.xid = client -> xid; | |
2485 | client -> packet.secs = 0; /* Filled in by send_request. */ | |
7d3bc735 TL |
2486 | if (can_receive_unicast_unconfigured (client -> interface)) |
2487 | client -> packet.flags = 0; | |
2488 | else | |
2489 | client -> packet.flags = htons (BOOTP_BROADCAST); | |
cc26de46 TL |
2490 | |
2491 | /* ciaddr must always be zero. */ | |
02d9e453 TL |
2492 | memset (&client -> packet.ciaddr, 0, |
2493 | sizeof client -> packet.ciaddr); | |
2494 | memset (&client -> packet.yiaddr, 0, | |
2495 | sizeof client -> packet.yiaddr); | |
2496 | memset (&client -> packet.siaddr, 0, | |
2497 | sizeof client -> packet.siaddr); | |
11373fb6 | 2498 | client -> packet.giaddr = giaddr; |
02d9e453 | 2499 | memcpy (client -> packet.chaddr, |
31730f17 | 2500 | &client -> interface -> hw_address.hbuf [1], |
02d9e453 | 2501 | client -> interface -> hw_address.hlen); |
cc26de46 TL |
2502 | |
2503 | #ifdef DEBUG_PACKET | |
01aeb18a | 2504 | dump_raw ((unsigned char *)&client -> packet, client -> packet_length); |
cc26de46 TL |
2505 | #endif |
2506 | } | |
2507 | ||
02d9e453 TL |
2508 | void make_release (client, lease) |
2509 | struct client_state *client; | |
cc26de46 TL |
2510 | struct client_lease *lease; |
2511 | { | |
2512 | unsigned char request = DHCPRELEASE; | |
02a015fb | 2513 | struct option_cache *oc; |
cc26de46 | 2514 | |
230e73e4 | 2515 | struct option_state *options = (struct option_state *)0; |
cc26de46 | 2516 | |
02d9e453 | 2517 | memset (&client -> packet, 0, sizeof (client -> packet)); |
469cf3a4 | 2518 | |
230e73e4 | 2519 | oc = lookup_option (&dhcp_universe, lease -> options, |
02a015fb | 2520 | DHO_DHCP_SERVER_IDENTIFIER); |
0c20eab3 | 2521 | make_client_options(client, lease, &request, oc, NULL, NULL, &options); |
469cf3a4 TL |
2522 | |
2523 | /* Set up the option buffer... */ | |
02d9e453 | 2524 | client -> packet_length = |
da38df14 | 2525 | cons_options ((struct packet *)0, &client -> packet, |
98311e4b DH |
2526 | (struct lease *)0, client, |
2527 | /* maximum packet size */1500, | |
2528 | (struct option_state *)0, | |
2529 | options, | |
2530 | /* scope */ &global_scope, | |
2531 | /* overload */ 0, | |
2532 | /* terminate */0, | |
2533 | /* bootpp */0, | |
2534 | (struct data_string *)0, | |
ce75142f | 2535 | client -> config -> vendor_space_name); |
98311e4b | 2536 | |
02d9e453 TL |
2537 | if (client -> packet_length < BOOTP_MIN_LEN) |
2538 | client -> packet_length = BOOTP_MIN_LEN; | |
cf78bf20 | 2539 | option_state_dereference (&options, MDL); |
02d9e453 TL |
2540 | |
2541 | client -> packet.op = BOOTREQUEST; | |
31730f17 TL |
2542 | client -> packet.htype = client -> interface -> hw_address.hbuf [0]; |
2543 | client -> packet.hlen = client -> interface -> hw_address.hlen - 1; | |
02d9e453 | 2544 | client -> packet.hops = 0; |
74f45f96 | 2545 | client -> packet.xid = random (); |
02d9e453 TL |
2546 | client -> packet.secs = 0; |
2547 | client -> packet.flags = 0; | |
2548 | memcpy (&client -> packet.ciaddr, | |
cc26de46 | 2549 | lease -> address.iabuf, lease -> address.len); |
02d9e453 TL |
2550 | memset (&client -> packet.yiaddr, 0, |
2551 | sizeof client -> packet.yiaddr); | |
2552 | memset (&client -> packet.siaddr, 0, | |
2553 | sizeof client -> packet.siaddr); | |
11373fb6 | 2554 | client -> packet.giaddr = giaddr; |
02d9e453 | 2555 | memcpy (client -> packet.chaddr, |
bdcaf7b9 | 2556 | &client -> interface -> hw_address.hbuf [1], |
02d9e453 | 2557 | client -> interface -> hw_address.hlen); |
469cf3a4 TL |
2558 | |
2559 | #ifdef DEBUG_PACKET | |
01aeb18a | 2560 | dump_raw ((unsigned char *)&client -> packet, client -> packet_length); |
469cf3a4 | 2561 | #endif |
e581d615 | 2562 | } |
cc26de46 | 2563 | |
02a015fb | 2564 | void destroy_client_lease (lease) |
cc26de46 TL |
2565 | struct client_lease *lease; |
2566 | { | |
cc26de46 | 2567 | if (lease -> server_name) |
cf78bf20 | 2568 | dfree (lease -> server_name, MDL); |
cc26de46 | 2569 | if (lease -> filename) |
cf78bf20 TL |
2570 | dfree (lease -> filename, MDL); |
2571 | option_state_dereference (&lease -> options, MDL); | |
2572 | free_client_lease (lease, MDL); | |
cc26de46 TL |
2573 | } |
2574 | ||
98bd7ca0 DH |
2575 | FILE *leaseFile = NULL; |
2576 | int leases_written = 0; | |
cc26de46 TL |
2577 | |
2578 | void rewrite_client_leases () | |
2579 | { | |
2580 | struct interface_info *ip; | |
02d9e453 | 2581 | struct client_state *client; |
cc26de46 TL |
2582 | struct client_lease *lp; |
2583 | ||
98bd7ca0 | 2584 | if (leaseFile != NULL) |
cc26de46 TL |
2585 | fclose (leaseFile); |
2586 | leaseFile = fopen (path_dhclient_db, "w"); | |
98bd7ca0 | 2587 | if (leaseFile == NULL) { |
ee8a3653 TL |
2588 | log_error ("can't create %s: %m", path_dhclient_db); |
2589 | return; | |
2590 | } | |
b00d3884 | 2591 | |
98bd7ca0 DH |
2592 | /* If there is a default duid, write it out. */ |
2593 | if (default_duid.len != 0) | |
2594 | write_duid(&default_duid); | |
2595 | ||
b00d3884 TL |
2596 | /* Write out all the leases attached to configured interfaces that |
2597 | we know about. */ | |
cc26de46 | 2598 | for (ip = interfaces; ip; ip = ip -> next) { |
02d9e453 TL |
2599 | for (client = ip -> client; client; client = client -> next) { |
2600 | for (lp = client -> leases; lp; lp = lp -> next) { | |
007e3ee4 | 2601 | write_client_lease (client, lp, 1, 0); |
02d9e453 TL |
2602 | } |
2603 | if (client -> active) | |
2604 | write_client_lease (client, | |
007e3ee4 | 2605 | client -> active, 1, 0); |
98bd7ca0 DH |
2606 | |
2607 | if (client->active_lease != NULL) | |
2608 | write_client6_lease(client, | |
2609 | client->active_lease, | |
2610 | 1, 0); | |
cc26de46 | 2611 | } |
cc26de46 | 2612 | } |
b00d3884 TL |
2613 | |
2614 | /* Write out any leases that are attached to interfaces that aren't | |
2615 | currently configured. */ | |
2616 | for (ip = dummy_interfaces; ip; ip = ip -> next) { | |
02d9e453 TL |
2617 | for (client = ip -> client; client; client = client -> next) { |
2618 | for (lp = client -> leases; lp; lp = lp -> next) { | |
007e3ee4 | 2619 | write_client_lease (client, lp, 1, 0); |
02d9e453 TL |
2620 | } |
2621 | if (client -> active) | |
2622 | write_client_lease (client, | |
007e3ee4 | 2623 | client -> active, 1, 0); |
98bd7ca0 DH |
2624 | |
2625 | if (client->active_lease != NULL) | |
2626 | write_client6_lease(client, | |
2627 | client->active_lease, | |
2628 | 1, 0); | |
b00d3884 | 2629 | } |
b00d3884 | 2630 | } |
cc26de46 TL |
2631 | fflush (leaseFile); |
2632 | } | |
2633 | ||
c0504523 TL |
2634 | void write_lease_option (struct option_cache *oc, |
2635 | struct packet *packet, struct lease *lease, | |
66e9cecf | 2636 | struct client_state *client_state, |
c0504523 TL |
2637 | struct option_state *in_options, |
2638 | struct option_state *cfg_options, | |
2639 | struct binding_scope **scope, | |
2640 | struct universe *u, void *stuff) | |
2641 | { | |
2642 | const char *name, *dot; | |
2643 | struct data_string ds; | |
98bd7ca0 | 2644 | char *preamble = stuff; |
b5f904a9 | 2645 | |
c0504523 TL |
2646 | memset (&ds, 0, sizeof ds); |
2647 | ||
2648 | if (u != &dhcp_universe) { | |
2649 | name = u -> name; | |
2650 | dot = "."; | |
2651 | } else { | |
2652 | name = ""; | |
2653 | dot = ""; | |
2654 | } | |
66e9cecf | 2655 | if (evaluate_option_cache (&ds, packet, lease, client_state, |
c0504523 | 2656 | in_options, cfg_options, scope, oc, MDL)) { |
98bd7ca0 DH |
2657 | fprintf(leaseFile, "%soption %s%s%s %s;\n", preamble, |
2658 | name, dot, oc->option->name, | |
2659 | pretty_print_option(oc->option, ds.data, ds.len, | |
2660 | 1, 1)); | |
c0504523 TL |
2661 | data_string_forget (&ds, MDL); |
2662 | } | |
2663 | } | |
2664 | ||
98bd7ca0 DH |
2665 | /* Write an option cache to the lease store. */ |
2666 | static void | |
2667 | write_options(struct client_state *client, struct option_state *options, | |
06eb8bab | 2668 | const char *preamble) |
98bd7ca0 DH |
2669 | { |
2670 | int i; | |
2671 | ||
2672 | for (i = 0; i < options->universe_count; i++) { | |
2673 | option_space_foreach(NULL, NULL, client, NULL, options, | |
3dbe2246 | 2674 | &global_scope, universes[i], |
06eb8bab | 2675 | (char *)preamble, write_lease_option); |
98bd7ca0 DH |
2676 | } |
2677 | } | |
2678 | ||
2679 | /* Write the default DUID to the lease store. */ | |
2680 | static isc_result_t | |
2681 | write_duid(struct data_string *duid) | |
2682 | { | |
2683 | char *str; | |
2684 | int stat; | |
2685 | ||
2686 | if ((duid == NULL) || (duid->len <= 2)) | |
98bf1607 | 2687 | return DHCP_R_INVALIDARG; |
98bd7ca0 DH |
2688 | |
2689 | if (leaseFile == NULL) { /* XXX? */ | |
2690 | leaseFile = fopen(path_dhclient_db, "w"); | |
2691 | if (leaseFile == NULL) { | |
2692 | log_error("can't create %s: %m", path_dhclient_db); | |
2693 | return ISC_R_IOERROR; | |
2694 | } | |
2695 | } | |
2696 | ||
2697 | /* It would make more sense to write this as a hex string, | |
2698 | * but our function to do that (print_hex_n) uses a fixed | |
2699 | * length buffer...and we can't guarantee a duid would be | |
2700 | * less than the fixed length. | |
2701 | */ | |
2702 | str = quotify_buf(duid->data, duid->len, MDL); | |
2703 | if (str == NULL) | |
2704 | return ISC_R_NOMEMORY; | |
2705 | ||
2706 | stat = fprintf(leaseFile, "default-duid \"%s\";\n", str); | |
2707 | dfree(str, MDL); | |
2708 | if (stat <= 0) | |
2709 | return ISC_R_IOERROR; | |
2710 | ||
2711 | if (fflush(leaseFile) != 0) | |
2712 | return ISC_R_IOERROR; | |
6705543f DH |
2713 | |
2714 | return ISC_R_SUCCESS; | |
98bd7ca0 DH |
2715 | } |
2716 | ||
2717 | /* Write a DHCPv6 lease to the store. */ | |
2718 | isc_result_t | |
2719 | write_client6_lease(struct client_state *client, struct dhc6_lease *lease, | |
2720 | int rewrite, int sync) | |
2721 | { | |
2722 | struct dhc6_ia *ia; | |
2723 | struct dhc6_addr *addr; | |
2724 | int stat; | |
1d9774ab | 2725 | const char *ianame; |
98bd7ca0 DH |
2726 | |
2727 | /* This should include the current lease. */ | |
2728 | if (!rewrite && (leases_written++ > 20)) { | |
2729 | rewrite_client_leases(); | |
2730 | leases_written = 0; | |
2731 | return ISC_R_SUCCESS; | |
2732 | } | |
2733 | ||
2734 | if (client == NULL || lease == NULL) | |
98bf1607 | 2735 | return DHCP_R_INVALIDARG; |
98bd7ca0 DH |
2736 | |
2737 | if (leaseFile == NULL) { /* XXX? */ | |
2738 | leaseFile = fopen(path_dhclient_db, "w"); | |
2739 | if (leaseFile == NULL) { | |
2740 | log_error("can't create %s: %m", path_dhclient_db); | |
2741 | return ISC_R_IOERROR; | |
2742 | } | |
2743 | } | |
2744 | ||
2745 | stat = fprintf(leaseFile, "lease6 {\n"); | |
2746 | if (stat <= 0) | |
2747 | return ISC_R_IOERROR; | |
2748 | ||
2749 | stat = fprintf(leaseFile, " interface \"%s\";\n", | |
2750 | client->interface->name); | |
2751 | if (stat <= 0) | |
2752 | return ISC_R_IOERROR; | |
2753 | ||
2754 | for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { | |
1d9774ab FD |
2755 | switch (ia->ia_type) { |
2756 | case D6O_IA_NA: | |
2757 | default: | |
2758 | ianame = "ia-na"; | |
2759 | break; | |
2760 | case D6O_IA_TA: | |
2761 | ianame = "ia-ta"; | |
2762 | break; | |
2763 | case D6O_IA_PD: | |
2764 | ianame = "ia-pd"; | |
2765 | break; | |
2766 | } | |
2767 | stat = fprintf(leaseFile, " %s %s {\n", | |
2768 | ianame, print_hex_1(4, ia->iaid, 12)); | |
98bd7ca0 DH |
2769 | if (stat <= 0) |
2770 | return ISC_R_IOERROR; | |
2771 | ||
1d9774ab FD |
2772 | if (ia->ia_type != D6O_IA_TA) |
2773 | stat = fprintf(leaseFile, " starts %d;\n" | |
2774 | " renew %u;\n" | |
2775 | " rebind %u;\n", | |
2776 | (int)ia->starts, ia->renew, ia->rebind); | |
2777 | else | |
2778 | stat = fprintf(leaseFile, " starts %d;\n", | |
2779 | (int)ia->starts); | |
98bd7ca0 DH |
2780 | if (stat <= 0) |
2781 | return ISC_R_IOERROR; | |
2782 | ||
2783 | for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { | |
1d9774ab FD |
2784 | if (ia->ia_type != D6O_IA_PD) |
2785 | stat = fprintf(leaseFile, | |
2786 | " iaaddr %s {\n", | |
2787 | piaddr(addr->address)); | |
2788 | else | |
2789 | stat = fprintf(leaseFile, | |
2790 | " iaprefix %s/%d {\n", | |
2791 | piaddr(addr->address), | |
2792 | (int)addr->plen); | |
98bd7ca0 DH |
2793 | if (stat <= 0) |
2794 | return ISC_R_IOERROR; | |
2795 | ||
2796 | stat = fprintf(leaseFile, " starts %d;\n" | |
2797 | " preferred-life %u;\n" | |
2798 | " max-life %u;\n", | |
6705543f | 2799 | (int)addr->starts, addr->preferred_life, |
98bd7ca0 DH |
2800 | addr->max_life); |
2801 | if (stat <= 0) | |
2802 | return ISC_R_IOERROR; | |
2803 | ||
2804 | if (addr->options != NULL) | |
2805 | write_options(client, addr->options, " "); | |
2806 | ||
2807 | stat = fprintf(leaseFile, " }\n"); | |
2808 | if (stat <= 0) | |
2809 | return ISC_R_IOERROR; | |
2810 | } | |
2811 | ||
2812 | if (ia->options != NULL) | |
2813 | write_options(client, ia->options, " "); | |
2814 | ||
2815 | stat = fprintf(leaseFile, " }\n"); | |
2816 | if (stat <= 0) | |
2817 | return ISC_R_IOERROR; | |
2818 | } | |
2819 | ||
cabdb9b1 FD |
2820 | if (lease->released) { |
2821 | stat = fprintf(leaseFile, " released;\n"); | |
2822 | if (stat <= 0) | |
2823 | return ISC_R_IOERROR; | |
2824 | } | |
2825 | ||
98bd7ca0 DH |
2826 | if (lease->options != NULL) |
2827 | write_options(client, lease->options, " "); | |
2828 | ||
2829 | stat = fprintf(leaseFile, "}\n"); | |
2830 | if (stat <= 0) | |
2831 | return ISC_R_IOERROR; | |
2832 | ||
2833 | if (fflush(leaseFile) != 0) | |
2834 | return ISC_R_IOERROR; | |
2835 | ||
2836 | if (sync) { | |
2837 | if (fsync(fileno(leaseFile)) < 0) { | |
2838 | log_error("write_client_lease: fsync(): %m"); | |
2839 | return ISC_R_IOERROR; | |
2840 | } | |
2841 | } | |
6705543f DH |
2842 | |
2843 | return ISC_R_SUCCESS; | |
98bd7ca0 DH |
2844 | } |
2845 | ||
007e3ee4 | 2846 | int write_client_lease (client, lease, rewrite, makesure) |
02d9e453 | 2847 | struct client_state *client; |
cc26de46 | 2848 | struct client_lease *lease; |
5c2f78b4 | 2849 | int rewrite; |
007e3ee4 | 2850 | int makesure; |
cc26de46 | 2851 | { |
02a015fb | 2852 | struct data_string ds; |
007e3ee4 | 2853 | int errors = 0; |
dd215f30 | 2854 | char *s; |
5e864416 | 2855 | const char *tval; |
5c2f78b4 TL |
2856 | |
2857 | if (!rewrite) { | |
2858 | if (leases_written++ > 20) { | |
2859 | rewrite_client_leases (); | |
2860 | leases_written = 0; | |
2861 | } | |
2862 | } | |
cc26de46 | 2863 | |
b00d3884 TL |
2864 | /* If the lease came from the config file, we don't need to stash |
2865 | a copy in the lease database. */ | |
2866 | if (lease -> is_static) | |
007e3ee4 | 2867 | return 1; |
b00d3884 | 2868 | |
98bd7ca0 | 2869 | if (leaseFile == NULL) { /* XXX */ |
cc26de46 | 2870 | leaseFile = fopen (path_dhclient_db, "w"); |
98bd7ca0 | 2871 | if (leaseFile == NULL) { |
ee8a3653 TL |
2872 | log_error ("can't create %s: %m", path_dhclient_db); |
2873 | return 0; | |
2874 | } | |
cc26de46 TL |
2875 | } |
2876 | ||
007e3ee4 | 2877 | errno = 0; |
cc26de46 | 2878 | fprintf (leaseFile, "lease {\n"); |
dd215f30 | 2879 | if (lease -> is_bootp) { |
66f973e4 | 2880 | fprintf (leaseFile, " bootp;\n"); |
dd215f30 TL |
2881 | if (errno) { |
2882 | ++errors; | |
2883 | errno = 0; | |
2884 | } | |
2885 | } | |
02d9e453 TL |
2886 | fprintf (leaseFile, " interface \"%s\";\n", |
2887 | client -> interface -> name); | |
dd215f30 TL |
2888 | if (errno) { |
2889 | ++errors; | |
2890 | errno = 0; | |
2891 | } | |
2892 | if (client -> name) { | |
02d9e453 | 2893 | fprintf (leaseFile, " name \"%s\";\n", client -> name); |
dd215f30 TL |
2894 | if (errno) { |
2895 | ++errors; | |
2896 | errno = 0; | |
2897 | } | |
2898 | } | |
cc26de46 TL |
2899 | fprintf (leaseFile, " fixed-address %s;\n", |
2900 | piaddr (lease -> address)); | |
dd215f30 TL |
2901 | if (errno) { |
2902 | ++errors; | |
2903 | errno = 0; | |
2904 | } | |
2905 | if (lease -> filename) { | |
2906 | s = quotify_string (lease -> filename, MDL); | |
2907 | if (s) { | |
2908 | fprintf (leaseFile, " filename \"%s\";\n", s); | |
2909 | if (errno) { | |
2910 | ++errors; | |
2911 | errno = 0; | |
2912 | } | |
2913 | dfree (s, MDL); | |
2914 | } else | |
2915 | errors++; | |
2916 | ||
2917 | } | |
23e10d37 DH |
2918 | if (lease->server_name != NULL) { |
2919 | s = quotify_string(lease->server_name, MDL); | |
2920 | if (s != NULL) { | |
2921 | fprintf(leaseFile, " server-name \"%s\";\n", s); | |
dd215f30 TL |
2922 | if (errno) { |
2923 | ++errors; | |
2924 | errno = 0; | |
2925 | } | |
23e10d37 | 2926 | dfree(s, MDL); |
dd215f30 TL |
2927 | } else |
2928 | ++errors; | |
2929 | } | |
2930 | if (lease -> medium) { | |
2931 | s = quotify_string (lease -> medium -> string, MDL); | |
2932 | if (s) { | |
2933 | fprintf (leaseFile, " medium \"%s\";\n", s); | |
2934 | if (errno) { | |
2935 | ++errors; | |
2936 | errno = 0; | |
2937 | } | |
2938 | dfree (s, MDL); | |
2939 | } else | |
2940 | errors++; | |
2941 | } | |
007e3ee4 TL |
2942 | if (errno != 0) { |
2943 | errors++; | |
2944 | errno = 0; | |
2945 | } | |
2455808f TL |
2946 | |
2947 | memset (&ds, 0, sizeof ds); | |
230e73e4 | 2948 | |
98bd7ca0 | 2949 | write_options(client, lease->options, " "); |
5c2f78b4 | 2950 | |
5e864416 DH |
2951 | tval = print_time(lease->renewal); |
2952 | if (tval == NULL || | |
2953 | fprintf(leaseFile, " renew %s\n", tval) < 0) | |
dd215f30 | 2954 | errors++; |
5e864416 DH |
2955 | |
2956 | tval = print_time(lease->rebind); | |
2957 | if (tval == NULL || | |
2958 | fprintf(leaseFile, " rebind %s\n", tval) < 0) | |
dd215f30 | 2959 | errors++; |
5e864416 DH |
2960 | |
2961 | tval = print_time(lease->expiry); | |
2962 | if (tval == NULL || | |
2963 | fprintf(leaseFile, " expire %s\n", tval) < 0) | |
dd215f30 | 2964 | errors++; |
5e864416 DH |
2965 | |
2966 | if (fprintf(leaseFile, "}\n") < 0) | |
dd215f30 | 2967 | errors++; |
5e864416 DH |
2968 | |
2969 | if (fflush(leaseFile) != 0) | |
007e3ee4 | 2970 | errors++; |
5e864416 | 2971 | |
007e3ee4 TL |
2972 | if (!errors && makesure) { |
2973 | if (fsync (fileno (leaseFile)) < 0) { | |
2974 | log_info ("write_client_lease: %m"); | |
2975 | return 0; | |
2976 | } | |
2977 | } | |
2978 | return errors ? 0 : 1; | |
cc26de46 TL |
2979 | } |
2980 | ||
2981 | /* Variables holding name of script and file pointer for writing to | |
2982 | script. Needless to say, this is not reentrant - only one script | |
2983 | can be invoked at a time. */ | |
2984 | char scriptName [256]; | |
2985 | FILE *scriptFile; | |
2986 | ||
02d9e453 TL |
2987 | void script_init (client, reason, medium) |
2988 | struct client_state *client; | |
b1b7b521 | 2989 | const char *reason; |
9bdb9271 | 2990 | struct string_list *medium; |
cc26de46 | 2991 | { |
32a47563 | 2992 | struct string_list *sl, *next; |
cc26de46 | 2993 | |
02d9e453 | 2994 | if (client) { |
32a47563 TL |
2995 | for (sl = client -> env; sl; sl = next) { |
2996 | next = sl -> next; | |
2997 | dfree (sl, MDL); | |
2998 | } | |
2999 | client -> env = (struct string_list *)0; | |
3000 | client -> envc = 0; | |
3dbe2246 | 3001 | |
02d9e453 | 3002 | if (client -> interface) { |
32a47563 TL |
3003 | client_envadd (client, "", "interface", "%s", |
3004 | client -> interface -> name); | |
02d9e453 TL |
3005 | } |
3006 | if (client -> name) | |
32a47563 TL |
3007 | client_envadd (client, |
3008 | "", "client", "%s", client -> name); | |
3009 | if (medium) | |
3010 | client_envadd (client, | |
3011 | "", "medium", "%s", medium -> string); | |
3012 | ||
3013 | client_envadd (client, "", "reason", "%s", reason); | |
6f7081bd | 3014 | client_envadd (client, "", "pid", "%ld", (long int)getpid ()); |
9bdb9271 | 3015 | } |
cc26de46 TL |
3016 | } |
3017 | ||
c0504523 TL |
3018 | void client_option_envadd (struct option_cache *oc, |
3019 | struct packet *packet, struct lease *lease, | |
66e9cecf | 3020 | struct client_state *client_state, |
c0504523 TL |
3021 | struct option_state *in_options, |
3022 | struct option_state *cfg_options, | |
3023 | struct binding_scope **scope, | |
3024 | struct universe *u, void *stuff) | |
3025 | { | |
3026 | struct envadd_state *es = stuff; | |
3027 | struct data_string data; | |
3028 | memset (&data, 0, sizeof data); | |
3029 | ||
66e9cecf | 3030 | if (evaluate_option_cache (&data, packet, lease, client_state, |
c0504523 TL |
3031 | in_options, cfg_options, scope, oc, MDL)) { |
3032 | if (data.len) { | |
3033 | char name [256]; | |
3034 | if (dhcp_option_ev_name (name, sizeof name, | |
3035 | oc -> option)) { | |
3036 | client_envadd (es -> client, es -> prefix, | |
3037 | name, "%s", | |
3038 | (pretty_print_option | |
b5f904a9 | 3039 | (oc -> option, |
c0504523 TL |
3040 | data.data, data.len, |
3041 | 0, 0))); | |
3042 | data_string_forget (&data, MDL); | |
3043 | } | |
3044 | } | |
3045 | } | |
3046 | } | |
3047 | ||
02d9e453 TL |
3048 | void script_write_params (client, prefix, lease) |
3049 | struct client_state *client; | |
b1b7b521 | 3050 | const char *prefix; |
cc26de46 TL |
3051 | struct client_lease *lease; |
3052 | { | |
3053 | int i; | |
02a015fb TL |
3054 | struct data_string data; |
3055 | struct option_cache *oc; | |
c0504523 TL |
3056 | struct envadd_state es; |
3057 | ||
3058 | es.client = client; | |
3059 | es.prefix = prefix; | |
cc26de46 | 3060 | |
32a47563 TL |
3061 | client_envadd (client, |
3062 | prefix, "ip_address", "%s", piaddr (lease -> address)); | |
c6abd205 TL |
3063 | |
3064 | /* For the benefit of Linux (and operating systems which may | |
3065 | have similar needs), compute the network address based on | |
3066 | the supplied ip address and netmask, if provided. Also | |
3067 | compute the broadcast address (the host address all ones | |
3068 | broadcast address, not the host address all zeroes | |
3069 | broadcast address). */ | |
3070 | ||
02a015fb | 3071 | memset (&data, 0, sizeof data); |
230e73e4 | 3072 | oc = lookup_option (&dhcp_universe, lease -> options, DHO_SUBNET_MASK); |
66e9cecf TL |
3073 | if (oc && evaluate_option_cache (&data, (struct packet *)0, |
3074 | (struct lease *)0, client, | |
0852a27f | 3075 | (struct option_state *)0, |
da38df14 | 3076 | lease -> options, |
cf78bf20 | 3077 | &global_scope, oc, MDL)) { |
02a015fb TL |
3078 | if (data.len > 3) { |
3079 | struct iaddr netmask, subnet, broadcast; | |
3080 | ||
8a4e543b DH |
3081 | /* |
3082 | * No matter the length of the subnet-mask option, | |
3083 | * use only the first four octets. Note that | |
3084 | * subnet-mask options longer than 4 octets are not | |
3085 | * in conformance with RFC 2132, but servers with this | |
3086 | * flaw do exist. | |
3087 | */ | |
3088 | memcpy(netmask.iabuf, data.data, 4); | |
3089 | netmask.len = 4; | |
cf78bf20 | 3090 | data_string_forget (&data, MDL); |
02a015fb TL |
3091 | |
3092 | subnet = subnet_number (lease -> address, netmask); | |
3093 | if (subnet.len) { | |
32a47563 TL |
3094 | client_envadd (client, prefix, "network_number", |
3095 | "%s", piaddr (subnet)); | |
3096 | ||
3097 | oc = lookup_option (&dhcp_universe, | |
3098 | lease -> options, | |
3099 | DHO_BROADCAST_ADDRESS); | |
3100 | if (!oc || | |
3101 | !(evaluate_option_cache | |
3102 | (&data, (struct packet *)0, | |
66e9cecf | 3103 | (struct lease *)0, client, |
32a47563 TL |
3104 | (struct option_state *)0, |
3105 | lease -> options, | |
3106 | &global_scope, oc, MDL))) { | |
3107 | broadcast = broadcast_addr (subnet, netmask); | |
3108 | if (broadcast.len) { | |
3109 | client_envadd (client, | |
3110 | prefix, "broadcast_address", | |
a8c190df | 3111 | "%s", piaddr (broadcast)); |
c6abd205 | 3112 | } |
32a47563 | 3113 | } |
c6abd205 TL |
3114 | } |
3115 | } | |
cf78bf20 | 3116 | data_string_forget (&data, MDL); |
c6abd205 TL |
3117 | } |
3118 | ||
32a47563 TL |
3119 | if (lease -> filename) |
3120 | client_envadd (client, | |
3121 | prefix, "filename", "%s", lease -> filename); | |
3122 | if (lease -> server_name) | |
3123 | client_envadd (client, prefix, "server_name", | |
3124 | "%s", lease -> server_name); | |
02a015fb | 3125 | |
c0504523 TL |
3126 | for (i = 0; i < lease -> options -> universe_count; i++) { |
3127 | option_space_foreach ((struct packet *)0, (struct lease *)0, | |
66e9cecf | 3128 | client, (struct option_state *)0, |
c0504523 TL |
3129 | lease -> options, &global_scope, |
3130 | universes [i], | |
3131 | &es, client_option_envadd); | |
cc26de46 | 3132 | } |
32a47563 | 3133 | client_envadd (client, prefix, "expiry", "%d", (int)(lease -> expiry)); |
cc26de46 TL |
3134 | } |
3135 | ||
02d9e453 TL |
3136 | int script_go (client) |
3137 | struct client_state *client; | |
cc26de46 | 3138 | { |
32a47563 TL |
3139 | char *scriptName; |
3140 | char *argv [2]; | |
3141 | char **envp; | |
32a47563 TL |
3142 | char reason [] = "REASON=NBI"; |
3143 | static char client_path [] = CLIENT_PATH; | |
3144 | int i; | |
3145 | struct string_list *sp, *next; | |
3146 | int pid, wpid, wstatus; | |
cc26de46 | 3147 | |
b1423aed | 3148 | if (client) |
32a47563 | 3149 | scriptName = client -> config -> script_name; |
b1423aed TL |
3150 | else |
3151 | scriptName = top_level_config.script_name; | |
3152 | ||
3153 | envp = dmalloc (((client ? client -> envc : 2) + | |
3154 | client_env_count + 2) * sizeof (char *), MDL); | |
3155 | if (!envp) { | |
3156 | log_error ("No memory for client script environment."); | |
3157 | return 0; | |
3158 | } | |
3159 | i = 0; | |
3160 | /* Copy out the environment specified on the command line, | |
3161 | if any. */ | |
3162 | for (sp = client_env; sp; sp = sp -> next) { | |
3163 | envp [i++] = sp -> string; | |
3164 | } | |
3165 | /* Copy out the environment specified by dhclient. */ | |
3166 | if (client) { | |
32a47563 TL |
3167 | for (sp = client -> env; sp; sp = sp -> next) { |
3168 | envp [i++] = sp -> string; | |
3169 | } | |
32a47563 | 3170 | } else { |
b1423aed | 3171 | envp [i++] = reason; |
32a47563 | 3172 | } |
b1423aed TL |
3173 | /* Set $PATH. */ |
3174 | envp [i++] = client_path; | |
3175 | envp [i] = (char *)0; | |
32a47563 TL |
3176 | |
3177 | argv [0] = scriptName; | |
3178 | argv [1] = (char *)0; | |
3179 | ||
3180 | pid = fork (); | |
3181 | if (pid < 0) { | |
3182 | log_error ("fork: %m"); | |
3183 | wstatus = 0; | |
3184 | } else if (pid) { | |
3185 | do { | |
3186 | wpid = wait (&wstatus); | |
3187 | } while (wpid != pid && wpid > 0); | |
3188 | if (wpid < 0) { | |
3189 | log_error ("wait: %m"); | |
3190 | wstatus = 0; | |
3191 | } | |
3192 | } else { | |
4d97a043 DH |
3193 | /* We don't want to pass an open file descriptor for |
3194 | * dhclient.leases when executing dhclient-script. | |
3195 | */ | |
3196 | if (leaseFile != NULL) | |
3197 | fclose(leaseFile); | |
32a47563 TL |
3198 | execve (scriptName, argv, envp); |
3199 | log_error ("execve (%s, ...): %m", scriptName); | |
3200 | exit (0); | |
3201 | } | |
3202 | ||
3203 | if (client) { | |
3204 | for (sp = client -> env; sp; sp = next) { | |
3205 | next = sp -> next; | |
3206 | dfree (sp, MDL); | |
3207 | } | |
3208 | client -> env = (struct string_list *)0; | |
3209 | client -> envc = 0; | |
32a47563 | 3210 | } |
b1423aed | 3211 | dfree (envp, MDL); |
be62cf06 | 3212 | gettimeofday(&cur_tv, NULL); |
ed0f73ab TL |
3213 | return (WIFEXITED (wstatus) ? |
3214 | WEXITSTATUS (wstatus) : -WTERMSIG (wstatus)); | |
32a47563 TL |
3215 | } |
3216 | ||
3217 | void client_envadd (struct client_state *client, | |
3218 | const char *prefix, const char *name, const char *fmt, ...) | |
3219 | { | |
3220 | char spbuf [1024]; | |
3221 | char *s; | |
28868515 | 3222 | unsigned len; |
32a47563 TL |
3223 | struct string_list *val; |
3224 | va_list list; | |
3225 | ||
3226 | va_start (list, fmt); | |
3227 | len = vsnprintf (spbuf, sizeof spbuf, fmt, list); | |
3228 | va_end (list); | |
3229 | ||
3230 | val = dmalloc (strlen (prefix) + strlen (name) + 1 /* = */ + | |
3231 | len + sizeof *val, MDL); | |
3232 | if (!val) | |
3233 | return; | |
3234 | s = val -> string; | |
3235 | strcpy (s, prefix); | |
3236 | strcat (s, name); | |
3237 | s += strlen (s); | |
3238 | *s++ = '='; | |
3239 | if (len >= sizeof spbuf) { | |
3240 | va_start (list, fmt); | |
3241 | vsnprintf (s, len + 1, fmt, list); | |
3242 | va_end (list); | |
3243 | } else | |
3244 | strcpy (s, spbuf); | |
3245 | val -> next = client -> env; | |
3246 | client -> env = val; | |
3247 | client -> envc++; | |
cc26de46 TL |
3248 | } |
3249 | ||
32a47563 TL |
3250 | int dhcp_option_ev_name (buf, buflen, option) |
3251 | char *buf; | |
6ceb9118 | 3252 | size_t buflen; |
cc26de46 TL |
3253 | struct option *option; |
3254 | { | |
c0504523 TL |
3255 | int i, j; |
3256 | const char *s; | |
cc26de46 | 3257 | |
c0504523 TL |
3258 | j = 0; |
3259 | if (option -> universe != &dhcp_universe) { | |
3260 | s = option -> universe -> name; | |
3261 | i = 0; | |
3dbe2246 | 3262 | } else { |
c0504523 TL |
3263 | s = option -> name; |
3264 | i = 1; | |
cc26de46 TL |
3265 | } |
3266 | ||
c0504523 TL |
3267 | do { |
3268 | while (*s) { | |
3269 | if (j + 1 == buflen) | |
3270 | return 0; | |
3271 | if (*s == '-') | |
3272 | buf [j++] = '_'; | |
3273 | else | |
3274 | buf [j++] = *s; | |
3275 | ++s; | |
3276 | } | |
3277 | if (!i) { | |
3278 | s = option -> name; | |
3279 | if (j + 1 == buflen) | |
3280 | return 0; | |
3281 | buf [j++] = '_'; | |
3282 | } | |
3283 | ++i; | |
3284 | } while (i != 2); | |
3285 | ||
3286 | buf [j] = 0; | |
32a47563 | 3287 | return 1; |
cc26de46 | 3288 | } |
b00d3884 TL |
3289 | |
3290 | void go_daemon () | |
3291 | { | |
3292 | static int state = 0; | |
3293 | int pid; | |
3294 | ||
3295 | /* Don't become a daemon if the user requested otherwise. */ | |
b8cf055d TL |
3296 | if (no_daemon) { |
3297 | write_client_pid_file (); | |
b00d3884 | 3298 | return; |
b8cf055d | 3299 | } |
b00d3884 TL |
3300 | |
3301 | /* Only do it once. */ | |
3302 | if (state) | |
3303 | return; | |
3304 | state = 1; | |
3305 | ||
3306 | /* Stop logging to stderr... */ | |
3307 | log_perror = 0; | |
3308 | ||
3309 | /* Become a daemon... */ | |
3310 | if ((pid = fork ()) < 0) | |
8ae2d595 | 3311 | log_fatal ("Can't fork daemon: %m"); |
b00d3884 TL |
3312 | else if (pid) |
3313 | exit (0); | |
3314 | /* Become session leader and get pid... */ | |
3315 | pid = setsid (); | |
b8cf055d | 3316 | |
62d0cb47 | 3317 | /* Close standard I/O descriptors. */ |
3dbe2246 FD |
3318 | close(0); |
3319 | close(1); | |
3320 | close(2); | |
62d0cb47 | 3321 | |
a609e69b | 3322 | /* Reopen them on /dev/null. */ |
509df655 SK |
3323 | open("/dev/null", O_RDWR); |
3324 | open("/dev/null", O_RDWR); | |
3325 | open("/dev/null", O_RDWR); | |
a609e69b | 3326 | |
b8cf055d | 3327 | write_client_pid_file (); |
a546f2a7 | 3328 | |
ae566556 | 3329 | IGNORE_RET (chdir("/")); |
b8cf055d TL |
3330 | } |
3331 | ||
3332 | void write_client_pid_file () | |
3333 | { | |
3334 | FILE *pf; | |
3335 | int pfdesc; | |
3336 | ||
3337 | pfdesc = open (path_dhclient_pid, O_CREAT | O_TRUNC | O_WRONLY, 0644); | |
3338 | ||
3339 | if (pfdesc < 0) { | |
8ae2d595 | 3340 | log_error ("Can't create %s: %m", path_dhclient_pid); |
b8cf055d TL |
3341 | return; |
3342 | } | |
3343 | ||
3344 | pf = fdopen (pfdesc, "w"); | |
3345 | if (!pf) | |
8ae2d595 | 3346 | log_error ("Can't fdopen %s: %m", path_dhclient_pid); |
b8cf055d | 3347 | else { |
19ea90f7 | 3348 | fprintf (pf, "%ld\n", (long)getpid ()); |
b8cf055d TL |
3349 | fclose (pf); |
3350 | } | |
3351 | } | |
3352 | ||
b8cf055d TL |
3353 | void client_location_changed () |
3354 | { | |
3355 | struct interface_info *ip; | |
02d9e453 | 3356 | struct client_state *client; |
b8cf055d TL |
3357 | |
3358 | for (ip = interfaces; ip; ip = ip -> next) { | |
02d9e453 TL |
3359 | for (client = ip -> client; client; client = client -> next) { |
3360 | switch (client -> state) { | |
3361 | case S_SELECTING: | |
3362 | cancel_timeout (send_discover, client); | |
3363 | break; | |
b8cf055d | 3364 | |
02d9e453 TL |
3365 | case S_BOUND: |
3366 | cancel_timeout (state_bound, client); | |
3367 | break; | |
b8cf055d | 3368 | |
02d9e453 TL |
3369 | case S_REBOOTING: |
3370 | case S_REQUESTING: | |
3371 | case S_RENEWING: | |
3372 | cancel_timeout (send_request, client); | |
3373 | break; | |
b8cf055d | 3374 | |
02d9e453 TL |
3375 | case S_INIT: |
3376 | case S_REBINDING: | |
57710b89 | 3377 | case S_STOPPED: |
02d9e453 TL |
3378 | break; |
3379 | } | |
3380 | client -> state = S_INIT; | |
3381 | state_reboot (client); | |
b8cf055d | 3382 | } |
b8cf055d TL |
3383 | } |
3384 | } | |
6767b592 | 3385 | |
3dbe2246 | 3386 | void do_release(client) |
347de8bd TL |
3387 | struct client_state *client; |
3388 | { | |
007e3ee4 TL |
3389 | struct data_string ds; |
3390 | struct option_cache *oc; | |
3391 | ||
57710b89 | 3392 | /* Pick a random xid. */ |
347de8bd TL |
3393 | client -> xid = random (); |
3394 | ||
bdcaf7b9 TL |
3395 | /* is there even a lease to release? */ |
3396 | if (client -> active) { | |
3397 | /* Make a DHCPRELEASE packet, and set appropriate per-interface | |
3398 | flags. */ | |
3399 | make_release (client, client -> active); | |
007e3ee4 TL |
3400 | |
3401 | memset (&ds, 0, sizeof ds); | |
3402 | oc = lookup_option (&dhcp_universe, | |
3403 | client -> active -> options, | |
3404 | DHO_DHCP_SERVER_IDENTIFIER); | |
3405 | if (oc && | |
3406 | evaluate_option_cache (&ds, (struct packet *)0, | |
66e9cecf | 3407 | (struct lease *)0, client, |
007e3ee4 TL |
3408 | (struct option_state *)0, |
3409 | client -> active -> options, | |
3410 | &global_scope, oc, MDL)) { | |
3411 | if (ds.len > 3) { | |
3412 | memcpy (client -> destination.iabuf, | |
3413 | ds.data, 4); | |
3414 | client -> destination.len = 4; | |
3415 | } else | |
3416 | client -> destination = iaddr_broadcast; | |
98311e4b DH |
3417 | |
3418 | data_string_forget (&ds, MDL); | |
007e3ee4 TL |
3419 | } else |
3420 | client -> destination = iaddr_broadcast; | |
bdcaf7b9 TL |
3421 | client -> first_sending = cur_time; |
3422 | client -> interval = client -> config -> initial_interval; | |
3dbe2246 | 3423 | |
bdcaf7b9 TL |
3424 | /* Zap the medium list... */ |
3425 | client -> medium = (struct string_list *)0; | |
3dbe2246 | 3426 | |
bdcaf7b9 TL |
3427 | /* Send out the first and only DHCPRELEASE packet. */ |
3428 | send_release (client); | |
347de8bd | 3429 | |
007e3ee4 | 3430 | /* Do the client script RELEASE operation. */ |
bdcaf7b9 TL |
3431 | script_init (client, |
3432 | "RELEASE", (struct string_list *)0); | |
3433 | if (client -> alias) | |
3434 | script_write_params (client, "alias_", | |
3435 | client -> alias); | |
d758ad8c | 3436 | script_write_params (client, "old_", client -> active); |
bdcaf7b9 TL |
3437 | script_go (client); |
3438 | } | |
007e3ee4 | 3439 | |
57710b89 TL |
3440 | /* Cancel any timeouts. */ |
3441 | cancel_timeout (state_bound, client); | |
3442 | cancel_timeout (send_discover, client); | |
3443 | cancel_timeout (state_init, client); | |
3444 | cancel_timeout (send_request, client); | |
3445 | cancel_timeout (state_reboot, client); | |
3446 | client -> state = S_STOPPED; | |
347de8bd TL |
3447 | } |
3448 | ||
20916cae TL |
3449 | int dhclient_interface_shutdown_hook (struct interface_info *interface) |
3450 | { | |
3451 | do_release (interface -> client); | |
57710b89 | 3452 | |
20916cae TL |
3453 | return 1; |
3454 | } | |
347de8bd | 3455 | |
20916cae TL |
3456 | int dhclient_interface_discovery_hook (struct interface_info *tmp) |
3457 | { | |
3458 | struct interface_info *last, *ip; | |
3459 | /* See if we can find the client from dummy_interfaces */ | |
3460 | last = 0; | |
3461 | for (ip = dummy_interfaces; ip; ip = ip -> next) { | |
3462 | if (!strcmp (ip -> name, tmp -> name)) { | |
3463 | /* Remove from dummy_interfaces */ | |
3464 | if (last) { | |
3465 | ip = (struct interface_info *)0; | |
3466 | interface_reference (&ip, last -> next, MDL); | |
3467 | interface_dereference (&last -> next, MDL); | |
3468 | if (ip -> next) { | |
3469 | interface_reference (&last -> next, | |
3470 | ip -> next, MDL); | |
3471 | interface_dereference (&ip -> next, | |
3472 | MDL); | |
3473 | } | |
3474 | } else { | |
3475 | ip = (struct interface_info *)0; | |
3476 | interface_reference (&ip, | |
3477 | dummy_interfaces, MDL); | |
3478 | interface_dereference (&dummy_interfaces, MDL); | |
3479 | if (ip -> next) { | |
3480 | interface_reference (&dummy_interfaces, | |
3481 | ip -> next, MDL); | |
3482 | interface_dereference (&ip -> next, | |
3483 | MDL); | |
3484 | } | |
3485 | } | |
3486 | /* Copy "client" to tmp */ | |
3487 | if (ip -> client) { | |
3488 | tmp -> client = ip -> client; | |
3489 | tmp -> client -> interface = tmp; | |
3490 | } | |
3491 | interface_dereference (&ip, MDL); | |
3492 | break; | |
3493 | } | |
3494 | last = ip; | |
3495 | } | |
3496 | return 1; | |
3497 | } | |
347de8bd | 3498 | |
57710b89 TL |
3499 | isc_result_t dhclient_interface_startup_hook (struct interface_info *interface) |
3500 | { | |
3501 | struct interface_info *ip; | |
3502 | struct client_state *client; | |
be62cf06 | 3503 | struct timeval tv; |
57710b89 TL |
3504 | |
3505 | /* This code needs some rethinking. It doesn't test against | |
3506 | a signal name, and it just kind of bulls into doing something | |
3507 | that may or may not be appropriate. */ | |
3508 | ||
3509 | if (interfaces) { | |
3510 | interface_reference (&interface -> next, interfaces, MDL); | |
3511 | interface_dereference (&interfaces, MDL); | |
3512 | } | |
3513 | interface_reference (&interfaces, interface, MDL); | |
3514 | ||
3515 | discover_interfaces (DISCOVER_UNCONFIGURED); | |
3516 | ||
3517 | for (ip = interfaces; ip; ip = ip -> next) { | |
3518 | /* If interfaces were specified, don't configure | |
3519 | interfaces that weren't specified! */ | |
3520 | if (ip -> flags & INTERFACE_RUNNING || | |
3521 | (ip -> flags & (INTERFACE_REQUESTED | | |
3522 | INTERFACE_AUTOMATIC)) != | |
3523 | INTERFACE_REQUESTED) | |
3524 | continue; | |
3525 | script_init (ip -> client, | |
3526 | "PREINIT", (struct string_list *)0); | |
3527 | if (ip -> client -> alias) | |
3528 | script_write_params (ip -> client, "alias_", | |
3529 | ip -> client -> alias); | |
3530 | script_go (ip -> client); | |
3531 | } | |
3dbe2246 FD |
3532 | |
3533 | discover_interfaces (interfaces_requested != 0 | |
57710b89 TL |
3534 | ? DISCOVER_REQUESTED |
3535 | : DISCOVER_RUNNING); | |
3536 | ||
3537 | for (ip = interfaces; ip; ip = ip -> next) { | |
3538 | if (ip -> flags & INTERFACE_RUNNING) | |
3539 | continue; | |
3540 | ip -> flags |= INTERFACE_RUNNING; | |
3541 | for (client = ip -> client; client; client = client -> next) { | |
3542 | client -> state = S_INIT; | |
3543 | /* Set up a timeout to start the initialization | |
3544 | process. */ | |
be62cf06 FD |
3545 | tv . tv_sec = cur_time + random () % 5; |
3546 | tv . tv_usec = 0; | |
3547 | add_timeout (&tv, state_reboot, client, 0, 0); | |
57710b89 TL |
3548 | } |
3549 | } | |
3550 | return ISC_R_SUCCESS; | |
3551 | } | |
3552 | ||
6767b592 TL |
3553 | /* The client should never receive a relay agent information option, |
3554 | so if it does, log it and discard it. */ | |
3555 | ||
3556 | int parse_agent_information_option (packet, len, data) | |
3557 | struct packet *packet; | |
3558 | int len; | |
3559 | u_int8_t *data; | |
3560 | { | |
6767b592 TL |
3561 | return 1; |
3562 | } | |
3563 | ||
3564 | /* The client never sends relay agent information options. */ | |
3565 | ||
3566 | unsigned cons_agent_information_options (cfg_options, outpacket, | |
3567 | agentix, length) | |
3568 | struct option_state *cfg_options; | |
3569 | struct dhcp_packet *outpacket; | |
3570 | unsigned agentix; | |
3571 | unsigned length; | |
3572 | { | |
3573 | return length; | |
3574 | } | |
d758ad8c TL |
3575 | |
3576 | static void shutdown_exit (void *foo) | |
3577 | { | |
3578 | exit (0); | |
3579 | } | |
3580 | ||
98bf1607 SR |
3581 | #if defined (NSUPDATE) |
3582 | /* | |
3583 | * If the first query fails, the updater MUST NOT delete the DNS name. It | |
3584 | * may be that the host whose lease on the server has expired has moved | |
3585 | * to another network and obtained a lease from a different server, | |
3586 | * which has caused the client's A RR to be replaced. It may also be | |
3587 | * that some other client has been configured with a name that matches | |
3588 | * the name of the DHCP client, and the policy was that the last client | |
3589 | * to specify the name would get the name. In this case, the DHCID RR | |
3590 | * will no longer match the updater's notion of the client-identity of | |
3591 | * the host pointed to by the DNS name. | |
3592 | * -- "Interaction between DHCP and DNS" | |
3593 | */ | |
3594 | ||
3595 | /* The first and second stages are pretty similar so we combine them */ | |
3596 | void | |
3597 | client_dns_remove_action(dhcp_ddns_cb_t *ddns_cb, | |
3598 | isc_result_t eresult) | |
3599 | { | |
3600 | ||
3601 | isc_result_t result; | |
3602 | ||
3603 | if ((eresult == ISC_R_SUCCESS) && | |
3604 | (ddns_cb->state == DDNS_STATE_REM_FW_YXDHCID)) { | |
3605 | /* Do the second stage of the FWD removal */ | |
3606 | ddns_cb->state = DDNS_STATE_REM_FW_NXRR; | |
3607 | ||
3608 | result = ddns_modify_fwd(ddns_cb); | |
3609 | if (result == ISC_R_SUCCESS) { | |
3610 | return; | |
3611 | } | |
3612 | } | |
3613 | ||
3614 | /* If we are done or have an error clean up */ | |
3615 | ddns_cb_free(ddns_cb, MDL); | |
3616 | return; | |
3617 | } | |
3618 | ||
3619 | void | |
3620 | client_dns_remove(struct client_state *client, | |
3621 | struct iaddr *addr) | |
3622 | { | |
3623 | dhcp_ddns_cb_t *ddns_cb; | |
3624 | isc_result_t result; | |
3625 | ||
3626 | /* if we have an old ddns request for this client, cancel it */ | |
3627 | if (client->ddns_cb != NULL) { | |
3628 | ddns_cancel(client->ddns_cb); | |
3629 | client->ddns_cb = NULL; | |
3630 | } | |
3631 | ||
3632 | ddns_cb = ddns_cb_alloc(MDL); | |
3633 | if (ddns_cb != NULL) { | |
3634 | ddns_cb->address = *addr; | |
3635 | ddns_cb->timeout = 0; | |
3636 | ||
3637 | ddns_cb->state = DDNS_STATE_REM_FW_YXDHCID; | |
3638 | ddns_cb->flags = DDNS_UPDATE_ADDR; | |
3639 | ddns_cb->cur_func = client_dns_remove_action; | |
3640 | ||
3641 | result = client_dns_update(client, ddns_cb); | |
3642 | ||
3643 | if (result != ISC_R_TIMEDOUT) { | |
3644 | ddns_cb_free(ddns_cb, MDL); | |
3645 | } | |
3646 | } | |
3647 | } | |
3648 | #endif | |
3649 | ||
d758ad8c TL |
3650 | isc_result_t dhcp_set_control_state (control_object_state_t oldstate, |
3651 | control_object_state_t newstate) | |
3652 | { | |
3653 | struct interface_info *ip; | |
3654 | struct client_state *client; | |
be62cf06 | 3655 | struct timeval tv; |
d758ad8c TL |
3656 | |
3657 | /* Do the right thing for each interface. */ | |
3658 | for (ip = interfaces; ip; ip = ip -> next) { | |
3659 | for (client = ip -> client; client; client = client -> next) { | |
3660 | switch (newstate) { | |
3661 | case server_startup: | |
3662 | return ISC_R_SUCCESS; | |
3663 | ||
3664 | case server_running: | |
3665 | return ISC_R_SUCCESS; | |
3666 | ||
3667 | case server_shutdown: | |
3668 | if (client -> active && | |
3669 | client -> active -> expiry > cur_time) { | |
98bf1607 SR |
3670 | #if defined (NSUPDATE) |
3671 | if (client->config->do_forward_update) { | |
3672 | client_dns_remove(client, | |
3673 | &client->active->address); | |
3674 | } | |
3675 | #endif | |
d758ad8c TL |
3676 | do_release (client); |
3677 | } | |
3678 | break; | |
3679 | ||
3680 | case server_hibernate: | |
3681 | state_stop (client); | |
3682 | break; | |
3683 | ||
3684 | case server_awaken: | |
3685 | state_reboot (client); | |
3686 | break; | |
3687 | } | |
3688 | } | |
3689 | } | |
af5fa176 | 3690 | |
be62cf06 FD |
3691 | if (newstate == server_shutdown) { |
3692 | tv . tv_sec = cur_tv . tv_sec + 1; | |
3693 | tv . tv_usec = cur_tv . tv_usec; | |
3694 | add_timeout (&tv, shutdown_exit, 0, 0, 0); | |
3695 | } | |
d758ad8c TL |
3696 | return ISC_R_SUCCESS; |
3697 | } | |
3698 | ||
98bf1607 SR |
3699 | #if defined (NSUPDATE) |
3700 | /* | |
3701 | * Called after a timeout if the DNS update failed on the previous try. | |
3702 | * Starts the retry process. If the retry times out it will schedule | |
3703 | * this routine to run again after a 10x wait. | |
98bd7ca0 DH |
3704 | */ |
3705 | void | |
98bf1607 | 3706 | client_dns_update_timeout (void *cp) |
98bd7ca0 | 3707 | { |
98bf1607 SR |
3708 | dhcp_ddns_cb_t *ddns_cb = (dhcp_ddns_cb_t *)cp; |
3709 | struct client_state *client = (struct client_state *)ddns_cb->lease; | |
3710 | isc_result_t status = ISC_R_FAILURE; | |
98bd7ca0 | 3711 | |
98bf1607 SR |
3712 | if ((client != NULL) && |
3713 | ((client->active != NULL) || | |
3714 | (client->active_lease != NULL))) | |
3715 | status = client_dns_update(client, ddns_cb); | |
98bd7ca0 | 3716 | |
98bf1607 SR |
3717 | /* |
3718 | * A status of timedout indicates that we started the update and | |
3719 | * have released control of the control block. Any other status | |
3720 | * indicates that we should clean up the control block. We either | |
3721 | * got a success which indicates that we didn't really need to | |
3722 | * send an update or some other error in which case we weren't able | |
3723 | * to start the update process. In both cases we still own | |
3724 | * the control block and should free it. | |
3725 | */ | |
3726 | if (status != ISC_R_TIMEDOUT) { | |
3727 | if (client != NULL) { | |
3728 | client->ddns_cb = NULL; | |
3729 | } | |
3730 | ddns_cb_free(ddns_cb, MDL); | |
98bd7ca0 DH |
3731 | } |
3732 | } | |
3733 | ||
98bf1607 SR |
3734 | /* |
3735 | * If the first query succeeds, the updater can conclude that it | |
3736 | * has added a new name whose only RRs are the A and DHCID RR records. | |
3737 | * The A RR update is now complete (and a client updater is finished, | |
3738 | * while a server might proceed to perform a PTR RR update). | |
3739 | * -- "Interaction between DHCP and DNS" | |
3740 | * | |
3741 | * If the second query succeeds, the updater can conclude that the current | |
3742 | * client was the last client associated with the domain name, and that | |
3743 | * the name now contains the updated A RR. The A RR update is now | |
3744 | * complete (and a client updater is finished, while a server would | |
3745 | * then proceed to perform a PTR RR update). | |
3746 | * -- "Interaction between DHCP and DNS" | |
3747 | * | |
3748 | * If the second query fails with NXRRSET, the updater must conclude | |
3749 | * that the client's desired name is in use by another host. At this | |
3750 | * juncture, the updater can decide (based on some administrative | |
3751 | * configuration outside of the scope of this document) whether to let | |
3752 | * the existing owner of the name keep that name, and to (possibly) | |
3753 | * perform some name disambiguation operation on behalf of the current | |
3754 | * client, or to replace the RRs on the name with RRs that represent | |
3755 | * the current client. If the configured policy allows replacement of | |
3756 | * existing records, the updater submits a query that deletes the | |
3757 | * existing A RR and the existing DHCID RR, adding A and DHCID RRs that | |
3758 | * represent the IP address and client-identity of the new client. | |
3759 | * -- "Interaction between DHCP and DNS" | |
3760 | */ | |
98311e4b | 3761 | |
98bf1607 SR |
3762 | /* The first and second stages are pretty similar so we combine them */ |
3763 | void | |
3764 | client_dns_update_action(dhcp_ddns_cb_t *ddns_cb, | |
3765 | isc_result_t eresult) | |
98311e4b | 3766 | { |
98bf1607 | 3767 | isc_result_t result; |
be62cf06 | 3768 | struct timeval tv; |
98311e4b | 3769 | |
98bf1607 SR |
3770 | switch(eresult) { |
3771 | case ISC_R_SUCCESS: | |
3772 | default: | |
3773 | /* Either we succeeded or broke in a bad way, clean up */ | |
3774 | break; | |
3775 | ||
3776 | case DNS_R_YXRRSET: | |
3777 | /* | |
3778 | * This is the only difference between the two stages, | |
3779 | * check to see if it is the first stage, in which case | |
3780 | * start the second stage | |
3781 | */ | |
3782 | if (ddns_cb->state == DDNS_STATE_ADD_FW_NXDOMAIN) { | |
3783 | ddns_cb->state = DDNS_STATE_ADD_FW_YXDHCID; | |
3784 | ddns_cb->cur_func = client_dns_update_action; | |
3785 | ||
3786 | result = ddns_modify_fwd(ddns_cb); | |
3787 | if (result == ISC_R_SUCCESS) { | |
3788 | return; | |
3789 | } | |
3790 | } | |
3791 | break; | |
3792 | ||
3793 | case ISC_R_TIMEDOUT: | |
3794 | /* | |
3795 | * We got a timeout response from the DNS module. Schedule | |
3796 | * another attempt for later. We forget the name, dhcid and | |
3797 | * zone so if it gets changed we will get the new information. | |
3798 | */ | |
3799 | data_string_forget(&ddns_cb->fwd_name, MDL); | |
3800 | data_string_forget(&ddns_cb->dhcid, MDL); | |
3801 | if (ddns_cb->zone != NULL) { | |
3802 | forget_zone((struct dns_zone **)&ddns_cb->zone); | |
3803 | } | |
3804 | ||
3805 | /* Reset to doing the first stage */ | |
3806 | ddns_cb->state = DDNS_STATE_ADD_FW_NXDOMAIN; | |
3807 | ddns_cb->cur_func = client_dns_update_action; | |
3808 | ||
3809 | /* and update our timer */ | |
3810 | if (ddns_cb->timeout < 3600) | |
3811 | ddns_cb->timeout *= 10; | |
3812 | tv.tv_sec = cur_time + ddns_cb->timeout; | |
be62cf06 FD |
3813 | tv.tv_usec = 0; |
3814 | add_timeout(&tv, client_dns_update_timeout, | |
98bf1607 SR |
3815 | ddns_cb, NULL, NULL); |
3816 | return; | |
3817 | } | |
3818 | ||
3819 | ddns_cb_free(ddns_cb, MDL); | |
3820 | return; | |
98311e4b DH |
3821 | } |
3822 | ||
d758ad8c TL |
3823 | /* See if we should do a DNS update, and if so, do it. */ |
3824 | ||
98bf1607 SR |
3825 | isc_result_t |
3826 | client_dns_update(struct client_state *client, dhcp_ddns_cb_t *ddns_cb) | |
d758ad8c | 3827 | { |
98bf1607 | 3828 | struct data_string client_identifier; |
d758ad8c TL |
3829 | struct option_cache *oc; |
3830 | int ignorep; | |
3831 | int result; | |
3832 | isc_result_t rcode; | |
3833 | ||
3834 | /* If we didn't send an FQDN option, we certainly aren't going to | |
3835 | be doing an update. */ | |
3836 | if (!client -> sent_options) | |
98311e4b | 3837 | return ISC_R_SUCCESS; |
d758ad8c TL |
3838 | |
3839 | /* If we don't have a lease, we can't do an update. */ | |
98bd7ca0 | 3840 | if ((client->active == NULL) && (client->active_lease == NULL)) |
98311e4b | 3841 | return ISC_R_SUCCESS; |
d758ad8c TL |
3842 | |
3843 | /* If we set the no client update flag, don't do the update. */ | |
3844 | if ((oc = lookup_option (&fqdn_universe, client -> sent_options, | |
3845 | FQDN_NO_CLIENT_UPDATE)) && | |
3846 | evaluate_boolean_option_cache (&ignorep, (struct packet *)0, | |
3847 | (struct lease *)0, client, | |
3848 | client -> sent_options, | |
3849 | (struct option_state *)0, | |
3850 | &global_scope, oc, MDL)) | |
98311e4b | 3851 | return ISC_R_SUCCESS; |
3dbe2246 | 3852 | |
d758ad8c TL |
3853 | /* If we set the "server, please update" flag, or didn't set it |
3854 | to false, don't do the update. */ | |
3855 | if (!(oc = lookup_option (&fqdn_universe, client -> sent_options, | |
3856 | FQDN_SERVER_UPDATE)) || | |
3857 | evaluate_boolean_option_cache (&ignorep, (struct packet *)0, | |
3858 | (struct lease *)0, client, | |
3859 | client -> sent_options, | |
3860 | (struct option_state *)0, | |
3861 | &global_scope, oc, MDL)) | |
98311e4b | 3862 | return ISC_R_SUCCESS; |
3dbe2246 | 3863 | |
d758ad8c | 3864 | /* If no FQDN option was supplied, don't do the update. */ |
d758ad8c TL |
3865 | if (!(oc = lookup_option (&fqdn_universe, client -> sent_options, |
3866 | FQDN_FQDN)) || | |
98bf1607 | 3867 | !evaluate_option_cache (&ddns_cb->fwd_name, (struct packet *)0, |
d758ad8c TL |
3868 | (struct lease *)0, client, |
3869 | client -> sent_options, | |
3870 | (struct option_state *)0, | |
3871 | &global_scope, oc, MDL)) | |
98311e4b | 3872 | return ISC_R_SUCCESS; |
d758ad8c | 3873 | |
98bd7ca0 DH |
3874 | /* If this is a DHCPv6 client update, make a dhcid string out of |
3875 | * the DUID. If this is a DHCPv4 client update, choose either | |
3876 | * the client identifier, if there is one, or the interface's | |
3877 | * MAC address. | |
3878 | */ | |
6705543f | 3879 | result = 0; |
98bd7ca0 DH |
3880 | memset(&client_identifier, 0, sizeof(client_identifier)); |
3881 | if (client->active_lease != NULL) { | |
3882 | if (((oc = | |
3883 | lookup_option(&dhcpv6_universe, client->sent_options, | |
3884 | D6O_CLIENTID)) != NULL) && | |
3885 | evaluate_option_cache(&client_identifier, NULL, NULL, | |
3886 | client, client->sent_options, NULL, | |
3887 | &global_scope, oc, MDL)) { | |
3888 | /* RFC4701 defines type '2' as being for the DUID | |
3889 | * field. We aren't using RFC4701 DHCID RR's yet, | |
3890 | * but this is as good a value as any. | |
3891 | */ | |
98bf1607 | 3892 | result = get_dhcid(&ddns_cb->dhcid, 2, |
98bd7ca0 DH |
3893 | client_identifier.data, |
3894 | client_identifier.len); | |
3895 | data_string_forget(&client_identifier, MDL); | |
3896 | } else | |
3897 | log_fatal("Impossible condition at %s:%d.", MDL); | |
3898 | } else { | |
3899 | if (((oc = | |
3900 | lookup_option(&dhcp_universe, client->sent_options, | |
3901 | DHO_DHCP_CLIENT_IDENTIFIER)) != NULL) && | |
3902 | evaluate_option_cache(&client_identifier, NULL, NULL, | |
3903 | client, client->sent_options, NULL, | |
3904 | &global_scope, oc, MDL)) { | |
98bf1607 | 3905 | result = get_dhcid(&ddns_cb->dhcid, |
98bd7ca0 DH |
3906 | DHO_DHCP_CLIENT_IDENTIFIER, |
3907 | client_identifier.data, | |
3908 | client_identifier.len); | |
3909 | data_string_forget(&client_identifier, MDL); | |
3910 | } else | |
98bf1607 | 3911 | result = get_dhcid(&ddns_cb->dhcid, 0, |
98bd7ca0 DH |
3912 | client->interface->hw_address.hbuf, |
3913 | client->interface->hw_address.hlen); | |
3914 | } | |
d758ad8c | 3915 | if (!result) { |
98311e4b | 3916 | return ISC_R_SUCCESS; |
d758ad8c TL |
3917 | } |
3918 | ||
d758ad8c TL |
3919 | /* |
3920 | * Perform updates. | |
3921 | */ | |
98bf1607 SR |
3922 | if (ddns_cb->fwd_name.len && ddns_cb->dhcid.len) { |
3923 | rcode = ddns_modify_fwd(ddns_cb); | |
98311e4b DH |
3924 | } else |
3925 | rcode = ISC_R_FAILURE; | |
3dbe2246 | 3926 | |
98bf1607 SR |
3927 | /* |
3928 | * A success from the modify routine means we are performing | |
3929 | * async processing, for which we use the timedout error message. | |
3930 | */ | |
3931 | if (rcode == ISC_R_SUCCESS) { | |
3932 | rcode = ISC_R_TIMEDOUT; | |
3933 | } | |
3934 | ||
98311e4b | 3935 | return rcode; |
d758ad8c | 3936 | } |
98bd7ca0 | 3937 | |
98bf1607 SR |
3938 | |
3939 | /* | |
3940 | * Schedule the first update. They will continue to retry occasionally | |
3941 | * until they no longer time out (or fail). | |
3942 | */ | |
3943 | void | |
3944 | dhclient_schedule_updates(struct client_state *client, | |
3945 | struct iaddr *addr, | |
3946 | int offset) | |
3947 | { | |
3948 | dhcp_ddns_cb_t *ddns_cb; | |
3949 | struct timeval tv; | |
3950 | ||
3951 | if (!client->config->do_forward_update) | |
3952 | return; | |
3953 | ||
3954 | /* cancel any outstanding ddns requests */ | |
3955 | if (client->ddns_cb != NULL) { | |
3956 | ddns_cancel(client->ddns_cb); | |
3957 | client->ddns_cb = NULL; | |
3958 | } | |
3959 | ||
3960 | ddns_cb = ddns_cb_alloc(MDL); | |
3961 | ||
3962 | if (ddns_cb != NULL) { | |
3963 | ddns_cb->lease = (void *)client; | |
3964 | ddns_cb->address = *addr; | |
3965 | ddns_cb->timeout = 1; | |
3966 | ||
3967 | /* | |
3968 | * XXX: DNS TTL is a problem we need to solve properly. | |
3969 | * Until that time, 300 is a placeholder default for | |
3970 | * something that is less insane than a value scaled | |
3971 | * by lease timeout. | |
3972 | */ | |
3973 | ddns_cb->ttl = 300; | |
3974 | ||
3975 | ddns_cb->state = DDNS_STATE_ADD_FW_NXDOMAIN; | |
3976 | ddns_cb->cur_func = client_dns_update_action; | |
3977 | ddns_cb->flags = DDNS_UPDATE_ADDR | DDNS_INCLUDE_RRSET; | |
3978 | ||
3979 | client->ddns_cb = ddns_cb; | |
3980 | ||
3981 | tv.tv_sec = cur_time + offset; | |
3982 | tv.tv_usec = 0; | |
3983 | add_timeout(&tv, client_dns_update_timeout, | |
3984 | ddns_cb, NULL, NULL); | |
3985 | } else { | |
3986 | log_error("Unable to allocate dns update state for %s", | |
3987 | piaddr(*addr)); | |
3988 | } | |
3989 | } | |
3990 | #endif | |
3991 | ||
98bd7ca0 DH |
3992 | void |
3993 | dhcpv4_client_assignments(void) | |
3994 | { | |
3995 | struct servent *ent; | |
3996 | ||
3997 | if (path_dhclient_pid == NULL) | |
3998 | path_dhclient_pid = _PATH_DHCLIENT_PID; | |
3999 | if (path_dhclient_db == NULL) | |
4000 | path_dhclient_db = _PATH_DHCLIENT_DB; | |
4001 | ||
4002 | /* Default to the DHCP/BOOTP port. */ | |
4003 | if (!local_port) { | |
4004 | /* If we're faking a relay agent, and we're not using loopback, | |
4005 | use the server port, not the client port. */ | |
4006 | if (mockup_relay && giaddr.s_addr != htonl (INADDR_LOOPBACK)) { | |
4007 | local_port = htons(67); | |
4008 | } else { | |
4009 | ent = getservbyname ("dhcpc", "udp"); | |
4010 | if (!ent) | |
4011 | local_port = htons (68); | |
4012 | else | |
4013 | local_port = ent -> s_port; | |
4014 | #ifndef __CYGWIN32__ | |
4015 | endservent (); | |
4016 | #endif | |
4017 | } | |
4018 | } | |
4019 | ||
4020 | /* If we're faking a relay agent, and we're not using loopback, | |
4021 | we're using the server port, not the client port. */ | |
4022 | if (mockup_relay && giaddr.s_addr != htonl (INADDR_LOOPBACK)) { | |
4023 | remote_port = local_port; | |
4024 | } else | |
4025 | remote_port = htons (ntohs (local_port) - 1); /* XXX */ | |
4026 | } |