]> git.ipfire.org Git - thirdparty/dhcp.git/blob - server/dhcpd.c
- ./configure was extended to cover many optional build features, such
[thirdparty/dhcp.git] / server / dhcpd.c
1 /* dhcpd.c
2
3 DHCP Server Daemon. */
4
5 /*
6 * Copyright (c) 2004-2008 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1996-2003 by Internet Software Consortium
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 *
21 * Internet Systems Consortium, Inc.
22 * 950 Charter Street
23 * Redwood City, CA 94063
24 * <info@isc.org>
25 * http://www.isc.org/
26 *
27 * This software has been written for Internet Systems Consortium
28 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29 * To learn more about Internet Systems Consortium, see
30 * ``http://www.isc.org/''. To learn more about Vixie Enterprises,
31 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
32 * ``http://www.nominum.com''.
33 */
34
35 static char copyright[] =
36 "Copyright 2004-2008 Internet Systems Consortium.";
37 static char arr [] = "All rights reserved.";
38 static char message [] = "Internet Systems Consortium DHCP Server";
39 static char url [] = "For info, please visit http://www.isc.org/sw/dhcp/";
40
41 #include "dhcpd.h"
42 #include <omapip/omapip_p.h>
43 #include <syslog.h>
44 #include <errno.h>
45 #include <limits.h>
46 #include <sys/types.h>
47 #include <sys/time.h>
48 #include <signal.h>
49
50 #if defined (PARANOIA)
51 # include <sys/types.h>
52 # include <unistd.h>
53 # include <pwd.h>
54 /* get around the ISC declaration of group */
55 # define group real_group
56 # include <grp.h>
57 # undef group
58 #endif /* PARANOIA */
59
60 static void usage(void);
61
62 struct iaddr server_identifier;
63 int server_identifier_matched;
64
65 #if defined (NSUPDATE)
66
67 /* This stuff is always executed to figure the default values for certain
68 ddns variables. */
69
70 char std_nsupdate [] = " \n\
71 option server.ddns-hostname = \n\
72 pick (option fqdn.hostname, option host-name); \n\
73 option server.ddns-domainname = config-option domain-name; \n\
74 option server.ddns-ttl = encode-int(lease-time / 2, 32); \n\
75 option server.ddns-rev-domainname = \"in-addr.arpa.\";";
76
77 /* This is the old-style name service updater that is executed
78 whenever a lease is committed. It does not follow the DHCP-DNS
79 draft at all. */
80
81 char old_nsupdate [] = " \n\
82 on commit { \n\
83 if (not static and \n\
84 ((config-option server.ddns-updates = null) or \n\
85 (config-option server.ddns-updates != 0))) { \n\
86 set new-ddns-fwd-name = \n\
87 concat (pick (config-option server.ddns-hostname, \n\
88 option host-name), \".\", \n\
89 pick (config-option server.ddns-domainname, \n\
90 config-option domain-name)); \n\
91 if (defined (ddns-fwd-name) and ddns-fwd-name != new-ddns-fwd-name) { \n\
92 switch (ns-update (delete (IN, A, ddns-fwd-name, leased-address))) { \n\
93 case NOERROR: \n\
94 unset ddns-fwd-name; \n\
95 on expiry or release { \n\
96 } \n\
97 } \n\
98 } \n\
99 \n\
100 if (not defined (ddns-fwd-name)) { \n\
101 set ddns-fwd-name = new-ddns-fwd-name; \n\
102 if defined (ddns-fwd-name) { \n\
103 switch (ns-update (not exists (IN, A, ddns-fwd-name, null), \n\
104 add (IN, A, ddns-fwd-name, leased-address, \n\
105 lease-time / 2))) { \n\
106 default: \n\
107 unset ddns-fwd-name; \n\
108 break; \n\
109 \n\
110 case NOERROR: \n\
111 set ddns-rev-name = \n\
112 concat (binary-to-ascii (10, 8, \".\", \n\
113 reverse (1, \n\
114 leased-address)), \".\", \n\
115 pick (config-option server.ddns-rev-domainname, \n\
116 \"in-addr.arpa.\")); \n\
117 switch (ns-update (delete (IN, PTR, ddns-rev-name, null), \n\
118 add (IN, PTR, ddns-rev-name, ddns-fwd-name, \n\
119 lease-time / 2))) \n\
120 { \n\
121 default: \n\
122 unset ddns-rev-name; \n\
123 on release or expiry { \n\
124 switch (ns-update (delete (IN, A, ddns-fwd-name, \n\
125 leased-address))) { \n\
126 case NOERROR: \n\
127 unset ddns-fwd-name; \n\
128 break; \n\
129 } \n\
130 on release or expiry; \n\
131 } \n\
132 break; \n\
133 \n\
134 case NOERROR: \n\
135 on release or expiry { \n\
136 switch (ns-update (delete (IN, PTR, ddns-rev-name, null))) {\n\
137 case NOERROR: \n\
138 unset ddns-rev-name; \n\
139 break; \n\
140 } \n\
141 switch (ns-update (delete (IN, A, ddns-fwd-name, \n\
142 leased-address))) { \n\
143 case NOERROR: \n\
144 unset ddns-fwd-name; \n\
145 break; \n\
146 } \n\
147 on release or expiry; \n\
148 } \n\
149 } \n\
150 } \n\
151 } \n\
152 } \n\
153 unset new-ddns-fwd-name; \n\
154 } \n\
155 }";
156
157 int ddns_update_style;
158 #endif /* NSUPDATE */
159
160 const char *path_dhcpd_conf = _PATH_DHCPD_CONF;
161 const char *path_dhcpd_db = _PATH_DHCPD_DB;
162 const char *path_dhcpd_pid = _PATH_DHCPD_PID;
163
164 int dhcp_max_agent_option_packet_length = DHCP_MTU_MAX;
165
166 static omapi_auth_key_t *omapi_key = (omapi_auth_key_t *)0;
167 int omapi_port;
168
169 #if defined (TRACING)
170 trace_type_t *trace_srandom;
171 #endif
172
173 static isc_result_t verify_addr (omapi_object_t *l, omapi_addr_t *addr) {
174 return ISC_R_SUCCESS;
175 }
176
177 static isc_result_t verify_auth (omapi_object_t *p, omapi_auth_key_t *a) {
178 if (a != omapi_key)
179 return ISC_R_INVALIDKEY;
180 return ISC_R_SUCCESS;
181 }
182
183 static void omapi_listener_start (void *foo)
184 {
185 omapi_object_t *listener;
186 isc_result_t result;
187 struct timeval tv;
188
189 listener = (omapi_object_t *)0;
190 result = omapi_generic_new (&listener, MDL);
191 if (result != ISC_R_SUCCESS)
192 log_fatal ("Can't allocate new generic object: %s",
193 isc_result_totext (result));
194 result = omapi_protocol_listen (listener,
195 (unsigned)omapi_port, 1);
196 if (result == ISC_R_SUCCESS && omapi_key)
197 result = omapi_protocol_configure_security
198 (listener, verify_addr, verify_auth);
199 if (result != ISC_R_SUCCESS) {
200 log_error ("Can't start OMAPI protocol: %s",
201 isc_result_totext (result));
202 tv.tv_sec = cur_time + 5;
203 tv.tv_usec = 0;
204 add_timeout (&tv, omapi_listener_start, 0, 0, 0);
205 }
206 omapi_object_dereference (&listener, MDL);
207 }
208
209 #if defined (PARANOIA)
210 /* to be used in one of two possible scenarios */
211 static void setup_chroot (char *chroot_dir) {
212 if (geteuid())
213 log_fatal ("you must be root to use chroot");
214
215 if (chroot(chroot_dir)) {
216 log_fatal ("chroot(\"%s\"): %m", chroot_dir);
217 }
218 if (chdir ("/")) {
219 /* probably permission denied */
220 log_fatal ("chdir(\"/\"): %m");
221 }
222 }
223 #endif /* PARANOIA */
224
225 #ifndef UNIT_TEST
226 int
227 main(int argc, char **argv) {
228 int fd;
229 int i, status;
230 struct servent *ent;
231 char *s;
232 int cftest = 0;
233 int lftest = 0;
234 #ifndef DEBUG
235 int pid;
236 char pbuf [20];
237 int daemon = 1;
238 #endif
239 int quiet = 0;
240 char *server = (char *)0;
241 isc_result_t result;
242 unsigned seed;
243 struct interface_info *ip;
244 struct parse *parse;
245 int lose;
246 int no_dhcpd_conf = 0;
247 int no_dhcpd_db = 0;
248 int no_dhcpd_pid = 0;
249 #ifdef DHCPv6
250 int local_family_set = 0;
251 #endif /* DHCPv6 */
252 #if defined (TRACING)
253 char *traceinfile = (char *)0;
254 char *traceoutfile = (char *)0;
255 #endif
256
257 #if defined (PARANOIA)
258 char *set_user = 0;
259 char *set_group = 0;
260 char *set_chroot = 0;
261
262 uid_t set_uid = 0;
263 gid_t set_gid = 0;
264 #endif /* PARANOIA */
265
266 /* Make sure that file descriptors 0 (stdin), 1, (stdout), and
267 2 (stderr) are open. To do this, we assume that when we
268 open a file the lowest available file descriptor is used. */
269 fd = open("/dev/null", O_RDWR);
270 if (fd == 0)
271 fd = open("/dev/null", O_RDWR);
272 if (fd == 1)
273 fd = open("/dev/null", O_RDWR);
274 if (fd == 2)
275 log_perror = 0; /* No sense logging to /dev/null. */
276 else if (fd != -1)
277 close(fd);
278
279 /* Set up the client classification system. */
280 classification_setup ();
281
282 /* Initialize the omapi system. */
283 result = omapi_init ();
284 if (result != ISC_R_SUCCESS)
285 log_fatal ("Can't initialize OMAPI: %s",
286 isc_result_totext (result));
287
288 /* Set up the OMAPI wrappers for common objects. */
289 dhcp_db_objects_setup ();
290 /* Set up the OMAPI wrappers for various server database internal
291 objects. */
292 dhcp_common_objects_setup ();
293
294 /* Initially, log errors to stderr as well as to syslogd. */
295 openlog ("dhcpd", LOG_NDELAY, DHCPD_LOG_FACILITY);
296
297 for (i = 1; i < argc; i++) {
298 if (!strcmp (argv [i], "-p")) {
299 if (++i == argc)
300 usage ();
301 for (s = argv [i]; *s; s++)
302 if (!isdigit ((unsigned char)*s))
303 log_fatal ("%s: not a valid UDP port",
304 argv [i]);
305 status = atoi (argv [i]);
306 if (status < 1 || status > 65535)
307 log_fatal ("%s: not a valid UDP port",
308 argv [i]);
309 local_port = htons (status);
310 log_debug ("binding to user-specified port %d",
311 ntohs (local_port));
312 } else if (!strcmp (argv [i], "-f")) {
313 #ifndef DEBUG
314 daemon = 0;
315 #endif
316 } else if (!strcmp (argv [i], "-d")) {
317 #ifndef DEBUG
318 daemon = 0;
319 #endif
320 log_perror = -1;
321 } else if (!strcmp (argv [i], "-s")) {
322 if (++i == argc)
323 usage ();
324 server = argv [i];
325 #if defined (PARANOIA)
326 } else if (!strcmp (argv [i], "-user")) {
327 if (++i == argc)
328 usage ();
329 set_user = argv [i];
330 } else if (!strcmp (argv [i], "-group")) {
331 if (++i == argc)
332 usage ();
333 set_group = argv [i];
334 } else if (!strcmp (argv [i], "-chroot")) {
335 if (++i == argc)
336 usage ();
337 set_chroot = argv [i];
338 #endif /* PARANOIA */
339 } else if (!strcmp (argv [i], "-cf")) {
340 if (++i == argc)
341 usage ();
342 path_dhcpd_conf = argv [i];
343 no_dhcpd_conf = 1;
344 } else if (!strcmp (argv [i], "-lf")) {
345 if (++i == argc)
346 usage ();
347 path_dhcpd_db = argv [i];
348 no_dhcpd_db = 1;
349 } else if (!strcmp (argv [i], "-pf")) {
350 if (++i == argc)
351 usage ();
352 path_dhcpd_pid = argv [i];
353 no_dhcpd_pid = 1;
354 } else if (!strcmp (argv [i], "-t")) {
355 /* test configurations only */
356 #ifndef DEBUG
357 daemon = 0;
358 #endif
359 cftest = 1;
360 log_perror = -1;
361 } else if (!strcmp (argv [i], "-T")) {
362 /* test configurations and lease file only */
363 #ifndef DEBUG
364 daemon = 0;
365 #endif
366 cftest = 1;
367 lftest = 1;
368 log_perror = -1;
369 } else if (!strcmp (argv [i], "-q")) {
370 quiet = 1;
371 quiet_interface_discovery = 1;
372 #ifdef DHCPv6
373 } else if (!strcmp(argv[i], "-4")) {
374 if (local_family_set && (local_family != AF_INET)) {
375 log_fatal("Server cannot run in both IPv4 and "
376 "IPv6 mode at the same time.");
377 }
378 local_family = AF_INET;
379 local_family_set = 1;
380 } else if (!strcmp(argv[i], "-6")) {
381 if (local_family_set && (local_family != AF_INET6)) {
382 log_fatal("Server cannot run in both IPv4 and "
383 "IPv6 mode at the same time.");
384 }
385 local_family = AF_INET6;
386 local_family_set = 1;
387 #endif /* DHCPv6 */
388 } else if (!strcmp (argv [i], "--version")) {
389 log_info("isc-dhcpd-%s", PACKAGE_VERSION);
390 exit (0);
391 #if defined (TRACING)
392 } else if (!strcmp (argv [i], "-tf")) {
393 if (++i == argc)
394 usage ();
395 traceoutfile = argv [i];
396 } else if (!strcmp (argv [i], "-play")) {
397 if (++i == argc)
398 usage ();
399 traceinfile = argv [i];
400 trace_replay_init ();
401 #endif /* TRACING */
402 } else if (argv [i][0] == '-') {
403 usage ();
404 } else {
405 struct interface_info *tmp =
406 (struct interface_info *)0;
407 result = interface_allocate (&tmp, MDL);
408 if (result != ISC_R_SUCCESS)
409 log_fatal ("Insufficient memory to %s %s: %s",
410 "record interface", argv [i],
411 isc_result_totext (result));
412 strcpy (tmp -> name, argv [i]);
413 if (interfaces) {
414 interface_reference (&tmp -> next,
415 interfaces, MDL);
416 interface_dereference (&interfaces, MDL);
417 }
418 interface_reference (&interfaces, tmp, MDL);
419 tmp -> flags = INTERFACE_REQUESTED;
420 }
421 }
422
423 if (!no_dhcpd_conf && (s = getenv ("PATH_DHCPD_CONF"))) {
424 path_dhcpd_conf = s;
425 }
426
427 #ifdef DHCPv6
428 if (local_family == AF_INET6) {
429 /* DHCPv6: override DHCPv4 lease and pid filenames */
430 if (!no_dhcpd_db) {
431 if ((s = getenv ("PATH_DHCPD6_DB")))
432 path_dhcpd_db = s;
433 else
434 path_dhcpd_db = _PATH_DHCPD6_DB;
435 }
436 if (!no_dhcpd_pid) {
437 if ((s = getenv ("PATH_DHCPD6_PID")))
438 path_dhcpd_pid = s;
439 else
440 path_dhcpd_pid = _PATH_DHCPD6_PID;
441 }
442 } else
443 #else /* !DHCPv6 */
444 {
445 if (!no_dhcpd_db && (s = getenv ("PATH_DHCPD_DB"))) {
446 path_dhcpd_db = s;
447 }
448 if (!no_dhcpd_pid && (s = getenv ("PATH_DHCPD_PID"))) {
449 path_dhcpd_pid = s;
450 }
451 }
452 #endif /* DHCPv6 */
453
454 /*
455 * convert relative path names to absolute, for files that need
456 * to be reopened after chdir() has been called
457 */
458 if (path_dhcpd_db[0] != '/') {
459 char *path = dmalloc(PATH_MAX, MDL);
460 if (path == NULL)
461 log_fatal("No memory for filename\n");
462 path_dhcpd_db = realpath(path_dhcpd_db, path);
463 if (path_dhcpd_db == NULL)
464 log_fatal("%s: %s", path, strerror(errno));
465 }
466
467 if (!quiet) {
468 log_info("%s %s", message, PACKAGE_VERSION);
469 log_info (copyright);
470 log_info (arr);
471 log_info (url);
472 } else {
473 quiet = 0;
474 log_perror = 0;
475 }
476
477 #if defined (TRACING)
478 trace_init (set_time, MDL);
479 if (traceoutfile) {
480 result = trace_begin (traceoutfile, MDL);
481 if (result != ISC_R_SUCCESS)
482 log_fatal ("Unable to begin trace: %s",
483 isc_result_totext (result));
484 }
485 interface_trace_setup ();
486 parse_trace_setup ();
487 trace_srandom = trace_type_register ("random-seed", (void *)0,
488 trace_seed_input,
489 trace_seed_stop, MDL);
490 #endif
491
492 #if defined (PARANOIA)
493 /* get user and group info if those options were given */
494 if (set_user) {
495 struct passwd *tmp_pwd;
496
497 if (geteuid())
498 log_fatal ("you must be root to set user");
499
500 if (!(tmp_pwd = getpwnam(set_user)))
501 log_fatal ("no such user: %s", set_user);
502
503 set_uid = tmp_pwd->pw_uid;
504
505 /* use the user's group as the default gid */
506 if (!set_group)
507 set_gid = tmp_pwd->pw_gid;
508 }
509
510 if (set_group) {
511 /* get around the ISC declaration of group */
512 #define group real_group
513 struct group *tmp_grp;
514
515 if (geteuid())
516 log_fatal ("you must be root to set group");
517
518 if (!(tmp_grp = getgrnam(set_group)))
519 log_fatal ("no such group: %s", set_group);
520
521 set_gid = tmp_grp->gr_gid;
522 #undef group
523 }
524
525 # if defined (EARLY_CHROOT)
526 if (set_chroot) setup_chroot (set_chroot);
527 # endif /* EARLY_CHROOT */
528 #endif /* PARANOIA */
529
530 /* Default to the DHCP/BOOTP port. */
531 if (!local_port)
532 {
533 if ((s = getenv ("DHCPD_PORT"))) {
534 local_port = htons (atoi (s));
535 log_debug ("binding to environment-specified port %d",
536 ntohs (local_port));
537 } else {
538 if (local_family == AF_INET) {
539 ent = getservbyname("dhcp", "udp");
540 if (ent == NULL) {
541 local_port = htons(67);
542 } else {
543 local_port = ent->s_port;
544 }
545 } else {
546 /* INSIST(local_family == AF_INET6); */
547 ent = getservbyname("dhcpv6-server", "udp");
548 if (ent == NULL) {
549 local_port = htons(547);
550 } else {
551 local_port = ent->s_port;
552 }
553 }
554 #ifndef __CYGWIN32__ /* XXX */
555 endservent ();
556 #endif
557 }
558 }
559
560 if (local_family == AF_INET) {
561 remote_port = htons(ntohs(local_port) + 1);
562 } else {
563 /* INSIST(local_family == AF_INET6); */
564 ent = getservbyname("dhcpv6-client", "udp");
565 if (ent == NULL) {
566 remote_port = htons(546);
567 } else {
568 remote_port = ent->s_port;
569 }
570 }
571
572 if (server) {
573 if (local_family != AF_INET) {
574 log_fatal("You can only specify address to send "
575 "replies to when running an IPv4 server.");
576 }
577 if (!inet_aton (server, &limited_broadcast)) {
578 struct hostent *he;
579 he = gethostbyname (server);
580 if (he) {
581 memcpy (&limited_broadcast,
582 he -> h_addr_list [0],
583 sizeof limited_broadcast);
584 } else
585 limited_broadcast.s_addr = INADDR_BROADCAST;
586 }
587 } else {
588 limited_broadcast.s_addr = INADDR_BROADCAST;
589 }
590
591 /* Get the current time... */
592 gettimeofday(&cur_tv, NULL);
593
594 /* Set up the initial dhcp option universe. */
595 initialize_common_option_spaces ();
596 initialize_server_option_spaces ();
597
598 /* Add the ddns update style enumeration prior to parsing. */
599 add_enumeration (&ddns_styles);
600 add_enumeration (&syslog_enum);
601
602 if (!group_allocate (&root_group, MDL))
603 log_fatal ("Can't allocate root group!");
604 root_group -> authoritative = 0;
605
606 /* Set up various hooks. */
607 dhcp_interface_setup_hook = dhcpd_interface_setup_hook;
608 bootp_packet_handler = do_packet;
609 #ifdef DHCPv6
610 dhcpv6_packet_handler = do_packet6;
611 #endif /* DHCPv6 */
612
613 #if defined (NSUPDATE)
614 /* Set up the standard name service updater routine. */
615 parse = (struct parse *)0;
616 status = new_parse (&parse, -1,
617 std_nsupdate, (sizeof std_nsupdate) - 1,
618 "standard name service update routine", 0);
619 if (status != ISC_R_SUCCESS)
620 log_fatal ("can't begin parsing name service updater!");
621
622 lose = 0;
623 if (!(parse_executable_statements
624 (&root_group -> statements, parse, &lose, context_any))) {
625 end_parse (&parse);
626 log_fatal ("can't parse standard name service updater!");
627 }
628 end_parse (&parse);
629 #endif
630
631 /* Initialize icmp support... */
632 if (!cftest && !lftest)
633 icmp_startup (1, lease_pinged);
634
635 #if defined (TRACING)
636 if (traceinfile) {
637 if (!no_dhcpd_db) {
638 log_error ("%s", "");
639 log_error ("** You must specify a lease file with -lf.");
640 log_error (" Dhcpd will not overwrite your default");
641 log_fatal (" lease file when playing back a trace. **");
642 }
643 trace_file_replay (traceinfile);
644
645 #if defined (DEBUG_MEMORY_LEAKAGE) && \
646 defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
647 free_everything ();
648 omapi_print_dmalloc_usage_by_caller ();
649 #endif
650
651 exit (0);
652 }
653 #endif
654
655 #ifdef DHCPv6
656 /* set up DHCPv6 hashes */
657 if (!ia_na_new_hash(&ia_na_active, DEFAULT_HASH_SIZE, MDL)) {
658 log_fatal("Out of memory creating hash for active IA_NA.");
659 }
660 if (!ia_na_new_hash(&ia_ta_active, DEFAULT_HASH_SIZE, MDL)) {
661 log_fatal("Out of memory creating hash for active IA_TA.");
662 }
663 if (!ia_pd_new_hash(&ia_pd_active, DEFAULT_HASH_SIZE, MDL)) {
664 log_fatal("Out of memory creating hash for active IA_PD.");
665 }
666 #endif /* DHCPv6 */
667
668 /* Read the dhcpd.conf file... */
669 if (readconf () != ISC_R_SUCCESS)
670 log_fatal ("Configuration file errors encountered -- exiting");
671
672 postconf_initialization (quiet);
673
674 #if defined (PARANOIA) && !defined (EARLY_CHROOT)
675 if (set_chroot) setup_chroot (set_chroot);
676 #endif /* PARANOIA && !EARLY_CHROOT */
677
678 /* test option should cause an early exit */
679 if (cftest && !lftest)
680 exit(0);
681
682 group_write_hook = group_writer;
683
684 /* Start up the database... */
685 db_startup (lftest);
686
687 if (lftest)
688 exit (0);
689
690 /* Discover all the network interfaces and initialize them. */
691 discover_interfaces(DISCOVER_SERVER);
692
693 #ifdef DHCPv6
694 /*
695 * Remove addresses from our pools that we should not issue
696 * to clients.
697 *
698 * We currently have no support for this in IPv4. It is not
699 * as important in IPv4, as making pools with ranges that
700 * leave out interfaces and hosts is fairly straightforward
701 * using range notation, but not so handy with CIDR notation.
702 */
703 if (local_family == AF_INET6) {
704 mark_hosts_unavailable();
705 mark_phosts_unavailable();
706 mark_interfaces_unavailable();
707 }
708 #endif /* DHCPv6 */
709
710
711 /* Make up a seed for the random number generator from current
712 time plus the sum of the last four bytes of each
713 interface's hardware address interpreted as an integer.
714 Not much entropy, but we're booting, so we're not likely to
715 find anything better. */
716 seed = 0;
717 for (ip = interfaces; ip; ip = ip -> next) {
718 int junk;
719 memcpy (&junk,
720 &ip -> hw_address.hbuf [ip -> hw_address.hlen -
721 sizeof seed], sizeof seed);
722 seed += junk;
723 }
724 srandom (seed + cur_time);
725 #if defined (TRACING)
726 trace_seed_stash (trace_srandom, seed + cur_time);
727 #endif
728 postdb_startup ();
729
730 #ifdef DHCPv6
731 /*
732 * Set server DHCPv6 identifier.
733 * See dhcpv6.c for discussion of setting DUID.
734 */
735 if (set_server_duid_from_option() == ISC_R_SUCCESS) {
736 write_server_duid();
737 } else {
738 if (!server_duid_isset()) {
739 if (generate_new_server_duid() != ISC_R_SUCCESS) {
740 log_fatal("Unable to set server identifier.");
741 }
742 write_server_duid();
743 }
744 }
745 #endif /* DHCPv6 */
746
747 #ifndef DEBUG
748 if (daemon) {
749 /* First part of becoming a daemon... */
750 if ((pid = fork ()) < 0)
751 log_fatal ("Can't fork daemon: %m");
752 else if (pid)
753 exit (0);
754 }
755
756 #if defined (PARANOIA)
757 /* change uid to the specified one */
758
759 if (set_gid) {
760 if (setgroups (0, (void *)0))
761 log_fatal ("setgroups: %m");
762 if (setgid (set_gid))
763 log_fatal ("setgid(%d): %m", (int) set_gid);
764 }
765
766 if (set_uid) {
767 if (setuid (set_uid))
768 log_fatal ("setuid(%d): %m", (int) set_uid);
769 }
770 #endif /* PARANOIA */
771
772 /* Read previous pid file. */
773 if ((i = open (path_dhcpd_pid, O_RDONLY)) >= 0) {
774 status = read(i, pbuf, (sizeof pbuf) - 1);
775 close (i);
776 if (status > 0) {
777 pbuf[status] = 0;
778 pid = atoi(pbuf);
779
780 /*
781 * If there was a previous server process and it's
782 * is still running, abort
783 */
784 if (!pid || (pid != getpid() && kill(pid, 0) == 0))
785 log_fatal("There's already a "
786 "DHCP server running.");
787 }
788 }
789
790 /* Write new pid file. */
791 if ((i = open(path_dhcpd_pid, O_WRONLY|O_CREAT|O_TRUNC, 0644)) >= 0) {
792 sprintf(pbuf, "%d\n", (int) getpid());
793 write(i, pbuf, strlen(pbuf));
794 close(i);
795 } else {
796 log_error("Can't create PID file %s: %m.", path_dhcpd_pid);
797 }
798
799
800 /* If we were requested to log to stdout on the command line,
801 keep doing so; otherwise, stop. */
802 if (log_perror == -1)
803 log_perror = 1;
804 else
805 log_perror = 0;
806
807 if (daemon) {
808 /* Become session leader and get pid... */
809 pid = setsid();
810
811 /* Close standard I/O descriptors. */
812 close(0);
813 close(1);
814 close(2);
815
816 /* Reopen them on /dev/null. */
817 open("/dev/null", O_RDWR);
818 open("/dev/null", O_RDWR);
819 open("/dev/null", O_RDWR);
820 log_perror = 0; /* No sense logging to /dev/null. */
821
822 chdir("/");
823 }
824 #endif /* !DEBUG */
825
826 #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \
827 defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
828 dmalloc_cutoff_generation = dmalloc_generation;
829 dmalloc_longterm = dmalloc_outstanding;
830 dmalloc_outstanding = 0;
831 #endif
832
833 #if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY)
834 dump_rc_history ();
835 #endif
836
837 omapi_set_int_value ((omapi_object_t *)dhcp_control_object,
838 (omapi_object_t *)0, "state", server_running);
839
840 register_eventhandler(&rw_queue_empty,commit_leases_readerdry);
841
842 /* Receive packets and dispatch them... */
843 dispatch ();
844
845 /* Not reached */
846 return 0;
847 }
848 #endif /* !UNIT_TEST */
849
850 void postconf_initialization (int quiet)
851 {
852 struct option_state *options = (struct option_state *)0;
853 struct data_string db;
854 struct option_cache *oc;
855 char *s;
856 isc_result_t result;
857 struct parse *parse;
858 int tmp;
859
860 /* Now try to get the lease file name. */
861 option_state_allocate (&options, MDL);
862
863 execute_statements_in_scope ((struct binding_value **)0,
864 (struct packet *)0,
865 (struct lease *)0,
866 (struct client_state *)0,
867 (struct option_state *)0,
868 options, &global_scope,
869 root_group,
870 (struct group *)0);
871 memset (&db, 0, sizeof db);
872 oc = lookup_option (&server_universe, options, SV_LEASE_FILE_NAME);
873 if (oc &&
874 evaluate_option_cache (&db, (struct packet *)0,
875 (struct lease *)0, (struct client_state *)0,
876 options, (struct option_state *)0,
877 &global_scope, oc, MDL)) {
878 s = dmalloc (db.len + 1, MDL);
879 if (!s)
880 log_fatal ("no memory for lease db filename.");
881 memcpy (s, db.data, db.len);
882 s [db.len] = 0;
883 data_string_forget (&db, MDL);
884 path_dhcpd_db = s;
885 }
886
887 oc = lookup_option (&server_universe, options, SV_PID_FILE_NAME);
888 if (oc &&
889 evaluate_option_cache (&db, (struct packet *)0,
890 (struct lease *)0, (struct client_state *)0,
891 options, (struct option_state *)0,
892 &global_scope, oc, MDL)) {
893 s = dmalloc (db.len + 1, MDL);
894 if (!s)
895 log_fatal ("no memory for lease db filename.");
896 memcpy (s, db.data, db.len);
897 s [db.len] = 0;
898 data_string_forget (&db, MDL);
899 path_dhcpd_pid = s;
900 }
901
902 #ifdef DHCPv6
903 if (local_family == AF_INET6) {
904 /*
905 * Override lease file name with dhcpv6 lease file name,
906 * if it was set; then, do the same with the pid file name
907 */
908 oc = lookup_option(&server_universe, options,
909 SV_DHCPV6_LEASE_FILE_NAME);
910 if (oc &&
911 evaluate_option_cache(&db, NULL, NULL, NULL,
912 options, NULL, &global_scope,
913 oc, MDL)) {
914 s = dmalloc (db.len + 1, MDL);
915 if (!s)
916 log_fatal ("no memory for lease db filename.");
917 memcpy (s, db.data, db.len);
918 s [db.len] = 0;
919 data_string_forget (&db, MDL);
920 path_dhcpd_db = s;
921 }
922
923 oc = lookup_option(&server_universe, options,
924 SV_DHCPV6_PID_FILE_NAME);
925 if (oc &&
926 evaluate_option_cache(&db, NULL, NULL, NULL,
927 options, NULL, &global_scope,
928 oc, MDL)) {
929 s = dmalloc (db.len + 1, MDL);
930 if (!s)
931 log_fatal ("no memory for lease db filename.");
932 memcpy (s, db.data, db.len);
933 s [db.len] = 0;
934 data_string_forget (&db, MDL);
935 path_dhcpd_pid = s;
936 }
937 }
938 #endif /* DHCPv6 */
939
940 omapi_port = -1;
941 oc = lookup_option (&server_universe, options, SV_OMAPI_PORT);
942 if (oc &&
943 evaluate_option_cache (&db, (struct packet *)0,
944 (struct lease *)0, (struct client_state *)0,
945 options, (struct option_state *)0,
946 &global_scope, oc, MDL)) {
947 if (db.len == 2) {
948 omapi_port = getUShort (db.data);
949 } else
950 log_fatal ("invalid omapi port data length");
951 data_string_forget (&db, MDL);
952 }
953
954 oc = lookup_option (&server_universe, options, SV_OMAPI_KEY);
955 if (oc &&
956 evaluate_option_cache (&db, (struct packet *)0,
957 (struct lease *)0, (struct client_state *)0,
958 options,
959 (struct option_state *)0,
960 &global_scope, oc, MDL)) {
961 s = dmalloc (db.len + 1, MDL);
962 if (!s)
963 log_fatal ("no memory for OMAPI key filename.");
964 memcpy (s, db.data, db.len);
965 s [db.len] = 0;
966 data_string_forget (&db, MDL);
967 result = omapi_auth_key_lookup_name (&omapi_key, s);
968 dfree (s, MDL);
969 if (result != ISC_R_SUCCESS)
970 log_fatal ("OMAPI key %s: %s",
971 s, isc_result_totext (result));
972 }
973
974 oc = lookup_option (&server_universe, options, SV_LOCAL_PORT);
975 if (oc &&
976 evaluate_option_cache (&db, (struct packet *)0,
977 (struct lease *)0, (struct client_state *)0,
978 options,
979 (struct option_state *)0,
980 &global_scope, oc, MDL)) {
981 if (db.len == 2) {
982 local_port = htons (getUShort (db.data));
983 } else
984 log_fatal ("invalid local port data length");
985 data_string_forget (&db, MDL);
986 }
987
988 oc = lookup_option (&server_universe, options, SV_REMOTE_PORT);
989 if (oc &&
990 evaluate_option_cache (&db, (struct packet *)0,
991 (struct lease *)0, (struct client_state *)0,
992 options, (struct option_state *)0,
993 &global_scope, oc, MDL)) {
994 if (db.len == 2) {
995 remote_port = htons (getUShort (db.data));
996 } else
997 log_fatal ("invalid remote port data length");
998 data_string_forget (&db, MDL);
999 }
1000
1001 oc = lookup_option (&server_universe, options,
1002 SV_LIMITED_BROADCAST_ADDRESS);
1003 if (oc &&
1004 evaluate_option_cache (&db, (struct packet *)0,
1005 (struct lease *)0, (struct client_state *)0,
1006 options, (struct option_state *)0,
1007 &global_scope, oc, MDL)) {
1008 if (db.len == 4) {
1009 memcpy (&limited_broadcast, db.data, 4);
1010 } else
1011 log_fatal ("invalid remote port data length");
1012 data_string_forget (&db, MDL);
1013 }
1014
1015 oc = lookup_option (&server_universe, options,
1016 SV_LOCAL_ADDRESS);
1017 if (oc &&
1018 evaluate_option_cache (&db, (struct packet *)0,
1019 (struct lease *)0, (struct client_state *)0,
1020 options, (struct option_state *)0,
1021 &global_scope, oc, MDL)) {
1022 if (db.len == 4) {
1023 memcpy (&local_address, db.data, 4);
1024 } else
1025 log_fatal ("invalid remote port data length");
1026 data_string_forget (&db, MDL);
1027 }
1028
1029 oc = lookup_option (&server_universe, options, SV_DDNS_UPDATE_STYLE);
1030 if (oc) {
1031 if (evaluate_option_cache (&db, (struct packet *)0,
1032 (struct lease *)0,
1033 (struct client_state *)0,
1034 options,
1035 (struct option_state *)0,
1036 &global_scope, oc, MDL)) {
1037 if (db.len == 1) {
1038 ddns_update_style = db.data [0];
1039 } else
1040 log_fatal ("invalid dns update type");
1041 data_string_forget (&db, MDL);
1042 }
1043 } else {
1044 ddns_update_style = DDNS_UPDATE_STYLE_NONE;
1045 }
1046
1047 oc = lookup_option (&server_universe, options, SV_LOG_FACILITY);
1048 if (oc) {
1049 if (evaluate_option_cache (&db, (struct packet *)0,
1050 (struct lease *)0,
1051 (struct client_state *)0,
1052 options,
1053 (struct option_state *)0,
1054 &global_scope, oc, MDL)) {
1055 if (db.len == 1) {
1056 closelog ();
1057 openlog ("dhcpd", LOG_NDELAY, db.data[0]);
1058 /* Log the startup banner into the new
1059 log file. */
1060 if (!quiet) {
1061 /* Don't log to stderr twice. */
1062 tmp = log_perror;
1063 log_perror = 0;
1064 log_info("%s %s",
1065 message, PACKAGE_VERSION);
1066 log_info (copyright);
1067 log_info (arr);
1068 log_info (url);
1069 log_perror = tmp;
1070 }
1071 } else
1072 log_fatal ("invalid log facility");
1073 data_string_forget (&db, MDL);
1074 }
1075 }
1076
1077 oc = lookup_option(&server_universe, options, SV_DELAYED_ACK);
1078 if (oc &&
1079 evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
1080 &global_scope, oc, MDL)) {
1081 if (db.len == 2) {
1082 max_outstanding_acks = htons(getUShort(db.data));
1083 } else {
1084 log_fatal("invalid max delayed ACK count ");
1085 }
1086 data_string_forget(&db, MDL);
1087 }
1088
1089 /* Don't need the options anymore. */
1090 option_state_dereference (&options, MDL);
1091
1092 #if defined (NSUPDATE)
1093 /* If old-style ddns updates have been requested, parse the
1094 old-style ddns updater. */
1095 if (ddns_update_style == 1) {
1096 struct executable_statement **e, *s;
1097
1098 if (root_group -> statements) {
1099 s = (struct executable_statement *)0;
1100 if (!executable_statement_allocate (&s, MDL))
1101 log_fatal ("no memory for ddns updater");
1102 executable_statement_reference
1103 (&s -> next, root_group -> statements, MDL);
1104 executable_statement_dereference
1105 (&root_group -> statements, MDL);
1106 executable_statement_reference
1107 (&root_group -> statements, s, MDL);
1108 s -> op = statements_statement;
1109 e = &s -> data.statements;
1110 executable_statement_dereference (&s, MDL);
1111 } else {
1112 e = &root_group -> statements;
1113 }
1114
1115 /* Set up the standard name service updater routine. */
1116 parse = (struct parse *)0;
1117 result = new_parse (&parse, -1,
1118 old_nsupdate, (sizeof old_nsupdate) - 1,
1119 "old name service update routine", 0);
1120 if (result != ISC_R_SUCCESS)
1121 log_fatal ("can't begin parsing old ddns updater!");
1122
1123 tmp = 0;
1124 if (!(parse_executable_statements (e, parse,
1125 &tmp, context_any))) {
1126 end_parse (&parse);
1127 log_fatal ("can't parse standard ddns updater!");
1128 }
1129 end_parse (&parse);
1130 }
1131 #endif
1132 }
1133
1134 void postdb_startup (void)
1135 {
1136 /* Initialize the omapi listener state. */
1137 if (omapi_port != -1) {
1138 omapi_listener_start (0);
1139 }
1140
1141 #if defined (FAILOVER_PROTOCOL)
1142 /* Initialize the failover listener state. */
1143 dhcp_failover_startup ();
1144 #endif
1145
1146 /*
1147 * Begin our lease timeout background task.
1148 */
1149 schedule_all_ipv6_lease_timeouts();
1150 }
1151
1152 /* Print usage message. */
1153
1154 static void
1155 usage(void) {
1156 log_info("%s %s", message, PACKAGE_VERSION);
1157 log_info(copyright);
1158 log_info(arr);
1159
1160 log_fatal("Usage: dhcpd [-p <UDP port #>] [-f] [-d] [-q] [-t|-T]\n"
1161 #ifdef DHCPv6
1162 " [-4|-6] [-cf config-file] [-lf lease-file]\n"
1163 #else /* !DHCPv6 */
1164 " [-cf config-file] [-lf lease-file]\n"
1165 #endif /* DHCPv6 */
1166 #if defined (PARANOIA)
1167 /* meld into the following string */
1168 " [-user user] [-group group] [-chroot dir]\n"
1169 #endif /* PARANOIA */
1170 #if defined (TRACING)
1171 " [-tf trace-output-file]\n"
1172 " [-play trace-input-file]\n"
1173 #endif /* TRACING */
1174 " [-pf pid-file] [-s server] [if0 [...ifN]]");
1175 }
1176
1177 void lease_pinged (from, packet, length)
1178 struct iaddr from;
1179 u_int8_t *packet;
1180 int length;
1181 {
1182 struct lease *lp;
1183
1184 /* Don't try to look up a pinged lease if we aren't trying to
1185 ping one - otherwise somebody could easily make us churn by
1186 just forging repeated ICMP EchoReply packets for us to look
1187 up. */
1188 if (!outstanding_pings)
1189 return;
1190
1191 lp = (struct lease *)0;
1192 if (!find_lease_by_ip_addr (&lp, from, MDL)) {
1193 log_debug ("unexpected ICMP Echo Reply from %s",
1194 piaddr (from));
1195 return;
1196 }
1197
1198 if (!lp -> state) {
1199 #if defined (FAILOVER_PROTOCOL)
1200 if (!lp -> pool ||
1201 !lp -> pool -> failover_peer)
1202 #endif
1203 log_debug ("ICMP Echo Reply for %s late or spurious.",
1204 piaddr (from));
1205 goto out;
1206 }
1207
1208 if (lp -> ends > cur_time) {
1209 log_debug ("ICMP Echo reply while lease %s valid.",
1210 piaddr (from));
1211 }
1212
1213 /* At this point it looks like we pinged a lease and got a
1214 response, which shouldn't have happened. */
1215 data_string_forget (&lp -> state -> parameter_request_list, MDL);
1216 free_lease_state (lp -> state, MDL);
1217 lp -> state = (struct lease_state *)0;
1218
1219 abandon_lease (lp, "pinged before offer");
1220 cancel_timeout (lease_ping_timeout, lp);
1221 --outstanding_pings;
1222 out:
1223 lease_dereference (&lp, MDL);
1224 }
1225
1226 void lease_ping_timeout (vlp)
1227 void *vlp;
1228 {
1229 struct lease *lp = vlp;
1230
1231 #if defined (DEBUG_MEMORY_LEAKAGE)
1232 unsigned long previous_outstanding = dmalloc_outstanding;
1233 #endif
1234
1235 --outstanding_pings;
1236 dhcp_reply (lp);
1237
1238 #if defined (DEBUG_MEMORY_LEAKAGE)
1239 log_info ("generation %ld: %ld new, %ld outstanding, %ld long-term",
1240 dmalloc_generation,
1241 dmalloc_outstanding - previous_outstanding,
1242 dmalloc_outstanding, dmalloc_longterm);
1243 #endif
1244 #if defined (DEBUG_MEMORY_LEAKAGE)
1245 dmalloc_dump_outstanding ();
1246 #endif
1247 }
1248
1249 int dhcpd_interface_setup_hook (struct interface_info *ip, struct iaddr *ia)
1250 {
1251 struct subnet *subnet;
1252 struct shared_network *share;
1253 isc_result_t status;
1254
1255 /* Special case for fallback network - not sure why this is
1256 necessary. */
1257 if (!ia) {
1258 const char *fnn = "fallback-net";
1259 status = shared_network_allocate (&ip -> shared_network, MDL);
1260 if (status != ISC_R_SUCCESS)
1261 log_fatal ("No memory for shared subnet: %s",
1262 isc_result_totext (status));
1263 ip -> shared_network -> name = dmalloc (strlen (fnn) + 1, MDL);
1264 strcpy (ip -> shared_network -> name, fnn);
1265 return 1;
1266 }
1267
1268 /* If there's a registered subnet for this address,
1269 connect it together... */
1270 subnet = (struct subnet *)0;
1271 if (find_subnet (&subnet, *ia, MDL)) {
1272 /* If this interface has multiple aliases on the same
1273 subnet, ignore all but the first we encounter. */
1274 if (!subnet -> interface) {
1275 interface_reference (&subnet -> interface, ip, MDL);
1276 subnet -> interface_address = *ia;
1277 } else if (subnet -> interface != ip) {
1278 log_error ("Multiple interfaces match the %s: %s %s",
1279 "same subnet",
1280 subnet -> interface -> name, ip -> name);
1281 }
1282 share = subnet -> shared_network;
1283 if (ip -> shared_network &&
1284 ip -> shared_network != share) {
1285 log_fatal ("Interface %s matches multiple shared %s",
1286 ip -> name, "networks");
1287 } else {
1288 if (!ip -> shared_network)
1289 shared_network_reference
1290 (&ip -> shared_network, share, MDL);
1291 }
1292
1293 if (!share -> interface) {
1294 interface_reference (&share -> interface, ip, MDL);
1295 } else if (share -> interface != ip) {
1296 log_error ("Multiple interfaces match the %s: %s %s",
1297 "same shared network",
1298 share -> interface -> name, ip -> name);
1299 }
1300 subnet_dereference (&subnet, MDL);
1301 }
1302 return 1;
1303 }
1304
1305 static TIME shutdown_time;
1306 static int omapi_connection_count;
1307 enum dhcp_shutdown_state shutdown_state;
1308
1309 isc_result_t dhcp_io_shutdown (omapi_object_t *obj, void *foo)
1310 {
1311 /* Shut down all listeners. */
1312 if (shutdown_state == shutdown_listeners &&
1313 obj -> type == omapi_type_listener &&
1314 obj -> inner &&
1315 obj -> inner -> type == omapi_type_protocol_listener) {
1316 omapi_listener_destroy (obj, MDL);
1317 return ISC_R_SUCCESS;
1318 }
1319
1320 /* Shut down all existing omapi connections. */
1321 if (obj -> type == omapi_type_connection &&
1322 obj -> inner &&
1323 obj -> inner -> type == omapi_type_protocol) {
1324 if (shutdown_state == shutdown_drop_omapi_connections) {
1325 omapi_disconnect (obj, 1);
1326 }
1327 omapi_connection_count++;
1328 if (shutdown_state == shutdown_omapi_connections) {
1329 omapi_disconnect (obj, 0);
1330 return ISC_R_SUCCESS;
1331 }
1332 }
1333
1334 /* Shutdown all DHCP interfaces. */
1335 if (obj -> type == dhcp_type_interface &&
1336 shutdown_state == shutdown_dhcp) {
1337 dhcp_interface_remove (obj, (omapi_object_t *)0);
1338 return ISC_R_SUCCESS;
1339 }
1340 return ISC_R_SUCCESS;
1341 }
1342
1343 static isc_result_t dhcp_io_shutdown_countdown (void *vlp)
1344 {
1345 #if defined (FAILOVER_PROTOCOL)
1346 dhcp_failover_state_t *state;
1347 int failover_connection_count = 0;
1348 #endif
1349 struct timeval tv;
1350
1351 oncemore:
1352 if (shutdown_state == shutdown_listeners ||
1353 shutdown_state == shutdown_omapi_connections ||
1354 shutdown_state == shutdown_drop_omapi_connections ||
1355 shutdown_state == shutdown_dhcp) {
1356 omapi_connection_count = 0;
1357 omapi_io_state_foreach (dhcp_io_shutdown, 0);
1358 }
1359
1360 if ((shutdown_state == shutdown_listeners ||
1361 shutdown_state == shutdown_omapi_connections ||
1362 shutdown_state == shutdown_drop_omapi_connections) &&
1363 omapi_connection_count == 0) {
1364 shutdown_state = shutdown_dhcp;
1365 shutdown_time = cur_time;
1366 goto oncemore;
1367 } else if (shutdown_state == shutdown_listeners &&
1368 cur_time - shutdown_time > 4) {
1369 shutdown_state = shutdown_omapi_connections;
1370 shutdown_time = cur_time;
1371 } else if (shutdown_state == shutdown_omapi_connections &&
1372 cur_time - shutdown_time > 4) {
1373 shutdown_state = shutdown_drop_omapi_connections;
1374 shutdown_time = cur_time;
1375 } else if (shutdown_state == shutdown_drop_omapi_connections &&
1376 cur_time - shutdown_time > 4) {
1377 shutdown_state = shutdown_dhcp;
1378 shutdown_time = cur_time;
1379 goto oncemore;
1380 } else if (shutdown_state == shutdown_dhcp &&
1381 cur_time - shutdown_time > 4) {
1382 shutdown_state = shutdown_done;
1383 shutdown_time = cur_time;
1384 }
1385
1386 #if defined (FAILOVER_PROTOCOL)
1387 /* Set all failover peers into the shutdown state. */
1388 if (shutdown_state == shutdown_dhcp) {
1389 for (state = failover_states; state; state = state -> next) {
1390 if (state -> me.state == normal) {
1391 dhcp_failover_set_state (state, shut_down);
1392 failover_connection_count++;
1393 }
1394 if (state -> me.state == shut_down &&
1395 state -> partner.state != partner_down)
1396 failover_connection_count++;
1397 }
1398 }
1399
1400 if (shutdown_state == shutdown_done) {
1401 for (state = failover_states; state; state = state -> next) {
1402 if (state -> me.state == shut_down) {
1403 if (state -> link_to_peer)
1404 dhcp_failover_link_dereference (&state -> link_to_peer,
1405 MDL);
1406 dhcp_failover_set_state (state, recover);
1407 }
1408 }
1409 #if defined (DEBUG_MEMORY_LEAKAGE) && \
1410 defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
1411 free_everything ();
1412 omapi_print_dmalloc_usage_by_caller ();
1413 #endif
1414 exit (0);
1415 }
1416 #else
1417 if (shutdown_state == shutdown_done) {
1418 #if defined (DEBUG_MEMORY_LEAKAGE) && \
1419 defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
1420 free_everything ();
1421 omapi_print_dmalloc_usage_by_caller ();
1422 #endif
1423 exit (0);
1424 }
1425 #endif
1426 if (shutdown_state == shutdown_dhcp &&
1427 #if defined(FAILOVER_PROTOCOL)
1428 !failover_connection_count &&
1429 #endif
1430 ISC_TRUE) {
1431 shutdown_state = shutdown_done;
1432 shutdown_time = cur_time;
1433 goto oncemore;
1434 }
1435 tv.tv_sec = cur_tv.tv_sec + 1;
1436 tv.tv_usec = cur_tv.tv_usec;
1437 add_timeout (&tv,
1438 (void (*)(void *))dhcp_io_shutdown_countdown, 0, 0, 0);
1439 return ISC_R_SUCCESS;
1440 }
1441
1442 isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
1443 control_object_state_t newstate)
1444 {
1445 if (newstate == server_shutdown) {
1446 shutdown_time = cur_time;
1447 shutdown_state = shutdown_listeners;
1448 dhcp_io_shutdown_countdown (0);
1449 return ISC_R_SUCCESS;
1450 }
1451 return ISC_R_INVALIDARG;
1452 }