From: Thomas Markwalder Date: Tue, 16 Jun 2015 15:43:12 +0000 (-0400) Subject: [master] Delayed-ack now works correctly with failover X-Git-Tag: v4_1_esv_r12b1~10 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f7e75c8af3ef6bd433ee20faf31135990072fe63;p=thirdparty%2Fdhcp.git [master] Delayed-ack now works correctly with failover Merges in 31474. Conflicts: includes/dhcpd.h server/dhcp.c --- diff --git a/RELNOTES b/RELNOTES index 192a5edcb..887b422ab 100644 --- a/RELNOTES +++ b/RELNOTES @@ -60,6 +60,11 @@ by Eric Young (eay@cryptsoft.com). Changes since 4.1-ESV-R11 +- Delayed-ack now works properly with Failover. Prior to this, bind updates + post startup were being queued but never delivered. Among other things, this + was causing leases to not transition from expired or released to free. + [ISC-Bugs #31474] + - The server now does a better check to see if it can allocate the memory for large blocks of v4 leases and should provide a slightly better error message. Note well: the server pre-allocates v4 addresses, if you use diff --git a/includes/dhcpd.h b/includes/dhcpd.h index d374aa361..819c1c3cb 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -638,6 +638,10 @@ struct lease_state { # define DEFAULT_ACK_DELAY_USECS 250000 /* 1/4 of a second */ #endif +#if !defined (DEFAULT_MIN_ACK_DELAY_USECS) +# define DEFAULT_MIN_ACK_DELAY_USECS 10000 /* 1/100 second */ +#endif + #if !defined (DEFAULT_DEFAULT_LEASE_TIME) # define DEFAULT_DEFAULT_LEASE_TIME 43200 #endif @@ -1969,9 +1973,7 @@ void dhcpinform (struct packet *, int); void nak_lease (struct packet *, struct iaddr *cip); void ack_lease (struct packet *, struct lease *, unsigned int, TIME, char *, int, struct host_decl *); -void delayed_ack_enqueue(struct lease *); -void commit_leases_readerdry(void *); -void flush_ackqueue(void *); + void dhcp_reply (struct lease *); int find_lease (struct lease **, struct packet *, struct shared_network *, int *, int *, struct lease *, @@ -1996,6 +1998,9 @@ void get_server_source_address(struct in_addr *from, void setup_server_source_address(struct in_addr *from, struct option_state *options, struct packet *packet); +#if defined(DELAYED_ACK) +void delayed_acks_timer(void *); +#endif /* dhcpleasequery.c */ void dhcpleasequery (struct packet *, int); @@ -2604,7 +2609,6 @@ isc_result_t write_named_billing_class(const void *, unsigned, void *); void write_billing_classes (void); int write_billing_class (struct class *); void commit_leases_timeout (void *); -void commit_leases_readerdry(void *); int commit_leases (void); int commit_leases_timed (void); void db_startup (int); diff --git a/server/db.c b/server/db.c index 0ba711479..7186cb571 100644 --- a/server/db.c +++ b/server/db.c @@ -3,7 +3,7 @@ Persistent database management routines for DHCPD... */ /* - * Copyright (c) 2012-2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2012-2015 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * @@ -978,9 +978,6 @@ int commit_leases () return 0; } - /* send out all deferred ACKs now */ - flush_ackqueue(NULL); - /* If we haven't rewritten the lease database in over an hour, rewrite it now. (The length of time should probably be configurable. */ diff --git a/server/dhcp.c b/server/dhcp.c index d24f520e8..04e4e5e1a 100644 --- a/server/dhcp.c +++ b/server/dhcp.c @@ -31,17 +31,22 @@ #include #include -static void commit_leases_ackout(void *foo); - int outstanding_pings; +#if defined(DELAYED_ACK) +void delayed_acks_timer(void *); +static void delayed_ack_enqueue(struct lease *); + + struct leasequeue *ackqueue_head, *ackqueue_tail; static struct leasequeue *free_ackqueue; -static struct timeval next_fsync; -int outstanding_acks; +static struct timeval max_fsync; +int outstanding_acks = 0; int max_outstanding_acks = DEFAULT_DELAYED_ACK; int max_ack_delay_secs = DEFAULT_ACK_DELAY_SECS; int max_ack_delay_usecs = DEFAULT_ACK_DELAY_USECS; +int min_ack_delay_usecs = DEFAULT_MIN_ACK_DELAY_USECS; +#endif static char dhcp_message [256]; static int site_code_min; @@ -2912,14 +2917,17 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp) } } -/* CC: queue single ACK: - - write the lease (but do not fsync it yet) - - add to double linked list - - commit if more than xx ACKs pending - - Not yet: schedule a fsync at the next interval (1 second?) +#if defined(DELAYED_ACK) +/* + * CC: queue single ACK: + * - write the lease (but do not fsync it yet) + * - add to double linked list + * - commit if more than xx ACKs pending + * - if necessary set the max timer and bump the next timer + * but only up to the max timer value. */ -void +static void delayed_ack_enqueue(struct lease *lease) { struct leasequeue *q; @@ -2946,69 +2954,102 @@ delayed_ack_enqueue(struct lease *lease) q->next->prev = q; outstanding_acks++; - if (outstanding_acks > max_outstanding_acks) - commit_leases(); + if (outstanding_acks > max_outstanding_acks) { + /* Cancel any pending timeout and call handler directly */ + cancel_timeout(delayed_acks_timer, NULL); + delayed_acks_timer(NULL); + } else { + struct timeval next_fsync; - /* If next_fsync is not set, schedule an fsync. */ - if (next_fsync.tv_sec == 0 && next_fsync.tv_usec == 0) { - next_fsync.tv_sec = cur_tv.tv_sec + max_ack_delay_secs; - next_fsync.tv_usec = cur_tv.tv_usec + max_ack_delay_usecs; + if (max_fsync.tv_sec == 0 && max_fsync.tv_usec == 0) { + /* set the maximum time we'll wait */ + max_fsync.tv_sec = cur_tv.tv_sec + max_ack_delay_secs; + max_fsync.tv_usec = cur_tv.tv_usec + + max_ack_delay_usecs; - if (next_fsync.tv_usec >= 1000000) { - next_fsync.tv_sec++; - next_fsync.tv_usec -= 1000000; + if (max_fsync.tv_usec >= 1000000) { + max_fsync.tv_sec++; + max_fsync.tv_usec -= 1000000; + } } - add_timeout(&next_fsync, commit_leases_ackout, NULL, + /* Set the timeout */ + next_fsync.tv_sec = cur_tv.tv_sec; + next_fsync.tv_usec = cur_tv.tv_usec + min_ack_delay_usecs; + if (next_fsync.tv_usec >= 1000000) { + next_fsync.tv_sec++; + next_fsync.tv_usec -= 1000000; + } + /* but not more than the max */ + if ((next_fsync.tv_sec > max_fsync.tv_sec) || + ((next_fsync.tv_sec == max_fsync.tv_sec) && + (next_fsync.tv_usec > max_fsync.tv_usec))) { + next_fsync.tv_sec = max_fsync.tv_sec; + next_fsync.tv_usec = max_fsync.tv_usec; + } + + add_timeout(&next_fsync, delayed_acks_timer, NULL, (tvref_t) NULL, (tvunref_t) NULL); } } +/* Processes any delayed acks: + * Commits the leases and then for each delayed ack: + * - Update the failover peer if we're in failover + * - Send the REPLY to the client + */ void -commit_leases_readerdry(void *foo) +delayed_acks_timer(void *foo) { - if (outstanding_acks) { - commit_leases(); + struct leasequeue *ack, *p; + + /* Reset max fsync */ + memset(&max_fsync, 0, sizeof(max_fsync)); - /* Reset next_fsync and cancel any pending timeout. */ - memset(&next_fsync, 0, sizeof(next_fsync)); - cancel_timeout(commit_leases_ackout, NULL); + if (!outstanding_acks) { + /* Nothing to do, so punt, shouldn't happen? */ + return; } -} -static void -commit_leases_ackout(void *foo) -{ - if (outstanding_acks) { - commit_leases(); + /* Commit the leases first */ + commit_leases(); - memset(&next_fsync, 0, sizeof(next_fsync)); - } -} + /* Now process the delayed ACKs + - update failover peer + - send out the ACK packets + - move the queue slots to the free list + */ -/* CC: process the delayed ACK responses: - - send out the ACK packets - - move the queue slots to the free list - */ -void -flush_ackqueue(void *foo) -{ - struct leasequeue *ack, *p; /* process from bottom to retain packet order */ for (ack = ackqueue_tail ; ack ; ack = p) { p = ack->prev; +#if defined(FAILOVER_PROTOCOL) + /* If we're in failover we need to send any deferred + * bind updates as well as the replies */ + if (ack->lease->pool) { + dhcp_failover_state_t *fpeer; + + fpeer = ack->lease->pool->failover_peer; + if (fpeer && fpeer->link_to_peer) { + dhcp_failover_send_updates(fpeer); + } + } +#endif + /* dhcp_reply() requires that the reply state still be valid */ if (ack->lease->state == NULL) log_error("delayed ack for %s has gone stale", piaddr(ack->lease->ip_addr)); - else + else { dhcp_reply(ack->lease); + } lease_dereference(&ack->lease, MDL); ack->next = free_ackqueue; free_ackqueue = ack; } + ackqueue_head = NULL; ackqueue_tail = NULL; outstanding_acks = 0; @@ -3031,6 +3072,8 @@ relinquish_ackqueue(void) } #endif +#endif /* defined(DELAYED_ACK) */ + void dhcp_reply (lease) struct lease *lease; { diff --git a/server/dhcpd.c b/server/dhcpd.c index 991c3688f..d0ebbf824 100644 --- a/server/dhcpd.c +++ b/server/dhcpd.c @@ -856,8 +856,9 @@ main(int argc, char **argv) { omapi_set_int_value ((omapi_object_t *)dhcp_control_object, (omapi_object_t *)0, "state", server_running); - - register_eventhandler(&rw_queue_empty,commit_leases_readerdry); +#if defined(DELAYED_ACK) + register_eventhandler(&rw_queue_empty, delayed_acks_timer); +#endif /* Receive packets and dispatch them... */ dispatch (); @@ -1093,7 +1094,8 @@ void postconf_initialization (int quiet) data_string_forget (&db, MDL); } } - + +#if defined(DELAYED_ACK) oc = lookup_option(&server_universe, options, SV_DELAYED_ACK); if (oc && evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL, @@ -1121,6 +1123,7 @@ void postconf_initialization (int quiet) data_string_forget(&db, MDL); } +#endif /* Don't need the options anymore. */ option_state_dereference (&options, MDL);