6 * Copyright (c) 2004-2020 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1996-2003 by Internet Software Consortium
9 * This Source Code Form is subject to the terms of the Mozilla Public
10 * License, v. 2.0. If a copy of the MPL was not distributed with this
11 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
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.
21 * Internet Systems Consortium, Inc.
23 * Newmarket, NH 03857 USA
25 * https://www.isc.org/
29 static const char copyright
[] =
30 "Copyright 2004-2020 Internet Systems Consortium.";
31 static const char arr
[] = "All rights reserved.";
32 static const char message
[] = "Internet Systems Consortium DHCP Server";
33 static const char url
[] =
34 "For info, please visit https://www.isc.org/software/dhcp/";
37 #include <omapip/omapip_p.h>
42 #include <sys/types.h>
46 #if defined (PARANOIA)
47 # include <sys/types.h>
50 /* get around the ISC declaration of group */
51 # define group real_group
55 /* global values so db.c can look at them */
60 struct class unknown_class
;
61 struct class known_class
;
63 struct iaddr server_identifier
;
64 int server_identifier_matched
;
66 #if defined (NSUPDATE)
68 /* This stuff is always executed to figure the default values for certain
70 char std_nsupdate
[] = " \n\
71 option server.ddns-hostname = \n\
72 pick (option fqdn.hostname, option host-name, config-option host-name); \n\
73 option server.ddns-domainname = config-option domain-name; \n\
74 option server.ddns-rev-domainname = \"in-addr.arpa.\";";
76 /* Stores configured DDNS conflict detection flags */
77 u_int16_t ddns_conflict_mask
;
80 int ddns_update_style
;
81 int dont_use_fsync
= 0; /* 0 = default, use fsync, 1 = don't use fsync */
82 int server_id_check
= 0; /* 0 = default, don't check server id, 1 = do check */
85 int prefix_length_mode
= PLM_PREFER
;
86 int do_release_on_roam
= 0; /* 0 = default, do not release v6 leases on roam */
90 int persist_eui64
= 1; /* 1 = write EUI64 leases to disk, 0 = don't */
93 int authoring_byte_order
= 0; /* 0 = not set */
94 int lease_id_format
= TOKEN_OCTAL
; /* octal by default */
95 u_int32_t abandon_lease_time
= DEFAULT_ABANDON_LEASE_TIME
;
97 const char *path_dhcpd_conf
= _PATH_DHCPD_CONF
;
98 const char *path_dhcpd_db
= _PATH_DHCPD_DB
;
99 const char *path_dhcpd_pid
= _PATH_DHCPD_PID
;
100 /* False (default) => we write and use a pid file */
101 isc_boolean_t no_pid_file
= ISC_FALSE
;
103 int dhcp_max_agent_option_packet_length
= DHCP_MTU_MAX
;
105 static omapi_auth_key_t
*omapi_key
= (omapi_auth_key_t
*)0;
108 #if defined (TRACING)
109 trace_type_t
*trace_srandom
;
114 static isc_result_t
verify_addr (omapi_object_t
*l
, omapi_addr_t
*addr
) {
115 return ISC_R_SUCCESS
;
118 static isc_result_t
verify_auth (omapi_object_t
*p
, omapi_auth_key_t
*a
) {
120 return DHCP_R_INVALIDKEY
;
121 return ISC_R_SUCCESS
;
124 static void omapi_listener_start (void *foo
)
126 omapi_object_t
*listener
;
130 listener
= (omapi_object_t
*)0;
131 result
= omapi_generic_new (&listener
, MDL
);
132 if (result
!= ISC_R_SUCCESS
)
133 log_fatal ("Can't allocate new generic object: %s",
134 isc_result_totext (result
));
135 result
= omapi_protocol_listen (listener
,
136 (unsigned)omapi_port
, 1);
137 if (result
== ISC_R_SUCCESS
&& omapi_key
)
138 result
= omapi_protocol_configure_security
139 (listener
, verify_addr
, verify_auth
);
140 if (result
!= ISC_R_SUCCESS
) {
141 log_error ("Can't start OMAPI protocol: %s",
142 isc_result_totext (result
));
143 tv
.tv_sec
= cur_tv
.tv_sec
+ 5;
144 tv
.tv_usec
= cur_tv
.tv_usec
;
145 add_timeout (&tv
, omapi_listener_start
, 0, 0, 0);
147 omapi_object_dereference (&listener
, MDL
);
152 #define DHCPD_USAGE0 \
153 "[-p <UDP port #>] [-f] [-d] [-q] [-t|-T]\n"
157 #define DHCPD_USAGE1 \
158 " [-4|-6] [-4o6 <port>]\n" \
159 " [-cf config-file] [-lf lease-file]\n"
161 #define DHCPD_USAGE1 \
162 " [-4|-6] [-cf config-file] [-lf lease-file]\n"
165 #define DHCPD_USAGE1 \
166 " [-cf config-file] [-lf lease-file]\n"
169 #if defined (PARANOIA)
170 #define DHCPD_USAGEP \
171 " [-user user] [-group group] [-chroot dir]\n"
173 #define DHCPD_USAGEP ""
174 #endif /* PARANOIA */
176 #if defined (TRACING)
177 #define DHCPD_USAGET \
178 " [-tf trace-output-file]\n" \
179 " [-play trace-input-file]\n"
181 #define DHCPD_USAGET ""
184 #define DHCPD_USAGEC \
185 " [-pf pid-file] [--no-pid] [-s server]\n" \
188 #define DHCPD_USAGEH "{--version|--help|-h}"
192 * \brief Print the generic usage message
194 * If the user has provided an incorrect command line print out
195 * the description of the command line. The arguments provide
196 * a way for the caller to request more specific information about
197 * the error be printed as well. Mostly this will be that some
198 * command doesn't include its argument.
200 * \param sfmt - The basic string and format for the specific error
201 * \param sarg - Generally the offending argument from the command line.
205 static char use_noarg
[] = "No argument for command: %s ";
208 usage(const char *sfmt
, const char *sarg
) {
209 log_info("%s %s", message
, PACKAGE_VERSION
);
214 /* If desired print out the specific error message */
215 #ifdef PRINT_SPECIFIC_CL_ERRORS
217 log_error(sfmt
, sarg
);
220 log_fatal("Usage: %s %s%s%s%s%s\n %s %s",
221 isc_file_basename(progname
),
227 isc_file_basename(progname
),
231 /* Note: If we add unit tests to test setup_chroot it will
232 * need to be moved to be outside the ifndef UNIT_TEST block.
235 #if defined (PARANOIA)
236 /* to be used in one of two possible scenarios */
237 static void setup_chroot (char *chroot_dir
) {
239 log_fatal ("you must be root to use chroot");
241 if (chroot(chroot_dir
)) {
242 log_fatal ("chroot(\"%s\"): %m", chroot_dir
);
245 /* probably permission denied */
246 log_fatal ("chdir(\"/\"): %m");
249 #endif /* PARANOIA */
252 main(int argc
, char **argv
) {
263 int dfd
[2] = { -1, -1 };
266 char *server
= (char *)0;
269 struct interface_info
*ip
;
270 #if defined (NSUPDATE)
274 int have_dhcpd_conf
= 0;
275 int have_dhcpd_db
= 0;
276 int have_dhcpd_pid
= 0;
278 int local_family_set
= 0;
280 u_int16_t dhcp4o6_port
= 0;
283 #if defined (TRACING)
284 char *traceinfile
= (char *)0;
285 char *traceoutfile
= (char *)0;
288 #if defined (PARANOIA)
291 char *set_chroot
= 0;
292 #endif /* PARANOIA */
300 /* Make sure that file descriptors 0 (stdin), 1, (stdout), and
301 2 (stderr) are open. To do this, we assume that when we
302 open a file the lowest available file descriptor is used. */
303 fd
= open("/dev/null", O_RDWR
);
305 fd
= open("/dev/null", O_RDWR
);
307 fd
= open("/dev/null", O_RDWR
);
309 log_perror
= 0; /* No sense logging to /dev/null. */
313 /* Parse arguments changing daemon */
314 for (i
= 1; i
< argc
; i
++) {
315 if (!strcmp (argv
[i
], "-f")) {
319 } else if (!strcmp (argv
[i
], "-d")) {
323 } else if (!strcmp (argv
[i
], "-t")) {
327 } else if (!strcmp (argv
[i
], "-T")) {
331 } else if (!strcmp (argv
[i
], "--version")) {
332 const char vstring
[] = "isc-dhcpd-";
333 IGNORE_RET(write(STDERR_FILENO
, vstring
,
335 IGNORE_RET(write(STDERR_FILENO
,
337 strlen(PACKAGE_VERSION
)));
338 IGNORE_RET(write(STDERR_FILENO
, "\n", 1));
340 } else if (!strcmp(argv
[i
], "--help") ||
341 !strcmp(argv
[i
], "-h")) {
342 const char *pname
= isc_file_basename(progname
);
343 IGNORE_RET(write(STDERR_FILENO
, "Usage: ", 7));
344 IGNORE_RET(write(STDERR_FILENO
, pname
, strlen(pname
)));
345 IGNORE_RET(write(STDERR_FILENO
, " ", 1));
346 IGNORE_RET(write(STDERR_FILENO
, DHCPD_USAGE0
,
347 strlen(DHCPD_USAGE0
)));
348 IGNORE_RET(write(STDERR_FILENO
, DHCPD_USAGE1
,
349 strlen(DHCPD_USAGE1
)));
350 #if defined (PARANOIA)
351 IGNORE_RET(write(STDERR_FILENO
, DHCPD_USAGEP
,
352 strlen(DHCPD_USAGEP
)));
354 #if defined (TRACING)
355 IGNORE_RET(write(STDERR_FILENO
, DHCPD_USAGET
,
356 strlen(DHCPD_USAGET
)));
358 IGNORE_RET(write(STDERR_FILENO
, DHCPD_USAGEC
,
359 strlen(DHCPD_USAGEC
)));
360 IGNORE_RET(write(STDERR_FILENO
, "\n", 1));
361 IGNORE_RET(write(STDERR_FILENO
, " ", 7));
362 IGNORE_RET(write(STDERR_FILENO
, pname
, strlen(pname
)));
363 IGNORE_RET(write(STDERR_FILENO
, " ", 1));
364 IGNORE_RET(write(STDERR_FILENO
, DHCPD_USAGEH
,
365 strlen(DHCPD_USAGEH
)));
366 IGNORE_RET(write(STDERR_FILENO
, "\n", 1));
369 } else if (!strcmp (argv
[i
], "-play")) {
378 /* When not forbidden prepare to become a daemon */
381 log_fatal("Can't get pipe: %m");
382 if ((pid
= fork ()) < 0)
383 log_fatal("Can't fork daemon: %m");
385 /* Parent: wait for the child to start */
388 (void) close(dfd
[1]);
392 n
= read(dfd
[0], &buf
, 1);
395 } while (n
== -1 && errno
== EINTR
);
399 (void) close(dfd
[0]);
403 /* Set up the isc and dns library managers */
404 status
= dhcp_context_create(DHCP_CONTEXT_PRE_DB
,
406 if (status
!= ISC_R_SUCCESS
)
407 log_fatal("Can't initialize context: %s",
408 isc_result_totext(status
));
410 /* Set up the client classification system. */
411 classification_setup ();
413 /* Initialize the omapi system. */
414 result
= omapi_init ();
415 if (result
!= ISC_R_SUCCESS
)
416 log_fatal ("Can't initialize OMAPI: %s",
417 isc_result_totext (result
));
419 /* Set up the OMAPI wrappers for common objects. */
420 dhcp_db_objects_setup ();
421 /* Set up the OMAPI wrappers for various server database internal
423 dhcp_common_objects_setup ();
425 /* Initially, log errors to stderr as well as to syslogd. */
426 openlog (isc_file_basename(progname
),
427 DHCP_LOG_OPTIONS
, DHCPD_LOG_FACILITY
);
429 for (i
= 1; i
< argc
; i
++) {
430 if (!strcmp (argv
[i
], "-p")) {
432 usage(use_noarg
, argv
[i
-1]);
433 local_port
= validate_port (argv
[i
]);
434 log_debug ("binding to user-specified port %d",
436 } else if (!strcmp (argv
[i
], "-f")) {
440 } else if (!strcmp (argv
[i
], "-d")) {
445 } else if (!strcmp (argv
[i
], "-s")) {
447 usage(use_noarg
, argv
[i
-1]);
449 #if defined (PARANOIA)
450 } else if (!strcmp (argv
[i
], "-user")) {
452 usage(use_noarg
, argv
[i
-1]);
454 } else if (!strcmp (argv
[i
], "-group")) {
456 usage(use_noarg
, argv
[i
-1]);
457 set_group
= argv
[i
];
458 } else if (!strcmp (argv
[i
], "-chroot")) {
460 usage(use_noarg
, argv
[i
-1]);
461 set_chroot
= argv
[i
];
462 #endif /* PARANOIA */
463 } else if (!strcmp (argv
[i
], "-cf")) {
465 usage(use_noarg
, argv
[i
-1]);
466 path_dhcpd_conf
= argv
[i
];
468 } else if (!strcmp (argv
[i
], "-lf")) {
470 usage(use_noarg
, argv
[i
-1]);
471 path_dhcpd_db
= argv
[i
];
473 } else if (!strcmp (argv
[i
], "-pf")) {
475 usage(use_noarg
, argv
[i
-1]);
476 path_dhcpd_pid
= argv
[i
];
478 } else if (!strcmp(argv
[i
], "--no-pid")) {
479 no_pid_file
= ISC_TRUE
;
480 } else if (!strcmp (argv
[i
], "-t")) {
481 /* test configurations only */
487 } else if (!strcmp (argv
[i
], "-T")) {
488 /* test configurations and lease file only */
495 } else if (!strcmp (argv
[i
], "-q")) {
497 quiet_interface_discovery
= 1;
499 } else if (!strcmp(argv
[i
], "-4")) {
500 if (local_family_set
&& (local_family
!= AF_INET
)) {
501 log_fatal("Server cannot run in both IPv4 and "
502 "IPv6 mode at the same time.");
504 local_family
= AF_INET
;
505 local_family_set
= 1;
506 } else if (!strcmp(argv
[i
], "-6")) {
507 if (local_family_set
&& (local_family
!= AF_INET6
)) {
508 log_fatal("Server cannot run in both IPv4 and "
509 "IPv6 mode at the same time.");
511 local_family
= AF_INET6
;
512 local_family_set
= 1;
514 } else if (!strcmp(argv
[i
], "-4o6")) {
516 usage(use_noarg
, argv
[i
-1]);
517 dhcp4o6_port
= validate_port_pair(argv
[i
]);
519 log_debug("DHCPv4 over DHCPv6 over ::1 port %d and %d",
521 ntohs(dhcp4o6_port
) + 1);
522 dhcpv4_over_dhcpv6
= 1;
525 #if defined (TRACING)
526 } else if (!strcmp (argv
[i
], "-tf")) {
528 usage(use_noarg
, argv
[i
-1]);
529 traceoutfile
= argv
[i
];
530 } else if (!strcmp (argv
[i
], "-play")) {
532 usage(use_noarg
, argv
[i
-1]);
533 traceinfile
= argv
[i
];
534 trace_replay_init ();
536 } else if (argv
[i
][0] == '-') {
537 usage("Unknown command %s", argv
[i
]);
539 struct interface_info
*tmp
=
540 (struct interface_info
*)0;
541 if (strlen(argv
[i
]) >= sizeof(tmp
->name
))
542 log_fatal("%s: interface name too long "
544 argv
[i
], (long)strlen(argv
[i
]));
545 result
= interface_allocate (&tmp
, MDL
);
546 if (result
!= ISC_R_SUCCESS
)
547 log_fatal ("Insufficient memory to %s %s: %s",
548 "record interface", argv
[i
],
549 isc_result_totext (result
));
550 strcpy (tmp
-> name
, argv
[i
]);
552 interface_reference (&tmp
-> next
,
554 interface_dereference (&interfaces
, MDL
);
556 interface_reference (&interfaces
, tmp
, MDL
);
557 tmp
-> flags
= INTERFACE_REQUESTED
;
561 #if defined(DHCPv6) && defined(DHCP4o6)
562 if (dhcpv4_over_dhcpv6
) {
563 if (!local_family_set
)
564 log_error("please specify the address family "
565 "with DHPv4 over DHCPv6 [-4|-6].");
566 if ((local_family
== AF_INET
) && (interfaces
!= NULL
))
567 log_fatal("DHCPv4 server in DHPv4 over DHCPv6 "
568 "mode with command line specified "
571 #endif /* DHCPv6 && DHCP4o6 */
573 if (!have_dhcpd_conf
&& (s
= getenv ("PATH_DHCPD_CONF"))) {
578 if (local_family
== AF_INET6
) {
579 /* DHCPv6: override DHCPv4 lease and pid filenames */
580 if (!have_dhcpd_db
) {
581 if ((s
= getenv ("PATH_DHCPD6_DB")))
584 path_dhcpd_db
= _PATH_DHCPD6_DB
;
586 if (!have_dhcpd_pid
) {
587 if ((s
= getenv ("PATH_DHCPD6_PID")))
590 path_dhcpd_pid
= _PATH_DHCPD6_PID
;
595 if (!have_dhcpd_db
&& (s
= getenv ("PATH_DHCPD_DB"))) {
599 if (!have_dhcpd_pid
&& (s
= getenv ("PATH_DHCPD_PID"))) {
606 * convert relative path names to absolute, for files that need
607 * to be reopened after chdir() has been called
609 if (have_dhcpd_db
&& path_dhcpd_db
[0] != '/') {
610 path_dhcpd_db
= absolute_path(path_dhcpd_db
);
614 log_info("%s %s", message
, PACKAGE_VERSION
);
615 log_info (copyright
);
622 #if defined (TRACING)
623 trace_init (set_time
, MDL
);
625 result
= trace_begin (traceoutfile
, MDL
);
626 if (result
!= ISC_R_SUCCESS
)
627 log_fatal ("Unable to begin trace: %s",
628 isc_result_totext (result
));
630 interface_trace_setup ();
631 parse_trace_setup ();
632 trace_srandom
= trace_type_register ("random-seed", (void *)0,
634 trace_seed_stop
, MDL
);
635 #if defined (NSUPDATE)
637 #endif /* NSUPDATE */
640 #if defined (PARANOIA)
641 /* get user and group info if those options were given */
643 struct passwd
*tmp_pwd
;
646 log_fatal ("you must be root to set user");
648 if (!(tmp_pwd
= getpwnam(set_user
)))
649 log_fatal ("no such user: %s", set_user
);
651 set_uid
= tmp_pwd
->pw_uid
;
653 /* use the user's group as the default gid */
655 set_gid
= tmp_pwd
->pw_gid
;
659 /* get around the ISC declaration of group */
660 #define group real_group
661 struct group
*tmp_grp
;
664 log_fatal ("you must be root to set group");
666 if (!(tmp_grp
= getgrnam(set_group
)))
667 log_fatal ("no such group: %s", set_group
);
669 set_gid
= tmp_grp
->gr_gid
;
673 # if defined (EARLY_CHROOT)
674 if (set_chroot
) setup_chroot (set_chroot
);
675 # endif /* EARLY_CHROOT */
676 #endif /* PARANOIA */
678 /* Default to the DHCP/BOOTP port. */
681 if ((s
= getenv ("DHCPD_PORT"))) {
682 local_port
= validate_port (s
);
683 log_debug ("binding to environment-specified port %d",
686 if (local_family
== AF_INET
) {
687 ent
= getservbyname("dhcp", "udp");
689 local_port
= htons(67);
691 local_port
= ent
->s_port
;
694 /* INSIST(local_family == AF_INET6); */
695 ent
= getservbyname("dhcpv6-server", "udp");
697 local_port
= htons(547);
699 local_port
= ent
->s_port
;
702 #ifndef __CYGWIN32__ /* XXX */
708 if (local_family
== AF_INET
) {
709 remote_port
= htons(ntohs(local_port
) + 1);
711 /* INSIST(local_family == AF_INET6); */
712 ent
= getservbyname("dhcpv6-client", "udp");
714 remote_port
= htons(546);
716 remote_port
= ent
->s_port
;
721 if (local_family
!= AF_INET
) {
722 log_fatal("You can only specify address to send "
723 "replies to when running an IPv4 server.");
725 if (!inet_aton (server
, &limited_broadcast
)) {
727 he
= gethostbyname (server
);
729 memcpy (&limited_broadcast
,
730 he
-> h_addr_list
[0],
731 sizeof limited_broadcast
);
733 limited_broadcast
.s_addr
= INADDR_BROADCAST
;
736 limited_broadcast
.s_addr
= INADDR_BROADCAST
;
739 /* Get the current time... */
740 gettimeofday(&cur_tv
, NULL
);
742 /* Set up the initial dhcp option universe. */
743 initialize_common_option_spaces ();
744 initialize_server_option_spaces ();
746 /* Add the ddns update style enumeration prior to parsing. */
747 add_enumeration (&ddns_styles
);
748 add_enumeration (&syslog_enum
);
749 #if defined (LDAP_CONFIGURATION)
750 add_enumeration (&ldap_methods
);
751 #if defined (LDAP_USE_SSL)
752 add_enumeration (&ldap_ssl_usage_enum
);
753 add_enumeration (&ldap_tls_reqcert_enum
);
754 add_enumeration (&ldap_tls_crlcheck_enum
);
758 if (!group_allocate (&root_group
, MDL
))
759 log_fatal ("Can't allocate root group!");
760 root_group
-> authoritative
= 0;
762 /* Set up various hooks. */
763 dhcp_interface_setup_hook
= dhcpd_interface_setup_hook
;
764 bootp_packet_handler
= do_packet
;
766 add_enumeration (&prefix_length_modes
);
767 dhcpv6_packet_handler
= do_packet6
;
770 #if defined (NSUPDATE)
771 /* Set up the standard name service updater routine. */
773 status
= new_parse(&parse
, -1, std_nsupdate
, sizeof(std_nsupdate
) - 1,
774 "standard name service update routine", 0);
775 if (status
!= ISC_R_SUCCESS
)
776 log_fatal ("can't begin parsing name service updater!");
780 if (!(parse_executable_statements(&root_group
->statements
,
781 parse
, &lose
, context_any
))) {
783 log_fatal("can't parse standard name service updater!");
789 /* Initialize icmp support... */
790 if (!cftest
&& !lftest
)
791 icmp_startup (1, lease_pinged
);
793 #if defined (TRACING)
795 if (!have_dhcpd_db
) {
796 log_error ("%s", "");
797 log_error ("** You must specify a lease file with -lf.");
798 log_error (" Dhcpd will not overwrite your default");
799 log_fatal (" lease file when playing back a trace. **");
801 trace_file_replay (traceinfile
);
803 #if defined (DEBUG_MEMORY_LEAKAGE) && \
804 defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
806 omapi_print_dmalloc_usage_by_caller ();
814 /* set up DHCPv6 hashes */
815 if (!ia_new_hash(&ia_na_active
, DEFAULT_HASH_SIZE
, MDL
)) {
816 log_fatal("Out of memory creating hash for active IA_NA.");
818 if (!ia_new_hash(&ia_ta_active
, DEFAULT_HASH_SIZE
, MDL
)) {
819 log_fatal("Out of memory creating hash for active IA_TA.");
821 if (!ia_new_hash(&ia_pd_active
, DEFAULT_HASH_SIZE
, MDL
)) {
822 log_fatal("Out of memory creating hash for active IA_PD.");
826 /* Read the dhcpd.conf file... */
827 if (readconf () != ISC_R_SUCCESS
)
828 log_fatal ("Configuration file errors encountered -- exiting");
830 postconf_initialization (quiet
);
832 #if defined (FAILOVER_PROTOCOL)
833 dhcp_failover_sanity_check();
836 #if defined(DHCPv6) && defined(DHCP4o6)
837 if (dhcpv4_over_dhcpv6
) {
838 if ((local_family
== AF_INET
) && (interfaces
!= NULL
))
839 log_fatal("DHCPv4 server in DHPv4 over DHCPv6 "
840 "mode with config file specified "
843 #endif /* DHCPv6 && DHCP4o6 */
845 #if defined (PARANOIA) && !defined (EARLY_CHROOT)
846 if (set_chroot
) setup_chroot (set_chroot
);
847 #endif /* PARANOIA && !EARLY_CHROOT */
850 /* log info about ipv6_ponds with large address ranges */
851 report_jumbo_ranges();
854 /* test option should cause an early exit */
855 if (cftest
&& !lftest
) {
860 * First part of dealing with pid files. Check to see if
861 * we should continue running or not. We run if:
862 * - we are testing the lease file out
863 * - we don't have a pid file to check
864 * - there is no other process running
866 if ((lftest
== 0) && (no_pid_file
== ISC_FALSE
)) {
867 /*Read previous pid file. */
868 if ((i
= open(path_dhcpd_pid
, O_RDONLY
)) >= 0) {
869 status
= read(i
, pbuf
, (sizeof pbuf
) - 1);
876 * If there was a previous server process and
877 * it is still running, abort
880 (pid
!= getpid() && kill(pid
, 0) == 0))
881 log_fatal("There's already a "
882 "DHCP server running.");
887 group_write_hook
= group_writer
;
889 /* Start up the database... */
895 /* Discover all the network interfaces and initialize them. */
896 #if defined(DHCPv6) && defined(DHCP4o6)
897 if (dhcpv4_over_dhcpv6
) {
898 int real_family
= local_family
;
899 local_family
= AF_INET6
;
900 /* The DHCPv4 side of DHCPv4-over-DHCPv6 service
901 uses a specific discovery which doesn't register
903 if (real_family
== AF_INET
)
904 discover_interfaces(DISCOVER_SERVER46
);
906 discover_interfaces(DISCOVER_SERVER
);
907 local_family
= real_family
;
909 #endif /* DHCPv6 && DHCP4o6 */
910 discover_interfaces(DISCOVER_SERVER
);
914 * Remove addresses from our pools that we should not issue
917 * We currently have no support for this in IPv4. It is not
918 * as important in IPv4, as making pools with ranges that
919 * leave out interfaces and hosts is fairly straightforward
920 * using range notation, but not so handy with CIDR notation.
922 if (local_family
== AF_INET6
) {
923 mark_hosts_unavailable();
924 mark_phosts_unavailable();
925 mark_interfaces_unavailable();
929 /* Make up a seed for the random number generator from current
930 time plus the sum of the last four bytes of each
931 interface's hardware address interpreted as an integer.
932 Not much entropy, but we're booting, so we're not likely to
933 find anything better. */
935 for (ip
= interfaces
; ip
; ip
= ip
-> next
) {
938 &ip
-> hw_address
.hbuf
[ip
-> hw_address
.hlen
-
939 sizeof seed
], sizeof seed
);
942 srandom (seed
+ cur_time
);
943 #if defined (TRACING)
944 trace_seed_stash (trace_srandom
, seed
+ cur_time
);
950 * Set server DHCPv6 identifier - we go in order:
951 * dhcp6.server-id in the config file
952 * server-duid from the lease file
953 * server-duid from the config file (the config file is read first
954 * and the lease file overwrites the config file information)
955 * generate a new one from the interface hardware addresses.
956 * In all cases we write it out to the lease file.
957 * See dhcpv6.c for discussion of setting DUID.
959 if ((set_server_duid_from_option() != ISC_R_SUCCESS
) &&
960 (!server_duid_isset()) &&
961 (generate_new_server_duid() != ISC_R_SUCCESS
)) {
962 log_fatal("Unable to set server identifier.");
966 if (dhcpv4_over_dhcpv6
)
967 dhcp4o6_setup(dhcp4o6_port
);
973 * Second part of dealing with pid files. Now
974 * that we have forked we can write our pid if
977 if (no_pid_file
== ISC_FALSE
) {
978 i
= open(path_dhcpd_pid
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0644);
980 sprintf(pbuf
, "%d\n", (int) getpid());
981 IGNORE_RET(write(i
, pbuf
, strlen(pbuf
)));
984 log_error("Can't create PID file %s: %m.",
989 #if defined (PARANOIA)
990 /* change uid to the specified one */
993 if (setgroups (0, (void *)0))
994 log_fatal ("setgroups: %m");
995 if (setgid (set_gid
))
996 log_fatal ("setgid(%d): %m", (int) set_gid
);
1000 if (setuid (set_uid
))
1001 log_fatal ("setuid(%d): %m", (int) set_uid
);
1003 #endif /* PARANOIA */
1005 /* If we were requested to log to stdout on the command line,
1006 keep doing so; otherwise, stop. */
1007 if (log_perror
== -1)
1013 if (dfd
[0] != -1 && dfd
[1] != -1) {
1016 if (write(dfd
[1], &buf
, 1) != 1)
1017 log_fatal("write to parent: %m");
1018 (void) close(dfd
[1]);
1019 dfd
[0] = dfd
[1] = -1;
1022 /* Become session leader and get pid... */
1025 /* Close standard I/O descriptors. */
1030 /* Reopen them on /dev/null. */
1031 (void) open("/dev/null", O_RDWR
);
1032 (void) open("/dev/null", O_RDWR
);
1033 (void) open("/dev/null", O_RDWR
);
1034 log_perror
= 0; /* No sense logging to /dev/null. */
1036 IGNORE_RET (chdir("/"));
1040 #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \
1041 defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
1042 dmalloc_cutoff_generation
= dmalloc_generation
;
1043 dmalloc_longterm
= dmalloc_outstanding
;
1044 dmalloc_outstanding
= 0;
1047 omapi_set_int_value ((omapi_object_t
*)dhcp_control_object
,
1048 (omapi_object_t
*)0, "state", server_running
);
1050 #if defined(ENABLE_GENTLE_SHUTDOWN)
1051 /* no signal handlers until we deal with the side effects */
1052 /* install signal handlers */
1053 signal(SIGINT
, dhcp_signal_handler
); /* control-c */
1054 signal(SIGTERM
, dhcp_signal_handler
); /* kill */
1057 /* Log that we are about to start working */
1058 log_info("Server starting service.");
1061 * Receive packets and dispatch them...
1062 * dispatch() will never return.
1066 /* Let's return status code */
1069 #endif /* !UNIT_TEST */
1071 void postconf_initialization (int quiet
)
1073 struct option_state
*options
= NULL
;
1074 struct data_string db
;
1075 struct option_cache
*oc
;
1077 isc_result_t result
;
1079 #if defined (NSUPDATE)
1080 struct in_addr local4
, *local4_ptr
= NULL
;
1081 struct in6_addr local6
, *local6_ptr
= NULL
;
1084 /* Now try to get the lease file name. */
1085 option_state_allocate(&options
, MDL
);
1087 execute_statements_in_scope(NULL
, NULL
, NULL
, NULL
, NULL
,
1088 options
, &global_scope
, root_group
,
1090 memset(&db
, 0, sizeof db
);
1091 oc
= lookup_option(&server_universe
, options
, SV_LEASE_FILE_NAME
);
1093 evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1094 &global_scope
, oc
, MDL
)) {
1095 s
= dmalloc(db
.len
+ 1, MDL
);
1097 log_fatal("no memory for lease db filename.");
1098 memcpy(s
, db
.data
, db
.len
);
1100 data_string_forget(&db
, MDL
);
1104 oc
= lookup_option(&server_universe
, options
, SV_PID_FILE_NAME
);
1106 evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1107 &global_scope
, oc
, MDL
)) {
1108 s
= dmalloc(db
.len
+ 1, MDL
);
1110 log_fatal("no memory for pid filename.");
1111 memcpy(s
, db
.data
, db
.len
);
1113 data_string_forget(&db
, MDL
);
1118 if (local_family
== AF_INET6
) {
1120 * Override lease file name with dhcpv6 lease file name,
1121 * if it was set; then, do the same with the pid file name
1123 oc
= lookup_option(&server_universe
, options
,
1124 SV_DHCPV6_LEASE_FILE_NAME
);
1126 evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1127 &global_scope
, oc
, MDL
)) {
1128 s
= dmalloc(db
.len
+ 1, MDL
);
1130 log_fatal("no memory for lease db filename.");
1131 memcpy(s
, db
.data
, db
.len
);
1133 data_string_forget(&db
, MDL
);
1137 oc
= lookup_option(&server_universe
, options
,
1138 SV_DHCPV6_PID_FILE_NAME
);
1140 evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1141 &global_scope
, oc
, MDL
)) {
1142 s
= dmalloc(db
.len
+ 1, MDL
);
1144 log_fatal("no memory for pid filename.");
1145 memcpy(s
, db
.data
, db
.len
);
1147 data_string_forget(&db
, MDL
);
1151 oc
= lookup_option(&server_universe
, options
,
1154 evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1155 &global_scope
, oc
, MDL
)) {
1157 memcpy(&local_address6
, db
.data
, 16);
1159 log_fatal("invalid local address "
1161 data_string_forget(&db
, MDL
);
1164 oc
= lookup_option(&server_universe
, options
,
1165 SV_BIND_LOCAL_ADDRESS6
);
1167 evaluate_boolean_option_cache(NULL
, NULL
, NULL
,
1168 NULL
, options
, NULL
,
1169 &global_scope
, oc
, MDL
)) {
1170 bind_local_address6
= 1;
1177 oc
= lookup_option(&server_universe
, options
, SV_OMAPI_PORT
);
1179 evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1180 &global_scope
, oc
, MDL
)) {
1182 omapi_port
= getUShort(db
.data
);
1184 log_fatal("invalid omapi port data length");
1185 data_string_forget(&db
, MDL
);
1188 oc
= lookup_option(&server_universe
, options
, SV_OMAPI_KEY
);
1190 evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1191 &global_scope
, oc
, MDL
)) {
1192 s
= dmalloc(db
.len
+ 1, MDL
);
1194 log_fatal("no memory for OMAPI key filename.");
1195 memcpy(s
, db
.data
, db
.len
);
1197 data_string_forget(&db
, MDL
);
1198 result
= omapi_auth_key_lookup_name(&omapi_key
, s
);
1200 if (result
!= ISC_R_SUCCESS
)
1201 log_fatal("OMAPI key %s: %s",
1202 s
, isc_result_totext (result
));
1205 oc
= lookup_option(&server_universe
, options
, SV_LOCAL_PORT
);
1207 evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1208 &global_scope
, oc
, MDL
)) {
1210 local_port
= htons(getUShort (db
.data
));
1212 log_fatal("invalid local port data length");
1213 data_string_forget(&db
, MDL
);
1216 oc
= lookup_option(&server_universe
, options
, SV_REMOTE_PORT
);
1218 evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1219 &global_scope
, oc
, MDL
)) {
1221 remote_port
= htons(getUShort (db
.data
));
1223 log_fatal("invalid remote port data length");
1224 data_string_forget(&db
, MDL
);
1227 oc
= lookup_option(&server_universe
, options
,
1228 SV_LIMITED_BROADCAST_ADDRESS
);
1230 evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1231 &global_scope
, oc
, MDL
)) {
1233 memcpy(&limited_broadcast
, db
.data
, 4);
1235 log_fatal("invalid broadcast address data length");
1236 data_string_forget(&db
, MDL
);
1239 oc
= lookup_option(&server_universe
, options
, SV_LOCAL_ADDRESS
);
1241 evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1242 &global_scope
, oc
, MDL
)) {
1244 memcpy(&local_address
, db
.data
, 4);
1246 log_fatal("invalid local address data length");
1247 data_string_forget(&db
, MDL
);
1250 oc
= lookup_option(&server_universe
, options
, SV_DDNS_UPDATE_STYLE
);
1252 if (evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1253 &global_scope
, oc
, MDL
)) {
1255 ddns_update_style
= db
.data
[0];
1257 log_fatal("invalid dns update type");
1258 data_string_forget(&db
, MDL
);
1261 ddns_update_style
= DDNS_UPDATE_STYLE_NONE
;
1263 #if defined (NSUPDATE)
1264 /* We no longer support ad_hoc, tell the user */
1265 if (ddns_update_style
== DDNS_UPDATE_STYLE_AD_HOC
) {
1266 log_fatal("ddns-update-style ad_hoc no longer supported");
1269 oc
= lookup_option(&server_universe
, options
, SV_DDNS_LOCAL_ADDRESS4
);
1271 if (evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1272 &global_scope
, oc
, MDL
)) {
1274 memcpy(&local4
, db
.data
, 4);
1275 local4_ptr
= &local4
;
1277 data_string_forget(&db
, MDL
);
1281 oc
= lookup_option(&server_universe
, options
, SV_DDNS_LOCAL_ADDRESS6
);
1283 if (evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1284 &global_scope
, oc
, MDL
)) {
1286 memcpy(&local6
, db
.data
, 16);
1287 local6_ptr
= &local6
;
1289 data_string_forget(&db
, MDL
);
1293 /* Don't init DNS client if update style is none. This avoids
1294 * listening ports that aren't needed. We don't use ddns-udpates
1295 * as that has multiple levels of scope. */
1296 if (ddns_update_style
!= DDNS_UPDATE_STYLE_NONE
) {
1297 if (dhcp_context_create(DHCP_CONTEXT_POST_DB
,
1298 local4_ptr
, local6_ptr
)
1300 log_fatal("Unable to complete ddns initialization");
1304 /* Set the conflict detection flag mask based on globally
1305 * defined DDNS configuration params. This mask should be
1306 * to init ddns_cb::flags before for every DDNS transaction. */
1307 ddns_conflict_mask
= get_conflict_mask(options
);
1310 /* If we don't have support for updates compiled in tell the user */
1311 if (ddns_update_style
!= DDNS_UPDATE_STYLE_NONE
) {
1312 log_fatal("Support for ddns-update-style not compiled in");
1317 log_info ("Config file: %s", path_dhcpd_conf
);
1318 log_info ("Database file: %s", path_dhcpd_db
);
1319 log_info ("PID file: %s", path_dhcpd_pid
);
1322 oc
= lookup_option(&server_universe
, options
, SV_LOG_FACILITY
);
1324 if (evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1325 &global_scope
, oc
, MDL
)) {
1328 openlog(isc_file_basename(progname
),
1329 DHCP_LOG_OPTIONS
, db
.data
[0]);
1330 /* Log the startup banner into the new
1332 /* Don't log to stderr twice. */
1335 log_info("%s %s", message
, PACKAGE_VERSION
);
1336 log_info(copyright
);
1341 log_fatal("invalid log facility");
1342 data_string_forget(&db
, MDL
);
1346 #if defined(DELAYED_ACK)
1347 oc
= lookup_option(&server_universe
, options
, SV_DELAYED_ACK
);
1349 evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1350 &global_scope
, oc
, MDL
)) {
1352 max_outstanding_acks
= htons(getUShort(db
.data
));
1354 log_fatal("invalid max delayed ACK count ");
1356 data_string_forget(&db
, MDL
);
1358 #if defined(DHCP4o6)
1359 /* Delayed acks and DHCPv4-over-DHCPv6 are incompatible */
1360 if (dhcpv4_over_dhcpv6
) {
1361 if (max_outstanding_acks
> 0) {
1362 log_debug("DHCP4o6 enabled, "
1363 "setting delayed-ack to zero (incompatible)");
1366 max_outstanding_acks
= 0;
1370 oc
= lookup_option(&server_universe
, options
, SV_MAX_ACK_DELAY
);
1372 evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1373 &global_scope
, oc
, MDL
)) {
1377 log_fatal("invalid max ack delay configuration");
1379 timeval
= getULong(db
.data
);
1380 max_ack_delay_secs
= timeval
/ 1000000;
1381 max_ack_delay_usecs
= timeval
% 1000000;
1383 data_string_forget(&db
, MDL
);
1387 oc
= lookup_option(&server_universe
, options
, SV_DONT_USE_FSYNC
);
1389 evaluate_boolean_option_cache(NULL
, NULL
, NULL
, NULL
, options
, NULL
,
1390 &global_scope
, oc
, MDL
)) {
1392 log_error("Not using fsync() to flush lease writes");
1395 oc
= lookup_option(&server_universe
, options
, SV_SERVER_ID_CHECK
);
1397 evaluate_boolean_option_cache(NULL
, NULL
, NULL
, NULL
, options
, NULL
,
1398 &global_scope
, oc
, MDL
)) {
1399 log_info("Setting server-id-check true");
1400 server_id_check
= 1;
1404 oc
= lookup_option(&server_universe
, options
, SV_PREFIX_LEN_MODE
);
1406 evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1407 &global_scope
, oc
, MDL
)) {
1409 prefix_length_mode
= db
.data
[0];
1411 log_fatal("invalid prefix-len-mode");
1414 data_string_forget(&db
, MDL
);
1418 // Set global abandon-lease-time option.
1419 oc
= lookup_option (&server_universe
, options
, SV_ABANDON_LEASE_TIME
);
1421 evaluate_option_cache(&db
, NULL
, NULL
, NULL
, options
, NULL
,
1422 &global_scope
, oc
, MDL
)) {
1423 if (db
.len
== sizeof (u_int32_t
)) {
1424 abandon_lease_time
= getULong (db
.data
);
1426 log_fatal("invalid abandon-lease-time");
1429 data_string_forget (&db
, MDL
);
1432 #if defined (FAILOVER_PROTOCOL)
1433 oc
= lookup_option(&server_universe
, options
, SV_CHECK_SECS_BYTE_ORDER
);
1435 evaluate_boolean_option_cache(NULL
, NULL
, NULL
, NULL
, options
, NULL
,
1436 &global_scope
, oc
, MDL
)) {
1437 check_secs_byte_order
= 1;
1442 oc
= lookup_option(&server_universe
, options
, SV_PERSIST_EUI_64_LEASES
);
1444 persist_eui64
= evaluate_boolean_option_cache(NULL
, NULL
, NULL
,
1451 if (!persist_eui64
) {
1452 log_info("EUI64 leases will not be written to lease file");
1457 oc
= lookup_option(&server_universe
, options
, SV_RELEASE_ON_ROAM
);
1459 do_release_on_roam
=
1460 evaluate_boolean_option_cache(NULL
, NULL
, NULL
, NULL
,
1462 &global_scope
, oc
, MDL
);
1466 #if defined (BINARY_LEASES)
1467 if (local_family
== AF_INET
) {
1468 log_info("Source compiled to use binary-leases");
1472 /* Don't need the options anymore. */
1473 option_state_dereference(&options
, MDL
);
1476 void postdb_startup (void)
1478 /* Initialize the omapi listener state. */
1479 if (omapi_port
!= -1) {
1480 omapi_listener_start (0);
1483 #if defined (FAILOVER_PROTOCOL)
1484 /* Initialize the failover listener state. */
1485 dhcp_failover_startup ();
1489 * Begin our lease timeout background task.
1491 schedule_all_ipv6_lease_timeouts();
1494 void lease_pinged (from
, packet
, length
)
1501 /* Don't try to look up a pinged lease if we aren't trying to
1502 ping one - otherwise somebody could easily make us churn by
1503 just forging repeated ICMP EchoReply packets for us to look
1505 if (!outstanding_pings
)
1508 lp
= (struct lease
*)0;
1509 if (!find_lease_by_ip_addr (&lp
, from
, MDL
)) {
1510 log_debug ("unexpected ICMP Echo Reply from %s",
1516 #if defined (FAILOVER_PROTOCOL)
1518 !lp
-> pool
-> failover_peer
)
1520 log_debug ("ICMP Echo Reply for %s late or spurious.",
1525 if (lp
-> ends
> cur_time
) {
1526 log_debug ("ICMP Echo reply while lease %s valid.",
1530 /* At this point it looks like we pinged a lease and got a
1531 response, which shouldn't have happened. */
1532 data_string_forget (&lp
-> state
-> parameter_request_list
, MDL
);
1533 free_lease_state (lp
-> state
, MDL
);
1534 lp
-> state
= (struct lease_state
*)0;
1536 abandon_lease (lp
, "pinged before offer");
1537 cancel_timeout (lease_ping_timeout
, lp
);
1538 --outstanding_pings
;
1540 lease_dereference (&lp
, MDL
);
1543 void lease_ping_timeout (vlp
)
1546 struct lease
*lp
= vlp
;
1548 #if defined (DEBUG_MEMORY_LEAKAGE)
1549 unsigned long previous_outstanding
= dmalloc_outstanding
;
1552 --outstanding_pings
;
1555 #if defined (DEBUG_MEMORY_LEAKAGE)
1556 log_info ("generation %ld: %ld new, %ld outstanding, %ld long-term",
1558 dmalloc_outstanding
- previous_outstanding
,
1559 dmalloc_outstanding
, dmalloc_longterm
);
1561 #if defined (DEBUG_MEMORY_LEAKAGE)
1562 dmalloc_dump_outstanding ();
1566 int dhcpd_interface_setup_hook (struct interface_info
*ip
, struct iaddr
*ia
)
1568 struct subnet
*subnet
;
1569 struct shared_network
*share
;
1570 isc_result_t status
;
1572 /* Special case for fallback network - not sure why this is
1575 const char *fnn
= "fallback-net";
1576 status
= shared_network_allocate (&ip
-> shared_network
, MDL
);
1577 if (status
!= ISC_R_SUCCESS
)
1578 log_fatal ("No memory for shared subnet: %s",
1579 isc_result_totext (status
));
1580 ip
-> shared_network
-> name
= dmalloc (strlen (fnn
) + 1, MDL
);
1581 if (!ip
-> shared_network
-> name
)
1582 log_fatal("no memory for shared network");
1583 strcpy (ip
-> shared_network
-> name
, fnn
);
1587 /* If there's a registered subnet for this address,
1588 connect it together... */
1589 subnet
= (struct subnet
*)0;
1590 if (find_subnet (&subnet
, *ia
, MDL
)) {
1591 /* If this interface has multiple aliases on the same
1592 subnet, ignore all but the first we encounter. */
1593 if (!subnet
-> interface
) {
1594 interface_reference (&subnet
-> interface
, ip
, MDL
);
1595 subnet
-> interface_address
= *ia
;
1596 } else if (subnet
-> interface
!= ip
) {
1597 log_error ("Multiple interfaces match the %s: %s %s",
1599 subnet
-> interface
-> name
, ip
-> name
);
1601 share
= subnet
-> shared_network
;
1602 if (ip
-> shared_network
&&
1603 ip
-> shared_network
!= share
) {
1604 log_fatal ("Interface %s matches multiple shared %s",
1605 ip
-> name
, "networks");
1607 if (!ip
-> shared_network
)
1608 shared_network_reference
1609 (&ip
-> shared_network
, share
, MDL
);
1612 if (!share
-> interface
) {
1613 interface_reference (&share
-> interface
, ip
, MDL
);
1614 } else if (share
-> interface
!= ip
) {
1615 log_error ("Multiple interfaces match the %s: %s %s",
1616 "same shared network",
1617 share
-> interface
-> name
, ip
-> name
);
1619 subnet_dereference (&subnet
, MDL
);
1624 static TIME shutdown_time
;
1625 static int omapi_connection_count
;
1626 enum dhcp_shutdown_state shutdown_state
;
1628 isc_result_t
dhcp_io_shutdown (omapi_object_t
*obj
, void *foo
)
1630 /* Shut down all listeners. */
1631 if (shutdown_state
== shutdown_listeners
&&
1632 obj
-> type
== omapi_type_listener
&&
1634 obj
-> inner
-> type
== omapi_type_protocol_listener
) {
1635 omapi_listener_destroy (obj
, MDL
);
1636 return ISC_R_SUCCESS
;
1639 /* Shut down all existing omapi connections. */
1640 if (obj
-> type
== omapi_type_connection
&&
1642 obj
-> inner
-> type
== omapi_type_protocol
) {
1643 if (shutdown_state
== shutdown_drop_omapi_connections
) {
1644 omapi_disconnect (obj
, 1);
1646 omapi_connection_count
++;
1647 if (shutdown_state
== shutdown_omapi_connections
) {
1648 omapi_disconnect (obj
, 0);
1649 return ISC_R_SUCCESS
;
1653 /* Shutdown all DHCP interfaces. */
1654 if (obj
-> type
== dhcp_type_interface
&&
1655 shutdown_state
== shutdown_dhcp
) {
1656 dhcp_interface_remove (obj
, (omapi_object_t
*)0);
1657 return ISC_R_SUCCESS
;
1659 return ISC_R_SUCCESS
;
1662 static isc_result_t
dhcp_io_shutdown_countdown (void *vlp
)
1664 #if defined (FAILOVER_PROTOCOL)
1665 dhcp_failover_state_t
*state
;
1666 int failover_connection_count
= 0;
1671 if (shutdown_state
== shutdown_listeners
||
1672 shutdown_state
== shutdown_omapi_connections
||
1673 shutdown_state
== shutdown_drop_omapi_connections
||
1674 shutdown_state
== shutdown_dhcp
) {
1675 omapi_connection_count
= 0;
1676 omapi_io_state_foreach (dhcp_io_shutdown
, 0);
1679 if ((shutdown_state
== shutdown_listeners
||
1680 shutdown_state
== shutdown_omapi_connections
||
1681 shutdown_state
== shutdown_drop_omapi_connections
) &&
1682 omapi_connection_count
== 0) {
1683 shutdown_state
= shutdown_dhcp
;
1684 shutdown_time
= cur_time
;
1686 } else if (shutdown_state
== shutdown_listeners
&&
1687 cur_time
- shutdown_time
> 4) {
1688 shutdown_state
= shutdown_omapi_connections
;
1689 shutdown_time
= cur_time
;
1690 } else if (shutdown_state
== shutdown_omapi_connections
&&
1691 cur_time
- shutdown_time
> 4) {
1692 shutdown_state
= shutdown_drop_omapi_connections
;
1693 shutdown_time
= cur_time
;
1694 } else if (shutdown_state
== shutdown_drop_omapi_connections
&&
1695 cur_time
- shutdown_time
> 4) {
1696 shutdown_state
= shutdown_dhcp
;
1697 shutdown_time
= cur_time
;
1699 } else if (shutdown_state
== shutdown_dhcp
&&
1700 cur_time
- shutdown_time
> 4) {
1701 shutdown_state
= shutdown_done
;
1702 shutdown_time
= cur_time
;
1705 #if defined (FAILOVER_PROTOCOL)
1706 /* Set all failover peers into the shutdown state. */
1707 if (shutdown_state
== shutdown_dhcp
) {
1708 for (state
= failover_states
; state
; state
= state
-> next
) {
1709 if (state
-> me
.state
== normal
) {
1710 dhcp_failover_set_state (state
, shut_down
);
1711 failover_connection_count
++;
1713 if (state
-> me
.state
== shut_down
&&
1714 state
-> partner
.state
!= partner_down
)
1715 failover_connection_count
++;
1719 if (shutdown_state
== shutdown_done
) {
1720 for (state
= failover_states
; state
; state
= state
-> next
) {
1721 if (state
-> me
.state
== shut_down
) {
1722 if (state
-> link_to_peer
)
1723 dhcp_failover_link_dereference (&state
-> link_to_peer
,
1725 dhcp_failover_set_state (state
, recover
);
1728 #if defined (DEBUG_MEMORY_LEAKAGE) && \
1729 defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
1731 omapi_print_dmalloc_usage_by_caller ();
1733 if (no_pid_file
== ISC_FALSE
)
1734 (void) unlink(path_dhcpd_pid
);
1738 if (shutdown_state
== shutdown_done
) {
1739 #if defined (DEBUG_MEMORY_LEAKAGE) && \
1740 defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
1742 omapi_print_dmalloc_usage_by_caller ();
1744 if (no_pid_file
== ISC_FALSE
)
1745 (void) unlink(path_dhcpd_pid
);
1749 if (shutdown_state
== shutdown_dhcp
&&
1750 #if defined(FAILOVER_PROTOCOL)
1751 !failover_connection_count
&&
1754 shutdown_state
= shutdown_done
;
1755 shutdown_time
= cur_time
;
1758 tv
.tv_sec
= cur_tv
.tv_sec
+ 1;
1759 tv
.tv_usec
= cur_tv
.tv_usec
;
1761 (void (*)(void *))dhcp_io_shutdown_countdown
, 0, 0, 0);
1762 return ISC_R_SUCCESS
;
1765 isc_result_t
dhcp_set_control_state (control_object_state_t oldstate
,
1766 control_object_state_t newstate
)
1770 if (newstate
!= server_shutdown
)
1771 return DHCP_R_INVALIDARG
;
1773 if (shutdown_signal
== SIGUSR1
)
1774 return ISC_R_SUCCESS
;
1775 shutdown_time
= cur_time
;
1776 shutdown_state
= shutdown_listeners
;
1777 /* Called by user. */
1778 if (shutdown_signal
== 0) {
1779 shutdown_signal
= SIGUSR1
;
1780 dhcp_io_shutdown_countdown (0);
1781 return ISC_R_SUCCESS
;
1783 /* Called on signal. */
1784 log_info("Received signal %d, initiating shutdown.", shutdown_signal
);
1785 shutdown_signal
= SIGUSR1
;
1788 * Prompt the shutdown event onto the timer queue
1789 * and return to the dispatch loop.
1791 tv
.tv_sec
= cur_tv
.tv_sec
;
1792 tv
.tv_usec
= cur_tv
.tv_usec
+ 1;
1794 (void (*)(void *))dhcp_io_shutdown_countdown
, 0, 0, 0);
1795 return ISC_R_SUCCESS
;