]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
Rework DHCP lease state handling to be compatible with failover protocol.
authorTed Lemon <source@isc.org>
Fri, 2 Jun 2000 21:27:21 +0000 (21:27 +0000)
committerTed Lemon <source@isc.org>
Fri, 2 Jun 2000 21:27:21 +0000 (21:27 +0000)
15 files changed:
client/clparse.c
client/dhclient.c
common/conflex.c
includes/dhcpd.h
includes/dhctoken.h
includes/failover.h
server/confpars.c
server/db.c
server/dhcp.c
server/dhcpd.conf.5
server/dhcpd.conf.cat5
server/failover.c
server/mdb.c
server/omapi.c
server/stables.c

index 393df3fe28d8adaba3b6f65528531d9847be2ea4..c52d1fc0732a79c259d5b55c13fa7fce8535a0ec 100644 (file)
@@ -43,7 +43,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: clparse.c,v 1.44 2000/05/16 23:01:57 mellon Exp $ Copyright (c) 1996-2000 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: clparse.c,v 1.45 2000/06/02 21:26:55 mellon Exp $ Copyright (c) 1996-2000 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -291,7 +291,7 @@ void parse_client_statement (cfile, ip, config)
                                return;
                        }
                        config -> auth_policy = policy;
-               } else if (token != BOOTP) {
+               } else if (token != TOKEN_BOOTP) {
                        if (policy != P_PREFER &&
                            policy != P_IGNORE &&
                            policy != P_ACCEPT) {
@@ -919,7 +919,7 @@ void parse_client_lease_declaration (cfile, lease, ipp, clientp)
                        parse_warn (cfile, "unknown key %s", val);
                parse_semi (cfile);
                break;
-             case BOOTP:
+             case TOKEN_BOOTP:
                lease -> is_bootp = 1;
                break;
 
index 91da4d2a378949c01743dc1956e0f2cebc20160c..a6e4b09f7d1dd8a792153f952df79a28359e91c7 100644 (file)
@@ -41,7 +41,7 @@
 
 #ifndef lint
 static char ocopyright[] =
-"$Id: dhclient.c,v 1.104 2000/05/30 21:43:42 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 Internet Software Consortium.  All rights reserved.\n";
+"$Id: dhclient.c,v 1.105 2000/06/02 21:26:57 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -779,7 +779,7 @@ void bind_lease (client)
        }
 
        /* Write out the new lease. */
-       write_client_lease (client, client -> new, 0);
+       write_client_lease (client, client -> new, 0, 0);
 
        /* Replace the old active lease with the new one. */
        if (client -> active)
@@ -1561,18 +1561,50 @@ void send_release (cpp)
        struct client_state *client = cpp;
 
        int result;
+       struct sockaddr_in destination;
+       struct in_addr from;
+
+       memcpy (&from, client -> active -> address.iabuf,
+               sizeof from);
+       memcpy (&destination.sin_addr.s_addr,
+               client -> destination.iabuf,
+               sizeof destination.sin_addr.s_addr);
+       destination.sin_port = remote_port;
+       destination.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+       destination.sin_len = sizeof destination;
+#endif
+
+
+       /* Set the lease to end now, so that we don't accidentally
+          reuse it if we restart before the old expiry time. */
+       client -> active -> expiry =
+               client -> active -> renewal =
+               client -> active -> rebind = cur_time;
+       if (!write_client_lease (client, client -> active, 1, 1)) {
+               log_error ("Can't release lease: lease write failed.");
+               return;
+       }
 
        log_info ("DHCPRELEASE on %s to %s port %d",
              client -> name ? client -> name : client -> interface -> name,
-             inet_ntoa (sockaddr_broadcast.sin_addr),
-             ntohs (sockaddr_broadcast.sin_port));
+             inet_ntoa (destination.sin_addr),
+             ntohs (destination.sin_port));
 
-       /* Send out a packet. */
-       result = send_packet (client -> interface, (struct packet *)0,
-                             &client -> packet,
-                             client -> packet_length,
-                             inaddr_any, &sockaddr_broadcast,
-                             (struct hardware *)0);
+       if (fallback_interface)
+               result = send_packet (fallback_interface,
+                                     (struct packet *)0,
+                                     &client -> packet,
+                                     client -> packet_length,
+                                     from, &destination,
+                                     (struct hardware *)0);
+       else
+               /* Send out a packet. */
+               result = send_packet (client -> interface, (struct packet *)0,
+                                     &client -> packet,
+                                     client -> packet_length,
+                                     from, &destination,
+                                     (struct hardware *)0);
 }
 
 void make_client_options (client, lease, type, sid, rip, prl, op)
@@ -1926,11 +1958,11 @@ void rewrite_client_leases ()
        for (ip = interfaces; ip; ip = ip -> next) {
                for (client = ip -> client; client; client = client -> next) {
                        for (lp = client -> leases; lp; lp = lp -> next) {
-                               write_client_lease (client, lp, 1);
+                               write_client_lease (client, lp, 1, 0);
                        }
                        if (client -> active)
                                write_client_lease (client,
-                                                   client -> active, 1);
+                                                   client -> active, 1, 0);
                }
        }
 
@@ -1939,20 +1971,21 @@ void rewrite_client_leases ()
        for (ip = dummy_interfaces; ip; ip = ip -> next) {
                for (client = ip -> client; client; client = client -> next) {
                        for (lp = client -> leases; lp; lp = lp -> next) {
-                               write_client_lease (client, lp, 1);
+                               write_client_lease (client, lp, 1, 0);
                        }
                        if (client -> active)
                                write_client_lease (client,
-                                                   client -> active, 1);
+                                                   client -> active, 1, 0);
                }
        }
        fflush (leaseFile);
 }
 
-void write_client_lease (client, lease, rewrite)
+int write_client_lease (client, lease, rewrite, makesure)
        struct client_state *client;
        struct client_lease *lease;
        int rewrite;
+       int makesure;
 {
        int i;
        struct tm *t;
@@ -1960,6 +1993,7 @@ void write_client_lease (client, lease, rewrite)
        struct option_cache *oc;
        struct data_string ds;
        pair *hash;
+       int errors = 0;
 
        if (!rewrite) {
                if (leases_written++ > 20) {
@@ -1971,7 +2005,7 @@ void write_client_lease (client, lease, rewrite)
        /* If the lease came from the config file, we don't need to stash
           a copy in the lease database. */
        if (lease -> is_static)
-               return;
+               return 1;
 
        if (!leaseFile) {       /* XXX */
                leaseFile = fopen (path_dhclient_db, "w");
@@ -1979,6 +2013,7 @@ void write_client_lease (client, lease, rewrite)
                        log_fatal ("can't create %s: %m", path_dhclient_db);
        }
 
+       errno = 0;
        fprintf (leaseFile, "lease {\n");
        if (lease -> is_bootp)
                fprintf (leaseFile, "  bootp;\n");
@@ -1997,6 +2032,10 @@ void write_client_lease (client, lease, rewrite)
        if (lease -> medium)
                fprintf (leaseFile, "  medium \"%s\";\n",
                         lease -> medium -> string);
+       if (errno != 0) {
+               errors++;
+               errno = 0;
+       }
 
        memset (&ds, 0, sizeof ds);
 
@@ -2018,6 +2057,10 @@ void write_client_lease (client, lease, rewrite)
                                         (oc -> option -> code,
                                          ds.data, ds.len, 1, 1));
                                data_string_forget (&ds, MDL);
+                               if (errno != 0) {
+                                       errors++;
+                                       errno = 0;
+                               }
                        }
                }
        }
@@ -2045,6 +2088,17 @@ void write_client_lease (client, lease, rewrite)
                 t -> tm_hour, t -> tm_min, t -> tm_sec);
        fprintf (leaseFile, "}\n");
        fflush (leaseFile);
+       if (errno != 0) {
+               errors++;
+               errno = 0;
+       }
+       if (!errors && makesure) {
+               if (fsync (fileno (leaseFile)) < 0) {
+                       log_info ("write_client_lease: %m");
+                       return 0;
+               }
+       }
+       return errors ? 0 : 1;
 }
 
 /* Variables holding name of script and file pointer for writing to
@@ -2357,6 +2411,9 @@ void client_location_changed ()
 void do_release(client) 
        struct client_state *client;
 {
+       struct data_string ds;
+       struct option_cache *oc;
+
        /* make_request doesn't initialize xid because it normally comes
           from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER,
           so pick an xid now. */
@@ -2367,7 +2424,25 @@ void do_release(client)
                /* Make a DHCPRELEASE packet, and set appropriate per-interface
                   flags. */
                make_release (client, client -> active);
-               client -> destination = iaddr_broadcast;
+
+               memset (&ds, 0, sizeof ds);
+               oc = lookup_option (&dhcp_universe,
+                                   client -> active -> options,
+                                   DHO_DHCP_SERVER_IDENTIFIER);
+               if (oc &&
+                   evaluate_option_cache (&ds, (struct packet *)0,
+                                          (struct lease *)0,
+                                          (struct option_state *)0,
+                                          client -> active -> options,
+                                          &global_scope, oc, MDL)) {
+                       if (ds.len > 3) {
+                               memcpy (client -> destination.iabuf,
+                                       ds.data, 4);
+                               client -> destination.len = 4;
+                       } else
+                               client -> destination = iaddr_broadcast;
+               } else
+                       client -> destination = iaddr_broadcast;
                client -> first_sending = cur_time;
                client -> interval = client -> config -> initial_interval;
        
@@ -2376,13 +2451,8 @@ void do_release(client)
        
                /* Send out the first and only DHCPRELEASE packet. */
                send_release (client);
-       }
-
-       /* remove the timeouts for this client */
-       cancel_timeout (NULL, client);
 
-       /* if there was no lease, nothing to "do" */
-       if (client -> active) {
+               /* Do the client script RELEASE operation. */
                script_init (client,
                             "RELEASE", (struct string_list *)0);
                if (client -> alias)
@@ -2390,6 +2460,9 @@ void do_release(client)
                                             client -> alias);
                script_go (client);
        }
+
+       /* remove the timeouts for this client */
+       cancel_timeout (0, client);
 }
 
 int dhclient_interface_shutdown_hook (struct interface_info *interface)
index ca6943a5fc8c98f2966f967fb480d2a06648be05..5d23d9f2253fa2c42cbbc857c282758ef7cb0814 100644 (file)
@@ -43,7 +43,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: conflex.c,v 1.75 2000/05/16 23:02:11 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: conflex.c,v 1.76 2000/06/02 21:27:01 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -503,7 +503,7 @@ static enum dhcp_token intern (atom, dfv)
                if (!strcasecmp (atom + 1, "lgorithm"))
                        return ALGORITHM;
                if (!strcasecmp (atom + 1, "bandoned"))
-                       return ABANDONED;
+                       return TOKEN_ABANDONED;
                if (!strcasecmp (atom + 1, "dd"))
                        return TOKEN_ADD;
                if (!strcasecmp (atom + 1, "ll"))
@@ -514,14 +514,20 @@ static enum dhcp_token intern (atom, dfv)
                        return ARRAY;
                if (!strcasecmp (atom + 1, "ddress"))
                        return ADDRESS;
+               if (!strcasecmp (atom + 1, "ctive"))
+                       return TOKEN_ACTIVE;
                break;
              case 'b':
+               if (!strcasecmp (atom + 1, "ackup"))
+                       return TOKEN_BACKUP;
+               if (!strcasecmp (atom + 1, "ootp"))
+                       return TOKEN_BOOTP;
+               if (!strcasecmp (atom + 1, "inding"))
+                       return BINDING;
                if (!strcasecmp (atom + 1, "inary-to-ascii"))
                        return BINARY_TO_ASCII;
                if (!strcasecmp (atom + 1, "ackoff-cutoff"))
                        return BACKOFF_CUTOFF;
-               if (!strcasecmp (atom + 1, "ootp"))
-                       return BOOTP;
                if (!strcasecmp (atom + 1, "ooting"))
                        return BOOTING;
                if (!strcasecmp (atom + 1, "oot-unknown-clients"))
@@ -575,7 +581,7 @@ static enum dhcp_token intern (atom, dfv)
                if (!strcasecmp (atom + 1, "eny"))
                        return DENY;
                if (!strcasecmp (atom + 1, "eleted"))
-                       return DELETED;
+                       return TOKEN_DELETED;
                if (!strcasecmp (atom + 1, "elete"))
                        return TOKEN_DELETE;
                if (!strncasecmp (atom + 1, "efault", 6)) {
@@ -619,6 +625,8 @@ static enum dhcp_token intern (atom, dfv)
                                return EXPIRY;
                        if (!strcasecmp (atom + 2, "pire"))
                                return EXPIRE;
+                       if (!strcasecmp (atom + 2, "pired"))
+                               return TOKEN_EXPIRED;
                }
                if (!strcasecmp (atom + 1, "ncode-int"))
                        return ENCODE_INT;
@@ -649,6 +657,8 @@ static enum dhcp_token intern (atom, dfv)
                        return FUNCTION;
                if (!strcasecmp (atom + 1, "ailover"))
                        return FAILOVER;
+               if (!strcasecmp (atom + 1, "ree"))
+                       return TOKEN_FREE;
                break;
              case 'g':
                if (!strcasecmp (atom + 1, "iaddr"))
@@ -779,6 +789,8 @@ static enum dhcp_token intern (atom, dfv)
                        return NS_NXRRSET;
                if (!strcasecmp (atom + 1, "ull"))
                        return TOKEN_NULL;
+               if (!strcasecmp (atom + 1, "ext"))
+                       return TOKEN_NEXT;
                break;
              case 'o':
                if (!strcasecmp (atom + 1, "r"))
@@ -850,6 +862,12 @@ static enum dhcp_token intern (atom, dfv)
                        return RELEASE;
                if (!strcasecmp (atom + 1, "efused"))
                        return NS_REFUSED;
+               if (!strcasecmp (atom + 1, "eleased"))
+                       return TOKEN_RELEASED;
+               if (!strcasecmp (atom + 1, "eset"))
+                       return TOKEN_RESET;
+               if (!strcasecmp (atom + 1, "eserved"))
+                       return TOKEN_RESERVED;
                break;
              case 's':
                if (!strcasecmp (atom + 1, "tate"))
index f41017b73dc56a08761c549c55244c298f1dee56..e0f821f00ef116142aa84dbadcb93fa6294ef8e9 100644 (file)
@@ -238,12 +238,10 @@ struct hardware {
 struct lease {
        OMAPI_OBJECT_PREAMBLE;
        struct lease *next;
-       struct lease *prev;
        struct lease *n_uid, *n_hw;
-       struct lease *waitq_next;
 
        struct iaddr ip_addr;
-       TIME starts, ends, timestamp;
+       TIME starts, ends, timestamp, sort_time;
        unsigned char *uid;
        unsigned uid_len;
        unsigned uid_max;
@@ -261,19 +259,17 @@ struct lease {
        struct executable_statement *on_commit;
        struct executable_statement *on_release;
 
-       int flags;
+       u_int16_t flags;
 #       define STATIC_LEASE            1
-#       define BOOTP_LEASE             2
 #      define PERSISTENT_FLAGS         (0)
 #      define MS_NULL_TERMINATION      8
-#      define ABANDONED_LEASE          16
-#      define PEER_IS_OWNER            32
-#      define ON_UPDATE_QUEUE          64
-#      define ON_ACK_QUEUE             128
-#      define EPHEMERAL_FLAGS          (BOOTP_LEASE | MS_NULL_TERMINATION | \
-                                        ABANDONED_LEASE | PEER_IS_OWNER | \
+#      define ON_UPDATE_QUEUE          16
+#      define ON_ACK_QUEUE             32
+#      define EPHEMERAL_FLAGS          (MS_NULL_TERMINATION | \
                                         ON_ACK_QUEUE | ON_UPDATE_QUEUE)
-
+       binding_state_t binding_state;  /* See failover.h, FTS_*. */
+       binding_state_t next_binding_state;     /* See failover.h, FTS_*. */
+       
        struct lease_state *state;
 
        TIME tstp;      /* Time sent to partner. */
@@ -500,14 +496,16 @@ struct pool {
        struct shared_network *shared_network;
        struct permit *permit_list;
        struct permit *prohibit_list;
-       struct lease *leases;
-       struct lease *insertion_point;
-       struct lease *last_lease;
-       struct lease *next_expiry;
+       struct lease *active;
+       struct lease *expired;
+       struct lease *free;
+       struct lease *backup;
+       struct lease *abandoned;
+       TIME next_event_time;
 #if defined (FAILOVER_PROTOCOL)
        int lease_count;
-       int local_leases;
-       int peer_leases;
+       int free_leases;
+       int backup_leases;
        dhcp_failover_state_t *failover_peer;
 #endif
 };
@@ -1550,6 +1548,8 @@ extern u_int32_t fto_allowed [];
 extern int ft_sizes [];
 extern const char *dhcp_flink_state_names [];
 #endif
+extern const char *binding_state_names [];
+
 extern struct universe agent_universe;
 extern struct option agent_options [256];
 extern struct universe server_universe;
@@ -1617,8 +1617,8 @@ void make_release PROTO ((struct client_state *, struct client_lease *));
 
 void destroy_client_lease PROTO ((struct client_lease *));
 void rewrite_client_leases PROTO ((void));
-void write_client_lease PROTO ((struct client_state *,
-                                struct client_lease *, int));
+int write_client_lease PROTO ((struct client_state *,
+                              struct client_lease *, int, int));
 char *dhcp_option_ev_name PROTO ((struct option *));
 
 void script_init PROTO ((struct client_state *, const char *,
@@ -2115,7 +2115,8 @@ void new_shared_network_interface PROTO ((struct parse *,
 int subnet_inner_than PROTO ((struct subnet *, struct subnet *, int));
 void enter_subnet PROTO ((struct subnet *));
 void enter_lease PROTO ((struct lease *));
-int supersede_lease PROTO ((struct lease *, struct lease *, int, int));
+int supersede_lease PROTO ((struct lease *, struct lease *, int, int, int));
+void process_state_transition (struct lease *);
 int lease_copy PROTO ((struct lease **, struct lease *, const char *, int));
 void release_lease PROTO ((struct lease *, struct packet *));
 void abandon_lease PROTO ((struct lease *, const char *));
@@ -2205,7 +2206,7 @@ int dhcp_failover_pool_rebalance (dhcp_failover_state_t *);
 int dhcp_failover_pool_check (struct pool *);
 int dhcp_failover_state_pool_check (dhcp_failover_state_t *);
 isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *);
-int dhcp_failover_queue_update (struct lease *);
+int dhcp_failover_queue_update (struct lease *, int);
 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *, struct lease *);
 isc_result_t dhcp_failover_state_set_value PROTO ((omapi_object_t *,
                                                   omapi_object_t *,
@@ -2243,7 +2244,9 @@ failover_option_t *dhcp_failover_make_option PROTO ((unsigned, char *,
 isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *,
                                        omapi_object_t *, int, ...);
 isc_result_t dhcp_failover_send_connect PROTO ((omapi_object_t *));
-isc_result_t dhcp_failover_send_connectack PROTO ((omapi_object_t *, int));
+isc_result_t dhcp_failover_send_connectack PROTO ((omapi_object_t *,
+                                                  dhcp_failover_state_t *,
+                                                  int, const char *));
 isc_result_t dhcp_failover_send_disconnect PROTO ((omapi_object_t *,
                                                   int, const char *));
 isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *,
@@ -2260,6 +2263,11 @@ isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *,
 void failover_print PROTO ((char *, unsigned *, unsigned, const char *));
 void update_partner PROTO ((struct lease *));
 int load_balance_mine (struct packet *, dhcp_failover_state_t *);
+binding_state_t binding_state_transition_check (struct lease *,
+                                               dhcp_failover_state_t *,
+                                               binding_state_t);
+int lease_mine_to_extend (struct lease *);
+
 OMAPI_OBJECT_ALLOC_DECL (dhcp_failover_state, dhcp_failover_state_t,
                         dhcp_type_failover_state)
 OMAPI_OBJECT_ALLOC_DECL (dhcp_failover_listener, dhcp_failover_listener_t,
index 3ed4751922bac27e2025b33bc06048a7e21b1fb5..98042d484374f689ebb8b38ed3ccb8aa4dd3610a 100644 (file)
@@ -114,7 +114,6 @@ enum dhcp_token {
        EXPIRE = 308,
        UNKNOWN_CLIENTS = 309,
        ALLOW = 310,
-       BOOTP = 311,
        DENY = 312,
        BOOTING = 313,
        DEFAULT = 314,
@@ -122,7 +121,7 @@ enum dhcp_token {
        MEDIUM = 316,
        ALIAS = 317,
        REBOOT = 318,
-       ABANDONED = 319,
+       TOKEN_ABANDONED = 319,
        BACKOFF_CUTOFF = 320,
        INITIAL_INTERVAL = 321,
        NAMESERVER = 322,
@@ -216,7 +215,7 @@ enum dhcp_token {
        STATIC = 414,
        NEVER = 415,
        INFINITE = 416,
-       DELETED = 417,
+       TOKEN_DELETED = 417,
        UPDATED_DNS_RR = 418,
        DNS_DELETE = 419,
        DUPLICATES = 420,
@@ -268,7 +267,17 @@ enum dhcp_token {
        STATE = 466,
        UNKNOWN_STATE = 567,
        CLTT = 568,
-       INCLUDE = 569
+       INCLUDE = 569,
+       BINDING = 570,
+       TOKEN_FREE = 571,
+       TOKEN_ACTIVE = 572,
+       TOKEN_EXPIRED = 573,
+       TOKEN_RELEASED = 574,
+       TOKEN_RESET = 575,
+       TOKEN_BACKUP = 576,
+       TOKEN_RESERVED = 577,
+       TOKEN_BOOTP = 578,
+       TOKEN_NEXT = 579,
 };
 
 #define is_identifier(x)       ((x) >= FIRST_TOKEN &&  \
index 9105a210470ff55fac084f1f1472195c7f4b7ebc..fef57f85698e5de21547cb0e149487402888cfd0 100644 (file)
@@ -157,13 +157,17 @@ typedef struct {
 #define FTR_UNKNOWN            254
 
 /* Lease states: */
-#define FTS_FREE               1
-#define FTS_ACTIVE             2
-#define FTS_EXPIRED            3
-#define FTS_RELEASED           4
-#define FTS_ABANDONED          5
-#define FTS_RESET              6
-#define FTS_BACKUP             7
+typedef enum {
+       FTS_FREE = 1,
+       FTS_ACTIVE = 2,
+       FTS_EXPIRED = 3,
+       FTS_RELEASED = 4,
+       FTS_ABANDONED = 5,
+       FTS_RESET = 6,
+       FTS_BACKUP = 7,
+       FTS_RESERVED = 8,
+       FTS_BOOTP = 9
+} binding_state_t;
 
 #define DHCP_FAILOVER_MAX_MESSAGE_SIZE 2048
 
index 8f877346af12529b69ef855cebaa2547d5d1f2f9..ae5a30b9c6d972ec774c70a29a332c497a5f8a17 100644 (file)
@@ -43,7 +43,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: confpars.c,v 1.112 2000/05/18 20:21:43 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: confpars.c,v 1.113 2000/06/02 21:27:11 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -711,6 +711,10 @@ void parse_failover_peer (cfile, group, type)
 
                      case SECONDARY:
                        peer -> i_am = secondary;
+                       if (peer -> hba)
+                               parse_warn (cfile,
+                                           "secondary may not define %s",
+                                           "load balance settings.");
                        break;
 
                      case PEER:
@@ -783,6 +787,10 @@ void parse_failover_peer (cfile, group, type)
 
                      case HBA:
                        hba_len = 32;
+                       if (peer -> i_am == secondary)
+                               parse_warn (cfile,
+                                           "secondary may not define %s",
+                                           "load balance settings.");
                        if (!parse_numeric_aggregate (cfile, hba, &hba_len,
                                                      COLON, 16, 8)) {
                                skip_to_rbrace (cfile, 1);
@@ -806,6 +814,10 @@ void parse_failover_peer (cfile, group, type)
 
                      case SPLIT:
                        token = next_token (&val, cfile);
+                       if (peer -> i_am == secondary)
+                               parse_warn (cfile,
+                                           "secondary may not define %s",
+                                           "load balance settings.");
                        if (token != NUMBER) {
                                parse_warn (cfile, "expecting number");
                              badsplit:
@@ -868,6 +880,11 @@ void parse_failover_peer (cfile, group, type)
                }
        } while (token != RBRACE);
                
+       if (peer -> i_am == primary && !peer -> hba) {
+               parse_warn (cfile, 
+                           "primary failover server must have hba or split.");
+       }
+
        if (type == SHARED_NET_DECL) {
                group -> shared_network -> failover_peer = peer;
        }
@@ -1175,7 +1192,7 @@ void parse_pool_statement (cfile, group, type)
                                
                              case DYNAMIC:
                                permit -> type = permit_dynamic_bootp_clients;
-                               if (next_token (&val, cfile) != BOOTP) {
+                               if (next_token (&val, cfile) != TOKEN_BOOTP) {
                                        parse_warn (cfile,
                                                    "expecting \"bootp\"");
                                        skip_to_semi (cfile);
@@ -1385,7 +1402,7 @@ void parse_host_declaration (cfile, group)
                }
                /* If the host declaration was created by the server,
                   remember to save it. */
-               if (token == DELETED) {
+               if (token == TOKEN_DELETED) {
                        deleted = 1;
                        token = next_token (&val, cfile);
                        if (!parse_semi (cfile))
@@ -2071,7 +2088,7 @@ void parse_group_declaration (cfile, group)
                        token = next_token (&val, cfile);
                        parse_warn (cfile, "unexpected end of file");
                        break;
-               } else if (token == DELETED) {
+               } else if (token == TOKEN_DELETED) {
                        token = next_token (&val, cfile);
                        parse_semi (cfile);
                        deletedp = 1;
@@ -2215,6 +2232,7 @@ int parse_lease_declaration (struct lease **lp, struct parse *cfile)
        int noequal, newbinding;
        struct binding *binding;
        isc_result_t status;
+       binding_state_t *statep;
 
        lease = (struct lease *)0;
        status = lease_allocate (&lease, MDL);
@@ -2351,34 +2369,76 @@ int parse_lease_declaration (struct lease **lp, struct parse *cfile)
                        break;
 
                      case DYNAMIC_BOOTP:
-                       seenbit = 128;
-                       lease -> flags |= BOOTP_LEASE;
+                       seenbit = 256;
+                       lease -> binding_state = FTS_BOOTP;
+                       lease -> next_binding_state = FTS_BOOTP;
                        parse_semi (cfile);
                        break;
                        
-                     case PEER:
-                       token = next_token (&val, cfile);
-                       if (token != IS) {
-                               parse_warn (cfile, "expecting \"is\".");
-                               skip_to_rbrace (cfile, 1);
-                               lease_dereference (&lease, MDL);
-                               return 0;
-                       }
-                       token = next_token (&val, cfile);
-                       if (token != OWNER) {
-                               parse_warn (cfile, "expecting \"owner\".");
-                               skip_to_rbrace (cfile, 1);
-                               lease_dereference (&lease, MDL);
-                               return 0;
-                       }
-                       seenbit = 262144;
-                       lease -> flags |= PEER_IS_OWNER;
+                     case TOKEN_ABANDONED:
+                       seenbit = 256;
+                       lease -> binding_state = FTS_ABANDONED;
+                       lease -> next_binding_state = FTS_ABANDONED;
                        parse_semi (cfile);
                        break;
 
-                     case ABANDONED:
+                     case TOKEN_NEXT:
+                       seenbit = 128;
+                       statep = &lease -> next_binding_state;
+                       goto do_binding_state;
+
+                     case BINDING:
                        seenbit = 256;
-                       lease -> flags |= ABANDONED_LEASE;
+                       statep = &lease -> binding_state;
+
+                     do_binding_state:
+                       token = next_token (&val, cfile);
+                       if (token != STATE) {
+                               parse_warn (cfile, "expecting 'state'");
+                               skip_to_semi (cfile);
+                               break;
+                       }
+                       token = next_token (&val, cfile);
+                       switch (token) {
+                             case TOKEN_ABANDONED:
+                               *statep = FTS_ABANDONED;
+                               break;
+                             case TOKEN_FREE:
+                               *statep = FTS_FREE;
+                               break;
+                             case TOKEN_ACTIVE:
+                               *statep = FTS_ACTIVE;
+                               break;
+                             case TOKEN_EXPIRED:
+                               *statep = FTS_EXPIRED;
+                               break;
+                             case TOKEN_RELEASED:
+                               *statep = FTS_RELEASED;
+                               break;
+                             case TOKEN_RESET:
+                               *statep = FTS_RESET;
+                               break;
+                             case TOKEN_BACKUP:
+                               *statep = FTS_BACKUP;
+                               break;
+                             case TOKEN_RESERVED:
+                               *statep = FTS_RESERVED;
+                               break;
+                             case TOKEN_BOOTP:
+                               *statep = FTS_BOOTP;
+                               break;
+                             default:
+                               parse_warn (cfile,
+                                           "%s: expecting a binding state.",
+                                           val);
+                               skip_to_semi (cfile);
+                               break;
+                       }
+                       /* If no next binding state is specified, it's
+                          the same as the current state. */
+                       if (!(seenmask & 128) && seenbit == 256)
+                               lease -> next_binding_state =
+                                       lease -> binding_state;
                        parse_semi (cfile);
                        break;
 
@@ -2817,7 +2877,7 @@ int parse_allow_deny (oc, cfile, flag)
 
        token = next_token (&val, cfile);
        switch (token) {
-             case BOOTP:
+             case TOKEN_BOOTP:
                status = option_cache (oc, (struct data_string *)0, data,
                                       &server_options [SV_ALLOW_BOOTP]);
                break;
index da252c5fcddbd8bacc5ad1ab8900742bb3d81dcc..6b8a3ba232ef9571b6f556e85b6556443723fada 100644 (file)
@@ -43,7 +43,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: db.c,v 1.50 2000/05/16 23:03:39 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: db.c,v 1.51 2000/06/02 21:27:12 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -143,13 +143,19 @@ int write_lease (lease)
                }
        }
 
-       if (lease -> flags & PEER_IS_OWNER) {
-               errno = 0;
-               fprintf (db_file, "\n  peer is owner;");
-               if (errno) {
-                       ++errors;
-               }
-       }
+       fprintf (db_file, "\n  binding state %s;",
+                ((lease -> binding_state > 0 &&
+                  lease -> binding_state <= FTS_BOOTP)
+                 ? binding_state_names [lease -> binding_state - 1]
+                 : "abandoned"));
+
+       if (lease -> binding_state != lease -> next_binding_state)
+               fprintf (db_file, "\n  next binding state %s;",
+                        ((lease -> next_binding_state > 0 &&
+                          lease -> next_binding_state <= FTS_BOOTP)
+                         ? (binding_state_names
+                            [lease -> next_binding_state - 1])
+                 : "abandoned"));
 
        /* If this lease is billed to a class and is still valid,
           write it out. */
@@ -190,20 +196,6 @@ int write_lease (lease)
                        putc (';', db_file);
                }
        }
-       if (lease -> flags & BOOTP_LEASE) {
-               errno = 0;
-               fprintf (db_file, "\n  dynamic-bootp;");
-               if (errno) {
-                       ++errors;
-               }
-       }
-       if (lease -> flags & ABANDONED_LEASE) {
-               errno = 0;
-               fprintf (db_file, "\n  abandoned;");
-               if (errno) {
-                       ++errors;
-               }
-       }
        for (b = lease -> scope.bindings; b; b = b -> next) {
                if (!b -> value)
                        continue;
index ff000ca6478d62ea15cac55ca973c3e739f45dc6..bd8c0e43a7a2c81eecdbb365141ce6f04391c13a 100644 (file)
@@ -43,7 +43,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: dhcp.c,v 1.150 2000/05/17 16:04:25 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: dhcp.c,v 1.151 2000/06/02 21:27:13 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -138,15 +138,10 @@ void dhcpdiscover (packet, ms_nulltp)
                  : packet -> interface -> name);
 
 #if defined (FAILOVER_PROTOCOL)
-       if (lease && lease -> pool &&
-           lease -> pool -> failover_peer) {
-               peer = lease -> pool -> failover_peer;
-               if ((lease -> flags & PEER_IS_OWNER) &&
-                   peer -> my_state == normal) {
-                       log_info ("%s: letting peer %s respond.",
-                                 msgbuf, peer -> name);
-                       goto out;
-               }
+       if (lease && !lease_mine_to_extend (lease)) {
+               log_info ("%s: letting peer %s answer", msgbuf,
+                         lease -> pool -> failover_peer -> name);
+               goto out;
        }
 #endif
 
@@ -191,7 +186,7 @@ void dhcpdiscover (packet, ms_nulltp)
        if (allocatedp && lease && lease -> pool &&
            lease -> pool -> failover_peer) {
                peer = lease -> pool -> failover_peer;
-               if (peer -> hba && peer -> my_state == normal) {
+               if (peer -> my_state == normal) {
                        if (!load_balance_mine (packet, peer)) {
                                log_debug ("%s: load balance to peer %s",
                                           msgbuf, peer -> name);
@@ -256,6 +251,7 @@ void dhcprequest (packet, ms_nulltp)
                find_lease (&lease, packet,
                            subnet -> shared_network, &ours, MDL);
 
+
        if (lease && lease -> client_hostname &&
            db_printable (lease -> client_hostname))
                s = lease -> client_hostname;
@@ -278,6 +274,14 @@ void dhcprequest (packet, ms_nulltp)
                  ? inet_ntoa (packet -> raw -> giaddr)
                  : packet -> interface -> name);
 
+#if defined (FAILOVER_PROTOCOL)
+       if (lease && !lease_mine_to_extend (lease)) {
+               log_info ("%s: letting peer %s answer", msgbuf,
+                         lease -> pool -> failover_peer -> name);
+               goto out;
+       }
+#endif
+
        /* If a client on a given network REQUESTs a lease on an
           address on a different network, NAK it.  If the Requested
           Address option was used, the protocol says that it must
@@ -360,7 +364,7 @@ void dhcprequest (packet, ms_nulltp)
                }
        }
 
-#if defined (FAILOVER_PROTOCOL)
+#if defined (FAILOVER_PROTOCOL) && 0 /* XXX this isn't the same as above! */
        /* If we found a lease, but it belongs to a failover peer, and
           the client is in the SELECTING state, ignore the request -
           it's not ours. */
@@ -420,6 +424,7 @@ void dhcprelease (packet, ms_nulltp)
        struct option_cache *oc;
        struct data_string data;
        char *s;
+       char msgbuf [1024]; /* XXX */
 
        /* DHCPRELEASE must not specify address in requested-address
            option, but old protocol specs weren't explicit about this,
@@ -469,27 +474,44 @@ void dhcprelease (packet, ms_nulltp)
                s = (char *)0;
 
        /* Say what we're doing... */
-       log_info ("DHCPRELEASE of %s from %s %s%s%svia %s (%sfound)",
-                 inet_ntoa (packet -> raw -> ciaddr),
-                 (packet -> raw -> htype
-                  ? print_hw_addr (packet -> raw -> htype,
-                            packet -> raw -> hlen,
-                            packet -> raw -> chaddr)
-                  : (lease
-                     ? print_hex_1 (lease -> uid_len, lease -> uid, 
-                                    lease -> uid_len)
-                     : "<no identifier>")),
-                 s ? "(" : "", s ? s : "", s ? ") " : "",
-                 packet -> raw -> giaddr.s_addr
-                 ? inet_ntoa (packet -> raw -> giaddr)
-                 : packet -> interface -> name,
-                 lease ? "" : "not ");
+       sprintf (msgbuf,
+                "DHCPRELEASE of %s from %s %s%s%svia %s (%sfound)",
+                inet_ntoa (packet -> raw -> ciaddr),
+                (packet -> raw -> htype
+                 ? print_hw_addr (packet -> raw -> htype,
+                                  packet -> raw -> hlen,
+                                  packet -> raw -> chaddr)
+                 : (lease
+                    ? print_hex_1 (lease -> uid_len, lease -> uid, 
+                                   lease -> uid_len)
+                    : "<no identifier>")),
+                s ? "(" : "", s ? s : "", s ? ") " : "",
+                packet -> raw -> giaddr.s_addr
+                ? inet_ntoa (packet -> raw -> giaddr)
+                : packet -> interface -> name,
+                lease ? "" : "not ");
+
+#if defined (FAILOVER_PROTOCOL)
+       if (lease && !lease_mine_to_extend (lease)) {
+               /* DHCPRELEASE messages are unicast, so if the client
+                  sent the DHCPRELEASE to us, it's not going to send it
+                  to the peer.   Not sure why this would happen, and
+                  if it does happen I think we still have to change the
+                  lease state.
+                  XXX See what it says in the draft about this. */
+               log_info ("%s: peer %s holds lease",
+                         msgbuf, lease -> pool -> failover_peer -> name);
+       }
+#endif
 
        /* If we found a lease, release it. */
        if (lease && lease -> ends > cur_time) {
                release_lease (lease, packet);
-               lease_dereference (&lease, MDL);
+               log_info ("%s", msgbuf);
        }
+      out:
+       if (lease)
+               lease_dereference (&lease, MDL);
 }
 
 void dhcpdecline (packet, ms_nulltp)
@@ -505,6 +527,7 @@ void dhcpdecline (packet, ms_nulltp)
        int i;
        const char *status;
        char *s;
+       char msgbuf [1024]; /* XXX */
 
        /* DHCPDECLINE must specify address. */
        if (!(oc = lookup_option (&dhcp_universe, packet -> options,
@@ -522,6 +545,36 @@ void dhcpdecline (packet, ms_nulltp)
        data_string_forget (&data, MDL);
        find_lease_by_ip_addr (&lease, cip, MDL);
 
+       if (lease && lease -> client_hostname &&
+           db_printable (lease -> client_hostname))
+               s = lease -> client_hostname;
+       else
+               s = (char *)0;
+
+       sprintf (msgbuf, "DHCPDECLINE of %s from %s %s%s%svia %s",
+                piaddr (cip),
+                (packet -> raw -> htype
+                 ? print_hw_addr (packet -> raw -> htype,
+                                  packet -> raw -> hlen,
+                                  packet -> raw -> chaddr)
+                 : (lease
+                    ? print_hex_1 (lease -> uid_len, lease -> uid, 
+                                   lease -> uid_len)
+                    : "<no identifier>")),
+                s ? "(" : "", s ? s : "", s ? ") " : "",
+                packet -> raw -> giaddr.s_addr
+                ? inet_ntoa (packet -> raw -> giaddr)
+                : packet -> interface -> name);
+
+#if defined (FAILOVER_PROTOCOL)
+       if (lease && !lease_mine_to_extend (lease)) {
+               if (!ignorep)
+                       log_info ("%s: peer %s holds lease", msgbuf,
+                                 lease -> pool -> failover_peer -> name);
+               goto out;
+       }
+#endif
+
        option_state_allocate (&options, MDL);
 
        /* Execute statements in scope starting with the subnet scope. */
@@ -549,38 +602,18 @@ void dhcpdecline (packet, ms_nulltp)
                /* If we found a lease, mark it as unusable and complain. */
                if (lease) {
                        abandon_lease (lease, "declined.");
-                       status = "";
+                       status = "abandoned";
                }
-               status = " (not found)";
+               status = "not found";
        } else
-               status = " (ignored)";
+               status = "ignored";
 
-       if (!ignorep) {
-               char *s;
-               if (lease && lease -> client_hostname &&
-                   db_printable (lease -> client_hostname))
-                       s = lease -> client_hostname;
-               else
-                       s = (char *)0;
-
-               log_info ("DHCPDECLINE of %s from %s %s%s%svia %s %s",
-                         piaddr (cip),
-                         (packet -> raw -> htype
-                          ? print_hw_addr (packet -> raw -> htype,
-                                           packet -> raw -> hlen,
-                                           packet -> raw -> chaddr)
-                          : (lease
-                             ? print_hex_1 (lease -> uid_len, lease -> uid, 
-                                            lease -> uid_len)
-                             : "<no identifier>")),
-                         s ? "(" : "", s ? s : "", s ? ") " : "",
-                         packet -> raw -> giaddr.s_addr
-                         ? inet_ntoa (packet -> raw -> giaddr)
-                         : packet -> interface -> name,
-                         status);
-       }
+       if (!ignorep)
+               log_info ("%s: %s", msgbuf, status);
                
-       option_state_dereference (&options, MDL);
+      out:
+       if (options)
+               option_state_dereference (&options, MDL);
        if (lease)
                lease_dereference (&lease, MDL);
 }
@@ -1358,7 +1391,8 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp)
 
        /* Try to find a matching host declaration for this lease. */
        if (!lease -> host) {
-               struct host_decl *hp;
+               struct host_decl *hp = (struct host_decl *)0;
+               struct host_decl *h;
 
                /* Try to find a host_decl that matches the client
                   identifier or hardware address on the packet, and
@@ -1371,26 +1405,26 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp)
                    evaluate_option_cache (&d1, packet, lease,
                                           packet -> options, state -> options,
                                           &lease -> scope, oc, MDL)) {
-                       struct host_decl *h;
-                       hp = (struct host_decl *)0;
                        find_hosts_by_uid (&hp, d1.data, d1.len, MDL);
                        data_string_forget (&d1, MDL);
-                       if (!hp)
-                               find_hosts_by_haddr (&hp,
-                                                    packet -> raw -> htype,
-                                                    packet -> raw -> chaddr,
-                                                    packet -> raw -> hlen,
-                                                    MDL);
+                       if (hp)
+                               host_reference (&lease -> host, hp, MDL);
+               }
+               if (!hp) {
+                       find_hosts_by_haddr (&hp,
+                                            packet -> raw -> htype,
+                                            packet -> raw -> chaddr,
+                                            packet -> raw -> hlen,
+                                            MDL);
                        for (h = hp; h; h = h -> n_ipaddr) {
                                if (!h -> fixed_addr)
                                        break;
                        }
                        if (h)
                                host_reference (&lease -> host, hp, MDL);
-                       if (hp)
-                               host_dereference (&hp, MDL);
-               } else
-                       lease -> host = (struct host_decl *)0;
+               }
+               if (hp)
+                       host_dereference (&hp, MDL);
        }
 
        /* Drop the request if it's not allowed for this client.   By
@@ -1640,6 +1674,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp)
                        lt -> ends = when;
                else
                        lt -> ends = state -> offered_expiry;
+               lt -> next_binding_state = FTS_ACTIVE;
        } else {
                lease_time = MAX_TIME - cur_time;
 
@@ -1669,7 +1704,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp)
                }
 
                lt -> ends = state -> offered_expiry = cur_time + lease_time;
-               lt -> flags = BOOTP_LEASE;
+               lt -> next_binding_state = FTS_BOOTP;
        }
 
        lt -> timestamp = cur_time;
@@ -1752,29 +1787,13 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp)
                   it) either. */
 
                if (!(supersede_lease (lease, lt, !offer || offer == DHCPACK,
-                                      offer == DHCPACK)
+                                      offer == DHCPACK, offer == DHCPACK)
                      || (offer && offer != DHCPACK))) {
                        log_info ("%s: database update failed", msg);
                        free_lease_state (state, MDL);
                        static_lease_dereference (lease, MDL);
                        lease_dereference (&lt, MDL);
                        return;
-               } else {
-                       /* If this is a DHCPOFFER transaction, supersede_lease
-                          will not add the timer for the expire event to the
-                          queue.  This is because DHCPOFFERS are not commited,
-                          and supersede_lease only adds commited leases to the
-                          timer queue.  So if supersede_lease set this lease
-                          as the next one to expire for the pool we need to
-                          put it on the timer queue ourself. */
-                       /* XXX need to think about this. */
-                       if (offer == DHCPOFFER && lease -> pool &&
-                           lease -> pool -> next_expiry == lease)
-                               add_timeout (lease -> ends, pool_timer,
-                                            lease -> pool,
-                                            (tvref_t)omapi_object_reference,
-                                            (tvunref_t)
-                                            omapi_object_dereference);
                }
        }
        lease_dereference (&lt, MDL);
@@ -2637,7 +2656,6 @@ int find_lease (struct lease **lp,
        /* Toss ip_lease if it hasn't yet expired and doesn't belong to the
           client. */
        if (ip_lease &&
-           ip_lease -> ends >= cur_time &&
            ((ip_lease -> uid &&
              (!have_client_identifier ||
               ip_lease -> uid_len != client_identifier.len ||
@@ -2649,25 +2667,43 @@ int find_lease (struct lease **lp,
               memcmp (&ip_lease -> hardware_addr.hbuf [1],
                       packet -> raw -> chaddr,
                       (unsigned)(ip_lease -> hardware_addr.hlen - 1)))))) {
+               /* If we're not doing failover, the only state in which
+                  we can allocate this lease to the client is FTS_FREE.
+                  If we are doing failover, and this lease is part of a
+                  failover pool, then if we're the primary, state has to be
+                  FTS_FREE; if we're the secondary, state has to be
+                  FTS_BACKUP. */
+               if ((ip_lease -> binding_state != FTS_FREE &&
+                    ip_lease -> binding_state != FTS_BACKUP)
+#if defined (FAILOVER_PROTOCOL)
+                   ||
+                   (ip_lease -> pool -> failover_peer &&
+                    ((ip_lease -> binding_state == FTS_FREE &&
+                      ip_lease -> pool -> failover_peer -> i_am == secondary)
+                     ||
+                     (ip_lease -> binding_state == FTS_BACKUP &&
+                      ip_lease -> pool -> failover_peer -> i_am == primary)))
+#endif
+                       ) {
 #if defined (DEBUG_FIND_LEASE)
-               if (ip_lease)
                        log_info ("rejecting lease for requested address.");
 #endif
-               lease_dereference (&ip_lease, MDL);
+                       lease_dereference (&ip_lease, MDL);
+               }
        }
 
        /* If for some reason the client has more than one lease
           on the subnet that matches its uid, pick the one that
           it asked for and (if we can) free the other. */
        if (ip_lease &&
-           ip_lease -> ends >= cur_time &&
+           ip_lease -> binding_state == FTS_ACTIVE &&
            ip_lease -> uid && ip_lease != uid_lease) {
                if (have_client_identifier &&
                    (ip_lease -> uid_len == client_identifier.len) &&
                    !memcmp (client_identifier.data,
                             ip_lease -> uid, ip_lease -> uid_len)) {
                        if (uid_lease) {
-                           if (uid_lease -> ends > cur_time) {
+                           if (uid_lease -> binding_state == FTS_ACTIVE) {
                                log_error ("client %s has duplicate%s on %s",
                                           (print_hw_addr
                                            (packet -> raw -> htype,
@@ -2722,17 +2758,6 @@ int find_lease (struct lease **lp,
        if (packet -> packet_type == DHCPREQUEST && fixed_lease && ip_lease)
                goto db_conflict;
 
-       /* Make sure the client is permitted to use the requested lease. */
-       if (ip_lease &&
-           ((ip_lease -> pool -> prohibit_list &&
-             permitted (packet, ip_lease -> pool -> prohibit_list)) ||
-            (ip_lease -> pool -> permit_list &&
-             !permitted (packet, ip_lease -> pool -> permit_list)))) {
-               if (!packet -> raw -> ciaddr.s_addr)
-                       release_lease (ip_lease, packet);
-               lease_dereference (&ip_lease, MDL);
-       }
-
        /* Toss extra pointers to the same lease... */
        if (hw_lease && hw_lease == uid_lease) {
 #if defined (DEBUG_FIND_LEASE)
@@ -2753,6 +2778,37 @@ int find_lease (struct lease **lp,
 #endif
        }
 
+       /* Make sure the client is permitted to use the requested lease. */
+       if (ip_lease &&
+           ((ip_lease -> pool -> prohibit_list &&
+             permitted (packet, ip_lease -> pool -> prohibit_list)) ||
+            (ip_lease -> pool -> permit_list &&
+             !permitted (packet, ip_lease -> pool -> permit_list)))) {
+               if (!packet -> raw -> ciaddr.s_addr)
+                       release_lease (ip_lease, packet);
+               lease_dereference (&ip_lease, MDL);
+       }
+
+       if (uid_lease &&
+           ((uid_lease -> pool -> prohibit_list &&
+             permitted (packet, uid_lease -> pool -> prohibit_list)) ||
+            (uid_lease -> pool -> permit_list &&
+             !permitted (packet, uid_lease -> pool -> permit_list)))) {
+               if (!packet -> raw -> ciaddr.s_addr)
+                       release_lease (uid_lease, packet);
+               lease_dereference (&uid_lease, MDL);
+       }
+
+       if (hw_lease &&
+           ((hw_lease -> pool -> prohibit_list &&
+             permitted (packet, hw_lease -> pool -> prohibit_list)) ||
+            (hw_lease -> pool -> permit_list &&
+             !permitted (packet, hw_lease -> pool -> permit_list)))) {
+               if (!packet -> raw -> ciaddr.s_addr)
+                       release_lease (hw_lease, packet);
+               lease_dereference (&hw_lease, MDL);
+       }
+
        /* If we've already eliminated the lease, it wasn't there to
           begin with.   If we have come up with a matching lease,
           set the message to bad network in case we have to throw it out. */
@@ -2862,12 +2918,13 @@ int find_lease (struct lease **lp,
           requested, we assume that previous bugginess on the part
           of the client, or a server database loss, caused the lease to
           be abandoned, so we reclaim it and let the client have it. */
-       if (lease && (lease -> flags & ABANDONED_LEASE) && lease == ip_lease &&
+       if (lease &&
+           (lease -> binding_state == FTS_ABANDONED) &&
+           lease == ip_lease &&
            packet -> packet_type == DHCPREQUEST) {
                log_error ("Reclaiming REQUESTed abandoned IP address %s.",
                      piaddr (lease -> ip_addr));
-               lease -> flags &= ~ABANDONED_LEASE;
-       } else if (lease && (lease -> flags & ABANDONED_LEASE)) {
+       } else if (lease && (lease -> binding_state == FTS_ABANDONED)) {
        /* Otherwise, if it's not the one the client requested, we do not
           return it - instead, we claim it's ours, causing a DHCPNAK to be
           sent if this lookup is for a DHCPREQUEST, and force the client
@@ -2941,6 +2998,7 @@ int mockup_lease (struct lease **lp, struct packet *packet,
        lease -> hardware_addr = hp -> interface;
        lease -> starts = lease -> timestamp = lease -> ends = MIN_TIME;
        lease -> flags = STATIC_LEASE;
+       lease -> binding_state = FTS_FREE;
        lease_reference (lp, lease, MDL);
        lease_dereference (&lease, MDL);
        return 1;
@@ -2986,6 +3044,7 @@ int allocate_lease (struct lease **lp, struct packet *packet,
                    struct pool *pool, int *peer_has_leases)
 {
        struct lease *lease = (struct lease *)0;
+       struct lease **lq;
        struct permit *permit;
 
        if (!pool)
@@ -2998,31 +3057,39 @@ int allocate_lease (struct lease **lp, struct packet *packet,
                return allocate_lease (lp, packet, pool -> next,
                                       peer_has_leases);
 
-       lease = pool -> last_lease;
-
 #if defined (FAILOVER_PROTOCOL)
        /* Peer_has_leases just says that we found at least one free lease.
           If no free lease is returned, the caller can deduce that this
           means the peer is hogging all the free leases, so we can print
           a better error message. */
-       if (lease)
-               *peer_has_leases = 1;
 
        /* XXX Do we need code here to ignore PEER_IS_OWNER and just check
           XXX tstp if we're in, e.g., PARTNER_DOWN?   Where do we deal with
           XXX CONFLICT_DETECTED, et al? */
+       /* XXX This should be handled by the lease binding "state machine" -
+          XXX that is, when we get here, if a lease could be allocated, it
+          XXX will have the correct binding state so that the following code
+          XXX will result in its being allocated. */
        /* Skip to the most expired lease in the pool that is not owned by a
           failover peer. */
-       if (lease -> pool && lease -> pool -> failover_peer) {
-               while (lease &&
-                      (lease -> flags & PEER_IS_OWNER) &&
-                      (lease -> ends <=
-                       cur_time + lease -> pool -> failover_peer -> mclt))
-                       lease = lease -> prev;
-               /* We didn't find an unexpired lease that we own? */
-               if (lease && lease -> flags & PEER_IS_OWNER)
-                       lease = (struct lease *)0;
+       if (pool -> failover_peer) {
+               if (pool -> failover_peer -> i_am == primary) {
+                       if (pool -> backup)
+                               *peer_has_leases = 1;
+                       lease = pool -> free;
+                       if (!lease)
+                               lease = pool -> abandoned;
+               } else {
+                       if (pool -> free)
+                               *peer_has_leases = 1;
+                       lease = pool -> backup;
+               }
        }
+#else
+       if (pool -> free)
+               lease = pool -> free;
+       else
+               lease = pool -> abandoned;
 #endif
 
        /* If there are no leases in the pool that have
@@ -3035,7 +3102,7 @@ int allocate_lease (struct lease **lp, struct packet *packet,
           better, take it. */
        /* XXX what if there are non-abandoned leases that are younger
           XXX than this?   Shouldn't we hunt for those here? */
-       if ((lease -> flags & ABANDONED_LEASE)) {
+       if (lease -> binding_state == FTS_ABANDONED) {
                /* If we already have a non-abandoned lease that we didn't
                   love, but that's okay, don't reclaim the abandoned lease. */
                if (*lp)
@@ -3045,7 +3112,6 @@ int allocate_lease (struct lease **lp, struct packet *packet,
                                     pool -> next, peer_has_leases)) {
                        log_error ("Reclaiming abandoned IP address %s.",
                              piaddr (lease -> ip_addr));
-                       lease -> flags &= ~ABANDONED_LEASE;
                        lease_reference (lp, lease, MDL);
                }
                return 1;
index d50b031af24cb1eddf51a5154543f54d00474db4..a4b66c11ae40b9ff2f2658c69666f6317fc1991b 100644 (file)
@@ -1272,18 +1272,21 @@ override the use of the name in the host declaration.
  \fBnot authoritative;\fR
 .PP
 The DHCP server will normally assume that the configuration
-information about a given network segment is known to be correct and
-is authoritative.   So if a client requests an IP address on a given
-network segment that the server knows is not valid for that segment,
-the server will respond with a DHCPNAK message, causing the client to
-forget its IP address and try to get a new one.
-.PP
-If a DHCP server is being configured by somebody who is not the
-network administrator and who therefore does not wish to assert this
-level of authority, then the statement "not authoritative" should be
-written in the appropriate scope in the configuration file.
-.PP
-Usually, writing \fBnot authoritative;\fR at the top level of the file
+information about a given network segment is not known to be correct
+and is not authoritative.  This is so that if a naive user installs a
+DHCP server not fully understanding how to configure it, it does not
+send spurious DHCPNAK messages to clients that have obtained addresses
+from a legitimate DHCP server on the network.
+.PP
+Network administrators setting up authoritative DHCP servers for their
+networks should always write \fBauthoritative;\fR at the top of their
+configuration file to indicate that the DHCP server \fIshould\fR send
+DHCPNAK messages to misconfigured clients.   If this is not done,
+clients will be unable to get a correct IP address after changing
+subnets until their old lease has expired, which could take quite a
+long time.
+.PP
+Usually, writing \fBauthoritative;\fR at the top level of the file
 should be sufficient.   However, if a DHCP server is to be set up so
 that it is aware of some networks for which it is authoritative and
 some networks for which it is not, it may be more appropriate to
index cb619ae6bed4fb9e102456e4a0dfe66bfe8ccdd6..f104d635030fbab1315e5e6cf396f628c7ef8634 100644 (file)
@@ -1478,39 +1478,39 @@ dhcpd.conf(5)                                       dhcpd.conf(5)
         n\bno\bot\bt a\bau\but\bth\bho\bor\bri\bit\bta\bat\bti\biv\bve\be;\b;
 
        The  DHCP  server will normally assume that the configura­
-       tion information about a given network segment is known to
-       be correct and is authoritative.   So if a client requests
-       an IP address on a given network segment that  the  server
-       knows  is  not  valid  for  that  segment, the server will
-       respond with a DHCPNAK message, causing the client to for­
-       get its IP address and try to get a new one.
-
-       If  a  DHCP  server is being configured by somebody who is
-       not the network administrator and who therefore  does  not
-       wish to assert this level of authority, then the statement
-       "not authoritative" should be written in  the  appropriate
-       scope in the configuration file.
-
-       Usually,  writing  n\bno\bot\bt  a\bau\but\bth\bho\bor\bri\bit\bta\bat\bti\biv\bve\be;\b; at the top level of
-       the file should be sufficient.   However, if a DHCP server
-       is  to  be set up so that it is aware of some networks for
-       which it is authoritative and some networks for  which  it
+       tion information about a  given  network  segment  is  not
+       known  to be correct and is not authoritative.  This is so
+       that if a naive user installs  a  DHCP  server  not  fully
+       understanding how to configure it, it does not send spuri­
+       ous  DHCPNAK  messages  to  clients  that  have   obtained
+       addresses from a legitimate DHCP server on the network.
+
+       Network   administrators  setting  up  authoritative  DHCP
+       servers for their networks should always write  a\bau\but\bth\bho\bor\bri\bit\bta\ba­\b­
+       t\bti\biv\bve\be;\b;  at  the top of their configuration file to indicate
+       that the DHCP server _\bs_\bh_\bo_\bu_\bl_\bd send DHCPNAK messages to  mis­
+       configured clients.   If this is not done, clients will be
+       unable to get a correct IP address after changing  subnets
+       until  their old lease has expired, which could take quite
+       a long time.
+
+       Usually, writing a\bau\but\bth\bho\bor\bri\bit\bta\bat\bti\biv\bve\be;\b; at the top  level  of  the
+       file  should be sufficient.   However, if a DHCP server is
+       to be set up so that it is  aware  of  some  networks  for
+       which  it  is authoritative and some networks for which it
        is not, it may be more appropriate to declare authority on
        a per-network-segment basis.
 
        Note that the most specific scope for which the concept of
-       authority  makes any sense is the physical network segment
-       - either a shared-network statement or a subnet  statement
-       that  is  not contained within a shared-network statement.
+       authority makes any sense is the physical network  segment
+       -  either a shared-network statement or a subnet statement
+       that is not contained within a  shared-network  statement.
        It is not meaningful to specify that the server is author­
-       itative  for some subnets within a shared network, but not
-       authoritative for others, nor is it meaningful to  specify
-       that  the  server  is authoritative for some host declara­
+       itative for some subnets within a shared network, but  not
+       authoritative  for others, nor is it meaningful to specify
+       that the server is authoritative for  some  host  declara­
        tions and not others.
 
-       T\bTh\bhe\be _\ba_\bl_\bw_\ba_\by_\bs_\b-_\br_\be_\bp_\bl_\by_\b-_\br_\bf_\bc_\b1_\b0_\b4_\b8 s\bst\bta\bat\bte\bem\bme\ben\bnt\bt
-
-
 
 
                                                                23
@@ -1522,36 +1522,38 @@ dhcpd.conf(5)                                       dhcpd.conf(5)
 dhcpd.conf(5)                                       dhcpd.conf(5)
 
 
+       T\bTh\bhe\be _\ba_\bl_\bw_\ba_\by_\bs_\b-_\br_\be_\bp_\bl_\by_\b-_\br_\bf_\bc_\b1_\b0_\b4_\b8 s\bst\bta\bat\bte\bem\bme\ben\bnt\bt
+
         a\bal\blw\bwa\bay\bys\bs-\b-r\bre\bep\bpl\bly\by-\b-r\brf\bfc\bc1\b10\b04\b48\b8 _\bf_\bl_\ba_\bg;\b;
 
-       Some BOOTP clients expect RFC1048-style responses, but  do
-       not  follow RFC1048 when sending their requests.   You can
-       tell that a client is having this problem  if  it  is  not
-       getting  the options you have configured for it and if you
-       see in the server log the message "(non-rfc1048)"  printed
+       Some  BOOTP clients expect RFC1048-style responses, but do
+       not follow RFC1048 when sending their requests.   You  can
+       tell  that  a  client  is having this problem if it is not
+       getting the options you have configured for it and if  you
+       see  in the server log the message "(non-rfc1048)" printed
        with each BOOTREQUEST that is logged.
 
-       If  you want to send rfc1048 options to such a client, you
-       can set the a\bal\blw\bwa\bay\bys\bs-\b-r\bre\bep\bpl\bly\by-\b-r\brf\bfc\bc1\b10\b04\b48\b8 option in  that  client's
+       If you want to send rfc1048 options to such a client,  you
+       can  set  the a\bal\blw\bwa\bay\bys\bs-\b-r\bre\bep\bpl\bly\by-\b-r\brf\bfc\bc1\b10\b04\b48\b8 option in that client's
        host declaration, and the DHCP server will respond with an
-       RFC-1048-style vendor options field.   This  flag  can  be
-       set  in  any scope, and will affect all clients covered by
+       RFC-1048-style  vendor  options  field.   This flag can be
+       set in any scope, and will affect all clients  covered  by
        that scope.
 
        T\bTh\bhe\be _\ba_\bl_\bw_\ba_\by_\bs_\b-_\bb_\br_\bo_\ba_\bd_\bc_\ba_\bs_\bt s\bst\bta\bat\bte\bem\bme\ben\bnt\bt
 
         a\bal\blw\bwa\bay\bys\bs-\b-b\bbr\bro\boa\bad\bdc\bca\bas\bst\bt _\bf_\bl_\ba_\bg;\b;
 
-       The DHCP and BOOTP protocols both require DHCP  and  BOOTP
+       The  DHCP  and BOOTP protocols both require DHCP and BOOTP
        clients to set the broadcast bit in the flags field of the
-       BOOTP message header.  Unfortunately, some DHCP and  BOOTP
-       clients  do  not  do  this,  and therefore may not receive
-       responses from the DHCP server.   The DHCP server  can  be
-       made  to always broadcast its responses to clients by set­
-       ting this flag to 'on' for the relevant scope.   To  avoid
+       BOOTP  message header.  Unfortunately, some DHCP and BOOTP
+       clients do not do this,  and  therefore  may  not  receive
+       responses  from  the DHCP server.   The DHCP server can be
+       made to always broadcast its responses to clients by  set­
+       ting  this flag to 'on' for the relevant scope.   To avoid
        creating excess broadcast traffic on your network, we rec­
-       ommend that you restrict the use of this option to as  few
-       clients  as  possible.    For  example, the Microsoft DHCP
+       ommend  that you restrict the use of this option to as few
+       clients as possible.   For  example,  the  Microsoft  DHCP
        client is known not to have this problem, as are the Open­
        Transport and ISC DHCP clients.
 
@@ -1560,13 +1562,13 @@ dhcpd.conf(5)                                       dhcpd.conf(5)
         o\bon\bne\be-\b-l\ble\bea\bas\bse\be-\b-p\bpe\ber\br-\b-c\bcl\bli\bie\ben\bnt\bt _\bf_\bl_\ba_\bg;\b;
 
        If this flag is enabled, whenever a client sends a DHCPRE­
-       QUEST for a particular lease, the  server  will  automati­
-       cally  free any other leases the client holds.   This pre­
-       sumes that when the client sends  a  DHCPREQUEST,  it  has
-       forgotten  any  lease  not  mentioned in the DHCPREQUEST -
-       i.e., the client has only a single network  interface  _\ba_\bn_\bd
-       it  does  not  remember leases it's holding on networks to
-       which it is not currently  attached.    Neither  of  these
+       QUEST  for  a  particular lease, the server will automati­
+       cally free any other leases the client holds.   This  pre­
+       sumes  that  when  the  client sends a DHCPREQUEST, it has
+       forgotten any lease not mentioned  in  the  DHCPREQUEST  -
+       i.e.,  the  client has only a single network interface _\ba_\bn_\bd
+       it does not remember leases it's holding  on  networks  to
+       which  it  is  not  currently attached.   Neither of these
        assumptions are guaranteed or provable, so we urge caution
        in the use of this statement.
 
@@ -1574,8 +1576,6 @@ dhcpd.conf(5)                                       dhcpd.conf(5)
 
         u\bus\bse\be-\b-l\ble\bea\bas\bse\be-\b-a\bad\bdd\bdr\br-\b-f\bfo\bor\br-\b-d\bde\bef\bfa\bau\bul\blt\bt-\b-r\bro\bou\but\bte\be _\bf_\bl_\ba_\bg;\b;
 
-       If the _\bu_\bs_\be_\b-_\bl_\be_\ba_\bs_\be_\b-_\ba_\bd_\bd_\br_\b-_\bf_\bo_\br_\b-_\bd_\be_\bf_\ba_\bu_\bl_\bt_\b-_\br_\bo_\bu_\bt_\be parameter is  true
-       in  a  given  scope,  then  instead  of  sending the value
 
 
 
@@ -1588,36 +1588,38 @@ dhcpd.conf(5)                                       dhcpd.conf(5)
 dhcpd.conf(5)                                       dhcpd.conf(5)
 
 
-       specified in the routers option (or sending  no  value  at
-       all),  the  IP address of the lease being assigned is sent
-       to the client.   This supposedly causes Win95 machines  to
-       ARP  for  all  IP  addresses, which can be helpful if your
-       router is configured for proxy ARP.
+       If  the _\bu_\bs_\be_\b-_\bl_\be_\ba_\bs_\be_\b-_\ba_\bd_\bd_\br_\b-_\bf_\bo_\br_\b-_\bd_\be_\bf_\ba_\bu_\bl_\bt_\b-_\br_\bo_\bu_\bt_\be parameter is true
+       in a given scope, then instead of sending the value speci­
+       fied  in  the routers option (or sending no value at all),
+       the IP address of the lease being assigned is sent to  the
+       client.   This supposedly causes Win95 machines to ARP for
+       all IP addresses, which can be helpful if your  router  is
+       configured for proxy ARP.
 
        T\bTh\bhe\be _\bs_\be_\br_\bv_\be_\br_\b-_\bi_\bd_\be_\bn_\bt_\bi_\bf_\bi_\be_\br s\bst\bta\bat\bte\bem\bme\ben\bnt\bt
 
         s\bse\ber\brv\bve\ber\br-\b-i\bid\bde\ben\bnt\bti\bif\bfi\bie\ber\br _\bh_\bo_\bs_\bt_\bn_\ba_\bm_\be;\b;
 
-       The server-identifier statement can be used to define  the
-       value  that  is  sent in the DHCP Server Identifier option
-       for a given scope.   The value specified  m\bmu\bus\bst\bt  be  an  IP
-       address  for the DHCP server, and must be reachable by all
+       The  server-identifier statement can be used to define the
+       value that is sent in the DHCP  Server  Identifier  option
+       for  a  given  scope.    The value specified m\bmu\bus\bst\bt be an IP
+       address for the DHCP server, and must be reachable by  all
        clients served by a particular scope.
 
-       The use of the server-identifier statement is  not  recom­
-       mended  -  the  only  reason to use it is to force a value
+       The  use  of the server-identifier statement is not recom­
+       mended - the only reason to use it is  to  force  a  value
        other than the default value to be sent on occasions where
-       the  default value would be incorrect.   The default value
-       is the first IP address associated with the physical  net­
+       the default value would be incorrect.   The default  value
+       is  the first IP address associated with the physical net­
        work interface on which the request arrived.
 
        The usual case where the _\bs_\be_\br_\bv_\be_\br_\b-_\bi_\bd_\be_\bn_\bt_\bi_\bf_\bi_\be_\br statement needs
-       to be sent is when a physical interface has more than  one
+       to  be sent is when a physical interface has more than one
        IP address, and the one being sent by default isn't appro­
-       priate for some or all clients served by  that  interface.
-       Another  common  case  is when an alias is defined for the
-       purpose of having a consistent IP  address  for  the  DHCP
-       server,  and  it  is  desired that the clients use this IP
+       priate  for  some or all clients served by that interface.
+       Another common case is when an alias is  defined  for  the
+       purpose  of  having  a  consistent IP address for the DHCP
+       server, and it is desired that the  clients  use  this  IP
        address when contacting the server.
 
        Supplying a value for the dhcp-server-identifier option is
@@ -1627,21 +1629,19 @@ dhcpd.conf(5)                                       dhcpd.conf(5)
 
         d\bdd\bdn\bns\bs-\b-u\bup\bpd\bda\bat\bte\bes\bs _\bf_\bl_\ba_\bg;\b;
 
-       The  _\bd_\bd_\bn_\bs_\b-_\bu_\bp_\bd_\ba_\bt_\be_\bs  parameter  controls  whether or not the
-       server will attempt to do a ddns update when  a  lease  is
-       confirmed.    Set  this  to  _\bo_\bf_\bf  if the server should not
-       attempt to do updates within a certain scope.   The  _\bd_\bd_\bn_\bs_\b-
+       The _\bd_\bd_\bn_\bs_\b-_\bu_\bp_\bd_\ba_\bt_\be_\bs parameter controls  whether  or  not  the
+       server  will  attempt  to do a ddns update when a lease is
+       confirmed.   Set this to _\bo_\bf_\bf  if  the  server  should  not
+       attempt  to  do updates within a certain scope.  The _\bd_\bd_\bn_\bs_\b-
        _\bu_\bp_\bd_\ba_\bt_\be_\bs parameter is on by default.
 
 R\bRE\bEF\bFE\bER\bRE\bEN\bNC\bCE\bE:\b: O\bOP\bPT\bTI\bIO\bON\bN S\bST\bTA\bAT\bTE\bEM\bME\bEN\bNT\bTS\bS
-       DHCP   option  statements  are  documented  in  the  d\bdh\bhc\bcp\bp-\b-
+       DHCP  option  statements  are  documented  in  the   d\bdh\bhc\bcp\bp-\b-
        o\bop\bpt\bti\bio\bon\bns\bs(\b(5\b5)\b) manual page.
 
 S\bSE\bEE\bE A\bAL\bLS\bSO\bO
        dhcpd.conf(5), dhcpd.leases(5), RFC2132, RFC2131.
 
-A\bAU\bUT\bTH\bHO\bOR\bR
-       d\bdh\bhc\bcp\bpd\bd(\b(8\b8)\b) was written by Ted Lemon <mellon@vix.com> under a
 
 
 
@@ -1654,9 +1654,11 @@ A\bAU\bUT\bTH\bHO\bOR\bR
 dhcpd.conf(5)                                       dhcpd.conf(5)
 
 
-       contract  with  Vixie Labs.   Funding for this project was
+A\bAU\bUT\bTH\bHO\bOR\bR
+       d\bdh\bhc\bcp\bpd\bd(\b(8\b8)\b) was written by Ted Lemon <mellon@vix.com> under a
+       contract with Vixie Labs.   Funding for this  project  was
        provided by the Internet Software Consortium.  Information
-       about  the  Internet  Software  Consortium can be found at
+       about the Internet Software Consortium  can  be  found  at
        h\bht\btt\btp\bp:\b:/\b//\b/w\bww\bww\bw.\b.i\bis\bsc\bc.\b.o\bor\brg\bg/\b/i\bis\bsc\bc.\b.
 
 
@@ -1706,8 +1708,6 @@ dhcpd.conf(5)                                       dhcpd.conf(5)
 
 
 
-
-
 
 
 
index 1f8627b689239b0bac1a1a9b4a9daf9fe127ac55..07539b8de2f059e518712ece61dc6f9b902d511b 100644 (file)
@@ -43,7 +43,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: failover.c,v 1.15 2000/05/17 16:04:26 mellon Exp $ Copyright (c) 1999-2000 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: failover.c,v 1.16 2000/06/02 21:27:17 mellon Exp $ Copyright (c) 1999-2000 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -383,6 +383,8 @@ isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
                   a state object. */
                /* XXX this should be authenticated! */
                if (link -> imsg -> type == FTM_CONNECT) {
+                   const char *errmsg;
+                   int reason;
                    /* See if we can find a failover_state object that
                       matches this connection.  This message should only
                       be received by a secondary from a primary. */
@@ -396,10 +398,13 @@ isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
                    /* If we can't find a failover protocol state
                       for this remote host, drop the connection */
                    if (!state) {
+                           errmsg = "unknown server";
+                           reason = FTR_INVALID_PARTNER;
+
+                         badconnect:
                                /* XXX Send a refusal message first?
                                   XXX Look in protocol spec for guidance. */
-                           log_error ("Failover CONNECT from %s %d.%d.%d.%d",
-                                      "unknown server",
+                           log_error ("Failover CONNECT from %d.%d.%d.%d: %s",
                                       ((u_int8_t *)
                                        (&link -> imsg -> server_addr)) [0],
                                       ((u_int8_t *)
@@ -407,15 +412,32 @@ isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
                                       ((u_int8_t *)
                                        (&link -> imsg -> server_addr)) [2],
                                       ((u_int8_t *)
-                                       (&link -> imsg -> server_addr)) [3]);
+                                       (&link -> imsg -> server_addr)) [3],
+                                      errmsg);
                            dhcp_failover_send_connectack
-                                   ((omapi_object_t *)link,
-                                    FTR_INVALID_PARTNER);
+                                   ((omapi_object_t *)link, state,
+                                    reason, errmsg);
                            omapi_disconnect (c, 0);
                            link -> state = dhcp_flink_disconnected;
                            return ISC_R_SUCCESS;
                    }
 
+                   if (!(link -> imsg -> options_present & FTB_HBA) ||
+                       link -> imsg -> hba.count != 32) {
+                           errmsg = "invalid HBA";
+                           reason = FTR_HBA_CONFLICT; /* XXX */
+                           goto badconnect;
+                   }
+                   if (state -> hba)
+                           dfree (state -> hba, MDL);
+                   state -> hba = dmalloc (32, MDL);
+                   if (!state -> hba) {
+                           errmsg = "no memory";
+                           reason = FTR_MISC_REJECT;
+                           goto badconnect;
+                   }
+                   memcpy (state -> hba, link -> imsg -> hba.data, 32);
+
                    if (!link -> state_object)
                            dhcp_failover_state_reference
                                    (&link -> state_object, state, MDL);
@@ -567,7 +589,7 @@ static isc_result_t do_a_failover_option (c, link)
                    ft_options [option_code].type == FT_DDNS1) {
                        ddns_fqdn_t *ddns =
                                ((ddns_fqdn_t *)
-                                (((char *)&link -> imsg) +
+                                (((char *)link -> imsg) +
                                  ft_options [option_code].offset));
 
                        op_count = (ft_options [option_code].type == FT_DDNS1
@@ -613,7 +635,7 @@ static isc_result_t do_a_failover_option (c, link)
                op_count = option_len / op_size;
                
                fo = ((failover_option_t *)
-                     (((char *)&link -> imsg) +
+                     (((char *)link -> imsg) +
                       ft_options [option_code].offset));
 
                fo -> count = op_count;
@@ -1052,7 +1074,7 @@ isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
                        dhcp_failover_link_reference (&state -> link_to_peer,
                                                      link, MDL);
                        status = (dhcp_failover_send_connectack
-                                 ((omapi_object_t *)link, 0));
+                                 ((omapi_object_t *)link, state, 0, 0));
                        if (status != ISC_R_SUCCESS) {
                                dhcp_failover_link_dereference
                                        (&state -> link_to_peer, MDL);
@@ -1061,8 +1083,10 @@ isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
                        }
                        dhcp_failover_state_transition (state, "connect");
                } else if (link -> imsg -> type == FTM_CONNECTACK) {
+                   const char *errmsg;
+                   int reason;
                    if (link -> imsg -> reject_reason) {
-                       log_error ("Failover CONNECTACK from %d.%d.%d.%d%s%s",
+                       log_error ("Failover CONNECT to %d.%d.%d.%d%s%s",
                                   ((u_int8_t *)
                                    (&link -> imsg -> server_addr)) [0],
                                   ((u_int8_t *)
@@ -1074,6 +1098,7 @@ isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
                                   " rejected: ",
                                   (dhcp_failover_reject_reason_print
                                    (link -> imsg -> reject_reason)));
+                       /* XXX print message from peer if peer sent message. */
                        omapi_disconnect (link -> outer, 1);
                        return ISC_R_SUCCESS;
                    }
@@ -1082,8 +1107,10 @@ isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
                        (state,
                         (u_int8_t *)&link -> imsg -> server_addr,
                         sizeof link -> imsg -> server_addr)) {
-                       log_error ("Failover CONNECTACK from %s %d.%d.%d.%d",
-                                  "unknown server",
+                       errmsg = "unknown server";
+                       reason = FTR_INVALID_PARTNER;
+                     badconnectack:
+                       log_error ("Failover CONNECTACK from %d.%d.%d.%d: %s",
                                   ((u_int8_t *)
                                    (&link -> imsg -> server_addr)) [0],
                                   ((u_int8_t *)
@@ -1091,27 +1118,19 @@ isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
                                   ((u_int8_t *)
                                    (&link -> imsg -> server_addr)) [2],
                                   ((u_int8_t *)
-                                   (&link -> imsg -> server_addr)) [3]);
+                                   (&link -> imsg -> server_addr)) [3],
+                                  errmsg);
                        dhcp_failover_send_disconnect ((omapi_object_t *)link,
-                                                      FTR_INVALID_PARTNER, 0);
+                                                      reason, errmsg);
                        omapi_disconnect (link -> outer, 0);
                    }
 
                    if (state -> link_to_peer) {
-                       log_error ("Failover CONNECTACK %s %d.%d.%d.%d",
-                                  "while already connected",
-                                  ((u_int8_t *)
-                                   (&link -> imsg -> server_addr)) [0],
-                                  ((u_int8_t *)
-                                   (&link -> imsg -> server_addr)) [1],
-                                  ((u_int8_t *)
-                                   (&link -> imsg -> server_addr)) [2],
-                                  ((u_int8_t *)
-                                   (&link -> imsg -> server_addr)) [3]);
-                       dhcp_failover_send_disconnect ((omapi_object_t *)link,
-                                                      FTR_DUP_CONNECTION, 0);
-                       omapi_disconnect (link -> outer, 0);
+                       errmsg = "already connected";
+                       reason = FTR_DUP_CONNECTION;
+                       goto badconnectack;
                    }
+
                    dhcp_failover_link_reference (&state -> link_to_peer,
                                                  link, MDL);
                    dhcp_failover_state_transition (state, "connect");
@@ -1331,9 +1350,14 @@ int dhcp_failover_pool_rebalance (dhcp_failover_state_t *state)
 {
        int lts;
        int leases_queued = 0;
-       struct lease *lp;
+       struct lease *lp = (struct lease *)0;
+       struct lease *next = (struct lease *)0;
        struct shared_network *s;
        struct pool *p;
+       int polarity;
+       binding_state_t peer_lease_state;
+       binding_state_t my_lease_state;
+       struct lease **lq;
 
        if (state -> my_state != normal || state -> i_am == secondary)
                return 0;
@@ -1342,32 +1366,59 @@ int dhcp_failover_pool_rebalance (dhcp_failover_state_t *state)
            for (p = s -> pools; p; p = p -> next) {
                if (p -> failover_peer != state)
                    continue;
-               log_info ("pool %lx total %d  local free %d  peer free %d",
+               log_info ("pool %lx total %d  free %d  backup %d",
                          (unsigned long)p, p -> lease_count,
-                         p -> local_leases, p -> peer_leases);
+                         p -> free_leases, p -> backup_leases);
+
+               /* Right now we're giving the peer half of the free leases.
+                  If we have more leases than the peer (i.e., more than
+                  half), then the number of leases we have, less the number
+                  of leases the peer has, will be how many more leases we
+                  have than the peer has.   So if we send half that number
+                  to the peer, we should be even. */
+               if (p -> failover_peer -> i_am == primary) {
+                       lts = (p -> free_leases - p -> backup_leases) / 2;
+                       peer_lease_state = FTS_BACKUP;
+                       my_lease_state = FTS_FREE;
+                       lq = &p -> free;
+               } else {
+                       lts = (p -> backup_leases - p -> free_leases) / 2;
+                       peer_lease_state = FTS_FREE;
+                       my_lease_state = FTS_BACKUP;
+                       lq = &p -> backup;
+               }
 
-               lts = ((p -> local_leases +
-                       p -> peer_leases) / 2) - p -> peer_leases;
                if (lts > 1) {
-                   struct lease lt;
-
-                   leases_queued += lts;
-                   for (lp = p -> last_lease; lp && lts;
-                        lp = lp -> prev) {
-                       if (!(lp -> flags & PEER_IS_OWNER)) {
-                               lp -> flags |= PEER_IS_OWNER;
-                               lp -> tstp = cur_time;
-                           if (!write_lease (lp) ||
-                               !commit_leases () ||
-                               !dhcp_failover_queue_update (lp)) {
-                                   log_info ("%s lease %s on giveaway",
-                                             "unable to commit",
-                                             piaddr (lp -> ip_addr));
-                           }
+                   lease_reference (&lp, *lq, MDL);
+
+                   while (lp && lts) {
+                       /* Remember the next lease in the list. */
+                       if (next)
+                           lease_dereference (&next, MDL);
+                       if (lp -> next)
+                           lease_reference (&next, lp -> next, MDL);
+
+                       --lts;
+                       ++leases_queued;
+                       lp -> next_binding_state = peer_lease_state;
+                       lp -> tstp = cur_time;
+                       lp -> starts = cur_time;
+                       if (!supersede_lease (lp, (struct lease *)0, 1, 1, 0))
+                       {
+                           log_info ("can't commit lease %s on giveaway",
+                                     piaddr (lp -> ip_addr));
                        }
+
+                       lease_dereference (&lp, MDL);
+                       if (next)
+                               lease_reference (&lp, next, MDL);
                    }
-               }
+                   if (next)
+                       lease_dereference (&next, MDL);
+                   if (lp)
+                       lease_dereference (&lp, MDL);
 
+               }
                if (lts > 1) {
                        log_info ("lease imbalance - lts = %d", lts);
                        leases_queued -= lts;
@@ -1375,6 +1426,7 @@ int dhcp_failover_pool_rebalance (dhcp_failover_state_t *state)
            }
        }
        dhcp_failover_send_poolresp (state, leases_queued);
+       dhcp_failover_send_updates (state);
        return leases_queued;
 }
 
@@ -1388,12 +1440,15 @@ int dhcp_failover_pool_check (struct pool *pool)
            pool -> failover_peer -> my_state != normal)
                return 0;
 
-       log_info ("pool %lx total %d  local free %d  peer free %d",
+       if (pool -> failover_peer -> i_am == primary)
+               lts = (pool -> backup_leases - pool -> free_leases) / 2;
+       else
+               lts = (pool -> free_leases - pool -> backup_leases) / 2;
+
+       log_info ("pool %lx total %d  free %d  backup %d  lts %d",
                  (unsigned long)pool, pool -> lease_count,
-                 pool -> local_leases, pool -> peer_leases);
+                 pool -> free_leases, pool -> backup_leases, lts);
 
-       lts = ((pool -> local_leases +
-               pool -> peer_leases) / 2) - pool -> local_leases;
        if (lts > 1) {
                /* XXX What about multiple pools? */
                dhcp_failover_send_poolreq (pool -> failover_peer);
@@ -1477,7 +1532,7 @@ isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *state)
    not an error for this to be called on a lease for which there's no
    failover peer. */
 
-int dhcp_failover_queue_update (struct lease *lease)
+int dhcp_failover_queue_update (struct lease *lease, int immediate)
 {
        dhcp_failover_state_t *state;
 
@@ -1509,7 +1564,8 @@ int dhcp_failover_queue_update (struct lease *lease)
        }
        lease_reference (&state -> update_queue_tail, lease, MDL);
        lease -> flags |= ON_UPDATE_QUEUE;
-       dhcp_failover_send_updates (state);
+       if (immediate)
+               dhcp_failover_send_updates (state);
        return 1;
 }
 
@@ -1530,7 +1586,7 @@ void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *state,
                return;
        }
        for (lp = state -> ack_queue_head;
-            lp -> next_pending != lease; lp = lp -> next_pending)
+            lp && lp -> next_pending != lease; lp = lp -> next_pending)
                ;
        if (lp) {
                lease_dereference (&lp -> next_pending, MDL);
@@ -2140,7 +2196,8 @@ failover_option_t *dhcp_failover_option_printf (unsigned code,
                                                char *obuf,
                                                unsigned *obufix,
                                                unsigned obufmax,
-                                               const char *fmt, ...) {
+                                               const char *fmt, ...)
+{
        va_list va;
        char tbuf [256];
 
@@ -2472,6 +2529,7 @@ isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
        dhcp_failover_link_t *link;
        dhcp_failover_state_t *state;
        isc_result_t status;
+       char hba [32];
 #if defined (DEBUG_FAILOVER_MESSAGES)  
        char obuf [64];
        unsigned obufix = 0;
@@ -2489,6 +2547,12 @@ isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
        if (!l -> outer || l -> outer -> type != omapi_type_connection)
                return ISC_R_INVALIDARG;
 
+       if (state -> hba) {
+               int i;
+               for (i = 0; i < 32; i++)
+                       hba [i] = ~state -> hba [i];
+       }
+
        status = (dhcp_failover_put_message
                  (link, l -> outer,
                   FTM_CONNECT,
@@ -2507,7 +2571,9 @@ isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
                                              0, 0),
                   dhcp_failover_make_option (FTO_MCLT, FMA,
                                              state -> mclt),
-                  dhcp_failover_make_option (FTO_HBA, FMA, 32, state -> hba),
+                  (state -> hba
+                   ? dhcp_failover_make_option (FTO_HBA, FMA, 32, hba)
+                   : &skip_failover_option),
                   (failover_option_t *)0));
 
 #if defined (DEBUG_FAILOVER_MESSAGES)
@@ -2521,10 +2587,11 @@ isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
        return status;
 }
 
-isc_result_t dhcp_failover_send_connectack (omapi_object_t *l, int reason)
+isc_result_t dhcp_failover_send_connectack (omapi_object_t *l,
+                                           dhcp_failover_state_t *state,
+                                           int reason, const char *errmsg)
 {
        dhcp_failover_link_t *link;
-       dhcp_failover_state_t *state;
        isc_result_t status;
 #if defined (DEBUG_FAILOVER_MESSAGES)  
        char obuf [64];
@@ -2539,20 +2606,26 @@ isc_result_t dhcp_failover_send_connectack (omapi_object_t *l, int reason)
        if (!l || l -> type != dhcp_type_failover_link)
                return ISC_R_INVALIDARG;
        link = (dhcp_failover_link_t *)l;
-       state = link -> state_object;
        if (!l -> outer || l -> outer -> type != omapi_type_connection)
                return ISC_R_INVALIDARG;
 
        status = (dhcp_failover_put_message
                  (link, l -> outer,
                   FTM_CONNECTACK,
-                  dhcp_failover_make_option (FTO_SERVER_ADDR, FMA,
-                                             state -> server_identifier.len,
-                                             state -> server_identifier.data),
-                  dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
-                                             state -> max_flying_updates),
-                  dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
-                                             state -> max_response_delay),
+                  (state
+                   ? (dhcp_failover_make_option
+                      (FTO_SERVER_ADDR, FMA,
+                       state -> server_identifier.len,
+                       state -> server_identifier.data))
+                   : &skip_failover_option),
+                  (state
+                   ? dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
+                                                state -> max_flying_updates)
+                   : &skip_failover_option),
+                  (state
+                   ? dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
+                                                state -> max_response_delay)
+                   : &skip_failover_option),
                   dhcp_failover_option_printf (FTO_VENDOR_CLASS, FMA,
                                                "isc-%s", DHCP_VERSION),
                   dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
@@ -2563,7 +2636,11 @@ isc_result_t dhcp_failover_send_connectack (omapi_object_t *l, int reason)
                    ? dhcp_failover_make_option (FTO_REJECT_REASON,
                                                 FMA, reason)
                    : &skip_failover_option),
-                   (failover_option_t *)0));
+                  (errmsg
+                   ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
+                                                strlen (errmsg), errmsg)
+                   : &skip_failover_option),
+                  (failover_option_t *)0));
 
 #if defined (DEBUG_FAILOVER_MESSAGES)
        if (status != ISC_R_SUCCESS)
@@ -2635,7 +2712,6 @@ isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
 #if defined (DEBUG_FAILOVER_MESSAGES)  
        char obuf [64];
        unsigned obufix = 0;
-       int binding_status;
        
 # define FMA obuf, &obufix, sizeof obuf
        failover_print (FMA, "(bndupd");
@@ -2651,26 +2727,6 @@ isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
        if (!link -> outer || link -> outer -> type != omapi_type_connection)
                return ISC_R_INVALIDARG;
 
-       /* Kludge up the binding status. */
-       if (lease -> flags & ABANDONED)
-               binding_status = FTS_ABANDONED;
-       else if (lease -> tsfp <= cur_time) {
-               if (lease -> flags & PEER_IS_OWNER) {
-                       if (state -> i_am == primary)
-                               binding_status = FTS_BACKUP;
-                       else
-                               binding_status = FTS_FREE;
-               } else {
-                       if (state -> i_am == primary)
-                               binding_status = FTS_FREE;
-                       else
-                               binding_status = FTS_BACKUP;
-               }
-       } else if (lease -> ends <= cur_time) {
-               binding_status = FTS_EXPIRED;
-       } else
-               binding_status = FTS_ACTIVE;
-
        /* Send the update. */
        status = (dhcp_failover_put_message
                  (link, link -> outer,
@@ -2679,7 +2735,7 @@ isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
                                              lease -> ip_addr.len,
                                              lease -> ip_addr.iabuf),
                   dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
-                                             binding_status),
+                                             lease -> binding_state),
                   lease -> uid_len
                   ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
                                                lease -> uid_len,
@@ -2726,7 +2782,6 @@ isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state,
 #if defined (DEBUG_FAILOVER_MESSAGES)  
        char obuf [64];
        unsigned obufix = 0;
-       int binding_status;
        
 # define FMA obuf, &obufix, sizeof obuf
        failover_print (FMA, "(bndack");
@@ -2742,26 +2797,6 @@ isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state,
        if (!link -> outer || link -> outer -> type != omapi_type_connection)
                return ISC_R_INVALIDARG;
 
-       /* Kludge up the binding status. */
-       if (lease -> flags & ABANDONED)
-               binding_status = FTS_ABANDONED;
-       else if (lease -> tsfp <= cur_time) {
-               if (lease -> flags & PEER_IS_OWNER) {
-                       if (state -> i_am == primary)
-                               binding_status = FTS_BACKUP;
-                       else
-                               binding_status = FTS_FREE;
-               } else {
-                       if (state -> i_am == primary)
-                               binding_status = FTS_FREE;
-                       else
-                               binding_status = FTS_BACKUP;
-               }
-       } else if (lease -> ends <= cur_time) {
-               binding_status = FTS_EXPIRED;
-       } else
-               binding_status = FTS_ACTIVE;
-
        if (!message && reason)
                message = dhcp_failover_reject_reason_print (reason);
 
@@ -2773,7 +2808,7 @@ isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state,
                                              lease -> ip_addr.len,
                                              lease -> ip_addr.iabuf),
                   dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
-                                             binding_status),
+                                             lease -> binding_state),
                   lease -> uid_len
                   ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
                                                lease -> uid_len,
@@ -2864,7 +2899,7 @@ isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *state,
        unsigned obufix = 0;
        
 # define FMA obuf, &obufix, sizeof obuf
-       failover_print (FMA, "(poolreq");
+       failover_print (FMA, "(poolresp");
 #else
 # define FMA (unsigned char *)0, (unsigned *)0, 0
 #endif
@@ -2902,6 +2937,7 @@ isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state,
        struct iaddr ia;
        int reason = FTR_MISC_REJECT;
        const char *message;
+       int new_binding_state;
 
        ia.len = sizeof msg -> assigned_addr;
        memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
@@ -2964,21 +3000,20 @@ isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state,
        }
 
        if (msg -> options_present & FTB_BINDING_STATUS) {
-               if (state -> i_am == primary) {
-                       if (msg -> binding_status == FTS_BACKUP)
-                               lease -> flags |= PEER_IS_OWNER;
-                       else if (msg -> binding_status == FTS_FREE)
-                               lease -> flags &= ~PEER_IS_OWNER;
-               } else {
-                       if (msg -> binding_status == FTS_BACKUP)
-                               lease -> flags &= PEER_IS_OWNER;
-                       else if (msg -> binding_status == FTS_FREE)
-                               lease -> flags |= ~PEER_IS_OWNER;
+               /* Check the requested transition to make sure it's
+                  valid. */
+               new_binding_state = (binding_state_transition_check
+                                    (lease, state, msg -> binding_status));
+               if (new_binding_state != msg -> binding_status) {
+                       dhcp_failover_send_bind_ack
+                               (state, lease, msg, FTR_FATAL_CONFLICT,
+                                "invalid binding state transition");
                }
+               lt -> next_binding_state = new_binding_state;
        }
 
        /* Try to install the new information. */
-       if (!supersede_lease (lease, lt, 1, 0)) {
+       if (!supersede_lease (lease, lt, 1, 0, 0)) {
                message = "database update failed";
              bad:
                dhcp_failover_send_bind_ack (state, lease, msg,
@@ -3022,7 +3057,7 @@ isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state,
        }
 
        /* Try to install the new information. */
-       supersede_lease (lease, lt, 1, 0);
+       supersede_lease (lease, lt, 1, 0, 0);
 
        state -> cur_unacked_updates--;
        dhcp_failover_ack_queue_remove (state, lease);
@@ -3120,6 +3155,11 @@ int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
                return 1;
        }
 
+       /* If we don't have a hash bucket array, we can't tell if this
+          one's ours, so we assume it's not. */
+       if (!state -> hba)
+               return 0;
+
        oc = lookup_option (&dhcp_universe, packet -> options,
                            DHO_DHCP_CLIENT_IDENTIFIER);
        memset (&ds, 0, sizeof ds);
@@ -3139,6 +3179,195 @@ int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
                return !hm;
 }
 
+binding_state_t binding_state_transition_check (struct lease *lease,
+                                               dhcp_failover_state_t *state,
+                                               binding_state_t binding_state)
+{
+       /* If there is no transition, it's no problem. */
+       if (binding_state == lease -> binding_state)
+               return binding_state;
+
+       /* This is really only dealing with what to do with bind updates when
+          we're in the normal state - if we were down and came back, and the
+          peer is in partner_down state, then we should take whatever it
+          sends, as long as it hasn't done anything illegal.   What about
+          when we're in potential_conflict? */
+       /* Also, we should sanity check things here - the partner shouldn't
+          be allowed to set a lease to the EXPIRED state when it hasn't
+          expired, for example. */
+       /* Note that tsfp had better be set from the latest bind update
+          _before_ this function is called! */
+       switch (lease -> binding_state) {
+             case FTS_FREE:
+             case FTS_ABANDONED:
+               switch (binding_state) {
+                     case FTS_ACTIVE:
+                     case FTS_ABANDONED:
+                     case FTS_BACKUP:
+                     case FTS_RESERVED:
+                     case FTS_BOOTP:
+                       /* If the lease was free, and our peer is primary,
+                          then it can make it active, or abandoned, or
+                          backup.    Abandoned is treated like free in
+                          this case. */
+                       if (state -> i_am == secondary)
+                               return binding_state;
+
+                       /* Otherwise, it can't do any sort of state
+                          transition. */
+                     case FTS_FREE: /* for compiler */
+                     case FTS_EXPIRED:
+                     case FTS_RELEASED:
+                     case FTS_RESET:
+                       return FTS_FREE;
+               }
+             case FTS_ACTIVE:
+             case FTS_RESERVED:
+             case FTS_BOOTP:
+               /* The secondary can't change the state of an active
+                  lease. */
+               if (state -> i_am == primary)
+                       return FTS_ACTIVE;
+
+               /* So this is only for transitions made by the primary: */
+               switch (binding_state) {
+                     case FTS_FREE:
+                     case FTS_BACKUP:
+                       /* Can't set a lease to free or backup until the
+                          peer agrees that it's expired. */
+                       if (lease -> tsfp > cur_time)
+                               return FTS_ACTIVE;
+                       return binding_state;
+
+                     case FTS_EXPIRED:
+                       if (lease -> ends > cur_time)
+                               return lease -> binding_state;
+
+                     case FTS_RESERVED:
+                     case FTS_BOOTP:
+                     case FTS_RELEASED:
+                     case FTS_ABANDONED:
+                     case FTS_RESET:
+                     case FTS_ACTIVE:
+                       return binding_state;
+
+               }
+             case FTS_EXPIRED:
+               switch (binding_state) {
+                     case FTS_FREE:
+                     case FTS_BACKUP:
+                       /* Can't set a lease to free or backup until the
+                          peer agrees that it's expired. */
+                       if (lease -> tsfp > cur_time)
+                               return FTS_ACTIVE;
+                       return binding_state;
+
+                     case FTS_RESERVED:
+                     case FTS_BOOTP:
+                     case FTS_ACTIVE:
+                     case FTS_RELEASED:
+                     case FTS_ABANDONED:
+                     case FTS_RESET:
+                     case FTS_EXPIRED:
+                       return binding_state;
+               }
+             case FTS_RELEASED:
+               switch (binding_state) {
+                     case FTS_FREE:
+                     case FTS_BACKUP:
+                       /* Can't set a lease to free or backup until the
+                          peer agrees that it's expired. */
+                       if (lease -> tsfp > cur_time)
+                               return FTS_ACTIVE;
+                       return binding_state;
+
+                     case FTS_RESERVED:
+                     case FTS_BOOTP:
+                     case FTS_EXPIRED:
+                     case FTS_ABANDONED:
+                     case FTS_RESET:
+                     case FTS_ACTIVE:
+                     case FTS_RELEASED:
+                       return binding_state;
+               }
+             case FTS_RESET:
+               switch (binding_state) {
+                     case FTS_FREE:
+                     case FTS_BACKUP:
+                       /* Can't set a lease to free or backup until the
+                          peer agrees that it's expired. */
+                       if (lease -> tsfp > cur_time)
+                               return FTS_ACTIVE;
+                       return binding_state;
+
+                     case FTS_ACTIVE:
+                     case FTS_RESERVED:
+                     case FTS_BOOTP:
+                     case FTS_EXPIRED:
+                     case FTS_RELEASED:
+                     case FTS_ABANDONED:
+                     case FTS_RESET:
+                       return binding_state;
+               }
+             case FTS_BACKUP:
+               switch (binding_state) {
+                     case FTS_ACTIVE:
+                     case FTS_ABANDONED:
+                     case FTS_FREE:
+                     case FTS_RESERVED:
+                     case FTS_BOOTP:
+                       /* If the lease was in backup, and our peer is
+                          secondary, then it can make it active, or
+                          abandoned, or free. */
+                       if (state -> i_am == primary)
+                               return binding_state;
+
+                       /* Otherwise, it can't do any sort of state
+                          transition. */
+                     case FTS_EXPIRED:
+                     case FTS_RELEASED:
+                     case FTS_RESET:
+                       return lease -> binding_state;
+
+                     case FTS_BACKUP:
+                       return FTS_BACKUP;
+               }
+       }
+       /*NOTREACHED*/
+       return lease -> binding_state;
+}
+
+int lease_mine_to_extend (struct lease *lease)
+{
+       dhcp_failover_state_t *peer;
+
+       if (lease && lease -> pool &&
+           lease -> pool -> failover_peer) {
+               peer = lease -> pool -> failover_peer;
+/* XXX This does't seem right - either peer can extend a lease to MCLT. */
+               if (peer -> my_state == normal) { /* XXX */
+                       switch (lease -> binding_state) {
+                             case FTS_ACTIVE:
+                             case FTS_ABANDONED:
+                             case FTS_RESET:
+                             case FTS_RELEASED:
+                             case FTS_EXPIRED:
+                             case FTS_FREE:
+                             case FTS_BOOTP:
+                             case FTS_RESERVED:
+                               if (peer -> i_am == secondary)
+                                       return 0;
+                               break;
+                             case FTS_BACKUP:
+                               if (peer -> i_am == primary)
+                                       return 0;
+                               break;
+                       }
+               }
+       }
+       return 1;
+}
+
 OMAPI_OBJECT_ALLOC (dhcp_failover_state, dhcp_failover_state_t,
                    dhcp_type_failover_state)
 OMAPI_OBJECT_ALLOC (dhcp_failover_listener, dhcp_failover_listener_t,
index c6b28978f2b24fa4675442a50d5e86eafd853717..079540ff6651456e1b79946cbdcd66542488d5bf 100644 (file)
@@ -3,7 +3,7 @@
    Server-specific in-memory database support. */
 
 /*
- * Copyright (c) 1996-1999 Internet Software Consortium.
+ * Copyright (c) 1996-2000 Internet Software Consortium.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -43,7 +43,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: mdb.c,v 1.32 2000/05/16 23:03:46 mellon Exp $ Copyright (c) 1996-2000 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: mdb.c,v 1.33 2000/06/02 21:27:19 mellon Exp $ Copyright (c) 1996-2000 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -498,45 +498,27 @@ void new_address_range (low, high, subnet, pool)
                                                    subnet -> netmask,
                                                    i + min)),
                                   isc_result_totext (status));
-               /* Fill in the last lease if it hasn't been already... */
-               if (!pool -> last_lease) {
-                       lease_reference (&pool -> last_lease, lp, MDL);
-               }
 #endif
                lp -> ip_addr = ip_addr (subnet -> net,
-                                     subnet -> netmask, i + min);
+                                        subnet -> netmask, i + min);
                lp -> starts = lp -> timestamp = MIN_TIME;
                lp -> ends = MIN_TIME;
                subnet_reference (&lp -> subnet, subnet, MDL);
                pool_reference (&lp -> pool, pool, MDL);
-#if defined (FAILOVER_PROTOCOL)
-               if (pool -> failover_peer &&
-                   pool -> failover_peer -> i_am == secondary)
-                       lp -> flags = PEER_IS_OWNER;
-               else
-                       lp -> flags = 0;
-#endif
+               lp -> binding_state = FTS_FREE;
+               lp -> next_binding_state = FTS_FREE;
+               lp -> flags = 0;
 
                /* Link this entry into the list. */
-               if (pool -> leases) {
-                       lease_reference (&lp -> next, pool -> leases, MDL);
-                       lease_dereference (&pool -> leases, MDL);
+               if (pool -> free) {
+                       lease_reference (&lp -> next, pool -> free, MDL);
+                       lease_dereference (&pool -> free, MDL);
                }
-               lease_reference (&pool -> leases, lp, MDL);
-               if (lp -> next)
-                       lease_reference (&lp -> next -> prev,
-                                        pool -> leases, MDL);
+               lease_reference (&pool -> free, lp, MDL);
                lease_hash_add (lease_ip_addr_hash, lp -> ip_addr.iabuf,
                                lp -> ip_addr.len, lp, MDL);
        }
 
-#if defined (COMPACT_LEASES)
-       /* Fill in the last lease if it hasn't been already... */
-       if (!pool -> last_lease) {
-               lease_reference (&pool -> last_lease, &address_range [0], MDL);
-       }
-#endif
-
        /* Find out if any dangling leases are in range... */
        plp = (struct lease *)0;
        for (lp = dangling_leases; lp; lp = lp -> next) {
@@ -568,7 +550,7 @@ void new_address_range (low, high, subnet, pool)
                                lp -> hostname = (char *)0;
                                lt -> client_hostname = lp -> client_hostname;
                                lp -> client_hostname = (char *)0;
-                               supersede_lease (lt, lp, 0, 0);
+                               supersede_lease (lt, lp, 0, 0, 0);
                                lease_dereference (&lt, MDL);
                        }
                        lease_dereference (&lp, MDL);
@@ -744,15 +726,13 @@ void enter_lease (lease)
        /* If we don't have a place for this lease yet, save it for
           later. */
        if (!find_lease_by_ip_addr (&comp, lease -> ip_addr, MDL)) {
-               if (comp -> next)
-                       lease_dereference (&comp -> next, MDL);
+               if (lease -> next)
+                       lease_dereference (&lease -> next, MDL);
                if (dangling_leases)
-                       lease_reference (&comp -> next, dangling_leases, MDL);
-               lease_reference (&dangling_leases, comp, MDL);
-               if (comp -> prev)
-                       lease_dereference (&comp -> prev, MDL);
+                       lease_reference (&lease -> next, dangling_leases, MDL);
+               lease_reference (&dangling_leases, lease, MDL);
        } else {
-               supersede_lease (comp, lease, 0, 0);
+               supersede_lease (comp, lease, 0, 0, 0);
        }
 }
 
@@ -761,14 +741,20 @@ void enter_lease (lease)
    list of leases by expiry time so that we can always find the oldest
    lease. */
 
-int supersede_lease (comp, lease, commit, propogate)
+int supersede_lease (comp, lease, commit, propogate, pimmediate)
        struct lease *comp, *lease;
        int commit;
        int propogate;
+       int pimmediate;
 {
        int enter_uid = 0;
        int enter_hwaddr = 0;
-       struct lease *lp;
+       struct lease *lp, **lq, *prev;
+       TIME lp_next_state;
+
+       /* If there is no sample lease, just do the move. */
+       if (!lease)
+               goto just_move_it;
 
        /* Static leases are not currently kept in the database... */
        if (lease -> flags & STATIC_LEASE)
@@ -783,8 +769,10 @@ int supersede_lease (comp, lease, commit, propogate)
           lease, then we allow that, in case a dynamic BOOTP lease is
           requested *after* a DHCP lease has been assigned. */
 
-       if (!(lease -> flags & ABANDONED_LEASE) &&
-           comp -> ends > cur_time &&
+       if (lease -> binding_state != FTS_ABANDONED &&
+           (comp -> binding_state == FTS_ACTIVE ||
+            comp -> binding_state == FTS_RESERVED ||
+            comp -> binding_state == FTS_BOOTP) &&
            (((comp -> uid && lease -> uid) &&
              (comp -> uid_len != lease -> uid_len ||
               memcmp (comp -> uid, lease -> uid, comp -> uid_len))) ||
@@ -905,272 +893,232 @@ int supersede_lease (comp, lease, commit, propogate)
                hw_hash_add (comp);
        }
        
-       /* Remove the lease from its current place in the 
-          timeout sequence. */
-       if (comp -> prev) {
-               lease_dereference (&comp -> prev -> next, MDL);
+#if defined (FAILOVER_PROTOCOL)
+       comp -> cltt = lease -> cltt;
+       comp -> tstp = lease -> tstp;
+       comp -> tsfp = lease -> tsfp;
+#endif /* FAILOVER_PROTOCOL */
+       comp -> ends = lease -> ends;
+       comp -> next_binding_state = lease -> next_binding_state;
+
+      just_move_it:
+       /* Figure out which queue it's on. */
+       switch (comp -> binding_state) {
+             case FTS_FREE:
+               lq = &comp -> pool -> free;
+               comp -> pool -> free_leases--;
+               break;
+
+             case FTS_ACTIVE:
+             case FTS_RESERVED:
+             case FTS_BOOTP:
+               lq = &comp -> pool -> active;
+               break;
+
+             case FTS_EXPIRED:
+             case FTS_RELEASED:
+             case FTS_RESET:
+               lq = &comp -> pool -> expired;
+               break;
+
+             case FTS_ABANDONED:
+               lq = &comp -> pool -> abandoned;
+               break;
+
+             case FTS_BACKUP:
+               lq = &comp -> pool -> backup;
+               comp -> pool -> backup_leases--;
+               break;
+
+             default:
+               log_error ("Lease with bogus binding state: %d",
+                          comp -> binding_state);
+               return 0;
+       }
+
+       /* Remove the lease from its current place in its current
+          timer sequence. */
+       prev = (struct lease *)0;
+       for (lp = *lq; lp; lp = lp -> next) {
+               if (lp == comp)
+                       break;
+               prev = lp;
+       }
+
+       if (!lp) {
+               log_error ("Lease with binding state %s not on its queue.",
+                          (comp -> binding_state < 1 &&
+                           comp -> binding_state < FTS_BOOTP)
+                          ? "unknown"
+                          : binding_state_names [comp -> binding_state - 1]);
+               return 0;
+       }
+       
+       if (prev) {
+               lease_dereference (&prev -> next, MDL);
                if (comp -> next) {
-                       lease_reference (&comp -> prev -> next,
-                                        comp -> next, MDL);
+                       lease_reference (&prev -> next, comp -> next, MDL);
                        lease_dereference (&comp -> next, MDL);
                }
        } else {
-               lease_dereference (&comp -> pool -> leases, MDL);
+               lease_dereference (lq, MDL);
                if (comp -> next) {
-                       lease_reference (&comp -> pool -> leases,
-                                        comp -> next, MDL);
-               }
-       }
-       if (comp -> next) {
-               lease_dereference (&comp -> next -> prev, MDL);
-               if (comp -> prev) {
-                       lease_reference (&comp -> next -> prev,
-                                        comp -> prev, MDL);
+                       lease_reference (lq, comp -> next, MDL);
+                       lease_dereference (&comp -> next, MDL);
                }
        }
-       if (comp -> pool -> last_lease == comp) {
-               lease_dereference (&comp -> pool -> last_lease, MDL);
-               if (comp -> prev)
-                       lease_reference (&comp -> pool -> last_lease,
-                                        comp -> prev, MDL);
-       }
-       if (comp -> prev)
-               lease_dereference (&comp -> prev, MDL);
-       if (comp -> next)
-               lease_dereference (&comp -> next, MDL);
 
-
-       /* If there's an expiry event on this lease, get rid of it
-          (we may wind up putting it back, but we can't count on
-          that here without too much additional complexity). */
-       if (comp -> pool -> next_expiry == comp) {
-#if defined (FAILOVER_PROTOCOL)
-               lp = comp -> prev;
-#else
-               for (lp = comp -> prev; lp; lp = lp -> prev)
-                       if (lp -> on_expiry)
-                               break;
-#endif
-               if (lp
-#if !defined (FAILOVER_PROTOCOL)
-                   && lp -> on_expiry
-#endif
-                       ) {
-                       lease_dereference (&comp -> pool -> next_expiry, MDL);
-                       lease_reference (&comp -> pool -> next_expiry,
-                                        lp, MDL);
-                       if (commit)
-                               add_timeout (lp -> ends,
-                                            pool_timer, lp -> pool,
-                                            (tvref_t)pool_reference,
-                                            (tvunref_t)pool_dereference);
-               } else {
-                       lease_dereference (&comp -> pool -> next_expiry, MDL);
-                       if (commit)
-                               cancel_timeout (pool_timer, comp -> pool);
-               }
+       /* Make the state transition. */
+       if (commit)
+               process_state_transition (comp);
+
+       /* Figure out which queue it's going to. */
+       switch (comp -> binding_state) {
+             case FTS_FREE:
+               lq = &comp -> pool -> free;
+               comp -> pool -> free_leases++;
+               comp -> sort_time = comp -> ends;
+               break;
+
+             case FTS_ACTIVE:
+             case FTS_RESERVED:
+             case FTS_BOOTP:
+               lq = &comp -> pool -> active;
+               comp -> sort_time = comp -> ends;
+               break;
+
+             case FTS_EXPIRED:
+             case FTS_RELEASED:
+             case FTS_RESET:
+               lq = &comp -> pool -> expired;
+               comp -> sort_time = comp -> ends;
+
+               break;
+
+             case FTS_ABANDONED:
+               lq = &comp -> pool -> abandoned;
+               comp -> sort_time = comp -> ends;
+               break;
+
+             case FTS_BACKUP:
+               lq = &comp -> pool -> backup;
+               comp -> pool -> backup_leases++;
+               comp -> sort_time = comp -> ends;
+               break;
+
+             default:
+               log_error ("Lease with bogus binding state: %d",
+                          comp -> binding_state);
+               return 0;
        }
-       
-       /* Find the last insertion point... */
-       if (comp == comp -> pool -> insertion_point ||
-           !comp -> pool -> insertion_point) {
-               lp = comp -> pool -> leases;
-       } else {
-               lp = comp -> pool -> insertion_point;
+
+       /* Insertion sort the lease onto the appropriate queue. */
+       prev = (struct lease *)0;
+       for (lp = *lq; lp; lp = lp -> next) {
+               if (lp -> sort_time > comp -> sort_time)
+                       break;
+               prev = lp;
        }
-       
-       if (!lp) {
-               /* Nothing on the list yet?    Just make comp the
-                  head of the list. */
-               lease_reference (&comp -> pool -> leases, comp, MDL);
-               if (comp -> pool -> last_lease) {
-                       lease_dereference (&comp -> pool -> last_lease, MDL);
-                       lease_reference (&comp -> pool -> last_lease,
-                                        comp, MDL);
-               }
-       } else if (lp -> ends > lease -> ends) {
-               /* Skip down the list until we run out of list
-                  or find a place for comp. */
-               while (lp -> next && lp -> ends > lease -> ends) {
-                       lp = lp -> next;
-               }
-               if (lp -> ends > lease -> ends) {
-                       /* If we ran out of list, put comp at the end. */
-                       lease_reference (&lp -> next, comp, MDL);
-                       lease_reference (&comp -> prev, lp, MDL);
-                       if (comp -> pool -> last_lease)
-                               lease_dereference (&comp -> pool -> last_lease,
-                                                  MDL);
-                       lease_reference (&comp -> pool -> last_lease,
-                                        comp, MDL);
-               } else {
-                       /* If we didn't, put it between lp and
-                          the previous item on the list. */
-                       if (lp -> prev) {
-                               lease_reference (&comp -> prev,
-                                                lp -> prev, MDL);
-                               lease_dereference (&lp -> prev -> next, MDL);
-                               lease_reference (&comp -> prev -> next,
-                                                comp, MDL);
-                               lease_dereference (&lp -> prev, MDL);
-                       } else {
-                               if (comp -> pool -> leases)
-                                       lease_dereference
-                                               (&comp -> pool -> leases, MDL);
-                               lease_reference (&comp -> pool -> leases,
-                                                comp, MDL);
-                       }
-                       lease_reference (&comp -> next, lp, MDL);
-                       lease_reference (&lp -> prev, comp, MDL);
+       if (prev) {
+               if (prev -> next) {
+                       lease_reference (&comp -> next, prev -> next, MDL);
+                       lease_dereference (&prev -> next, MDL);
                }
+               lease_reference (&prev -> next, comp, MDL);
        } else {
-               /* Skip up the list until we run out of list
-                  or find a place for comp. */
-               while (lp -> prev && lp -> ends < lease -> ends) {
-                       lp = lp -> prev;
-               }
-               if (lp -> ends < lease -> ends) {
-                       /* If we ran out of list, put comp at the beginning. */
-                       lease_reference (&lp -> prev, comp, MDL);
-                       lease_reference (&comp -> next, lp, MDL);
-                       if (comp -> pool -> leases)
-                               lease_dereference (&comp -> pool -> leases,
-                                                  MDL);
-                       lease_reference (&comp -> pool -> leases, comp, MDL);
-               } else {
-                       /* If we didn't, put it between lp and
-                          the next item on the list. */
-                       if (lp -> next) {
-                               lease_reference (&comp -> next,
-                                                lp -> next, MDL);
-                               lease_dereference (&lp -> next -> prev, MDL);
-                               lease_reference (&lp -> next -> prev,
-                                                comp, MDL);
-                               lease_dereference (&lp -> next, MDL);
-                       } else {
-                               /* XXX are we really supposed to
-                                  XXX be doing this? */
-                               if (comp -> pool -> last_lease)
-                                       lease_dereference
-                                               (&comp -> pool -> last_lease,
-                                                MDL);
-                               lease_reference (&comp -> pool -> last_lease,
-                                                comp, MDL);
-                       }
-                       lease_reference (&comp -> prev, lp, MDL);
-                       lease_reference (&lp -> next, comp, MDL);
+               if (*lq) {
+                       lease_reference (&comp -> next, *lq, MDL);
+                       lease_dereference (lq, MDL);
                }
+               lease_reference (lq, comp, MDL);
+       }
+
+       /* If this is the next lease that will timeout on the pool,
+          zap the old timeout and set the timeout on this pool to the
+          time that the lease's next event will happen.
+                  
+          We do not actually set the timeout unless commit is true -
+          we don't want to thrash the timer queue when reading the
+          lease database.  Instead, the database code calls the
+          expiry event on each pool after reading in the lease file,
+          and the expiry code sets the timer if there's anything left
+          to expire after it's run any outstanding expiry events on
+          the pool. */
+       if (commit &&
+           comp -> sort_time != MIN_TIME &&
+           comp -> sort_time < cur_time &&
+           comp -> sort_time < comp -> pool -> next_event_time) {
+               comp -> pool -> next_event_time = comp -> sort_time;
+               add_timeout (comp -> pool -> next_event_time,
+                            pool_timer, comp -> pool,
+                            (tvref_t)pool_reference,
+                            (tvunref_t)pool_dereference);
        }
-       if (comp -> pool -> insertion_point)
-               lease_dereference (&comp -> pool -> insertion_point, MDL);
-       lease_reference (&comp -> pool -> insertion_point, comp, MDL);
-#if defined (FAILOVER_PROTOCOL)
-       if (comp -> ends <= cur_time && lease -> ends > cur_time) {
-               if (lease -> flags & PEER_IS_OWNER)
-                       comp -> pool -> peer_leases--;
-               else
-                       comp -> pool -> local_leases--;
-       } else if (comp -> ends > cur_time && lease -> ends <= cur_time) {
-               if (lease -> flags & PEER_IS_OWNER)
-                       comp -> pool -> peer_leases++;
-               else
-                       comp -> pool -> local_leases++;
-       }               
-       comp -> cltt = lease -> cltt;
-       comp -> tstp = lease -> tstp;
-       comp -> tsfp = lease -> tsfp;
-#endif /* FAILOVER_PROTOCOL */
-       comp -> ends = lease -> ends;
 
-       /* If there's an expiry event on this lease, process it or
-          queue it. */
-#if !defined (FAILOVER_PROTOCOL)
-       if (comp -> on_expiry) {
+       /* Return zero if we didn't commit the lease to permanent storage;
+          nonzero if we did. */
+       return commit && write_lease (comp) && commit_leases ()
+#if defined (FAILOVER_PROTOCOL)
+               && (!propogate ||
+                   dhcp_failover_queue_update (comp, pimmediate))
 #endif
-               if (comp -> ends <= cur_time && commit) {
-                   if (comp -> on_expiry) {
+               ;
+}
+
+void process_state_transition (struct lease *lease)
+{
+       /* If the lease was active and is now no longer active, but isn't
+          released, then it just expired, so do the expiry event. */
+       if (lease -> next_binding_state != lease -> binding_state &&
+           (lease -> binding_state == FTS_ACTIVE ||
+            lease -> binding_state == FTS_BOOTP ||
+            lease -> binding_state == FTS_RESERVED) &&
+           lease -> next_binding_state != FTS_RELEASED) {
+               if (lease -> on_expiry) {
                        execute_statements ((struct packet *)0, lease,
                                            (struct option_state *)0,
                                            (struct option_state *)0, /* XXX */
                                            &lease -> scope,
-                                           comp -> on_expiry);
-                       executable_statement_dereference (&comp -> on_expiry,
+                                           lease -> on_expiry);
+                       executable_statement_dereference (&lease -> on_expiry,
                                                          MDL);
-                   }
+               }
+               
+               /* No sense releasing a lease after it's expired. */
+               if (lease -> on_release)
+                       executable_statement_dereference (&lease -> on_release,
+                                                         MDL);
+               /* Send the expiry time to the peer. */
+               lease -> tstp = lease -> ends;
+       }
 
-                   /* No sense releasing a lease after it's expired. */
-                   if (comp -> on_release)
-                           executable_statement_dereference
-                                   (&comp -> on_release, MDL);
-               } else {
-                       /* If this is the next lease that will timeout on the
-                          pool, zap the old timeout and set the timeout on
-                          this pool to the time that the lease ends.
-                          
-                          We do not actually set the timeout unless commit is
-                          true - we don't want to thrash the timer queue when
-                          reading the lease database.  Instead, the database
-                          code calls the expiry event on each pool after
-                          reading in the lease file, and the expiry code sets
-                          the timer if there's anything left to expire after
-                          it's run any outstanding expiry events on the
-                          pool. */
-                       if (comp -> pool) {
-                           if (!comp -> pool -> next_expiry ||
-                               (comp -> ends <
-                                comp -> pool -> next_expiry -> ends)) {
-                                   if (comp -> pool -> next_expiry)
-                                       lease_dereference
-                                               (&comp -> pool -> next_expiry,
-                                                MDL);
-                                   lease_reference
-                                           (&comp -> pool -> next_expiry,
-                                            comp, MDL);
-                                   if (commit)
-                                       add_timeout (comp -> ends,
-                                                    pool_timer,
-                                                    comp -> pool,
-                                                    (tvref_t)pool_reference,
-                                                    (tvunref_t)
-                                                    pool_dereference);
-                            } else if (comp -> ends ==
-                                       comp -> pool -> next_expiry -> ends) {
-                                    /* If there are other leases that expire at
-                                       the same time as comp, we need to make
-                                       sure that we have the one that appears
-                                       last on the list that needs an expiry
-                                       event - otherwise we'll miss expiry
-                                       events until the server restarts. */
-                                    struct lease *foo;
-                                    struct lease *install = comp;
-                                    for (foo = comp;
-                                        foo && foo -> ends == comp -> ends;
-                                         foo = foo -> next) {
-#if !defined (FAILOVER_PROTOCOL)
-                                            if (foo -> on_expiry)
-#endif
-                                                    install = foo;
-                                    }
-                                   lease_dereference
-                                           (&comp -> pool -> next_expiry,
-                                            MDL);
-                                    lease_reference
-                                           (&comp -> pool -> next_expiry,
-                                            install, MDL);
-                            }
-                       }
+       /* If the lease was active and is now released, do the release
+          event. */
+       if ((lease -> binding_state == FTS_ACTIVE ||
+            lease -> binding_state == FTS_BOOTP ||
+            lease -> binding_state == FTS_RESERVED) &&
+           lease -> next_binding_state == FTS_RELEASED) {
+               if (lease -> on_release) {
+                       execute_statements ((struct packet *)0, lease,
+                                           (struct option_state *)0,
+                                           (struct option_state *)0, /* XXX */
+                                           &lease -> scope,
+                                           lease -> on_release);
+                       executable_statement_dereference (&lease -> on_release,
+                                                         MDL);
                }
-#if !defined (FAILOVER_PROTOCOL)
+               
+               /* A released lease can't expire. */
+               if (lease -> on_expiry)
+                       executable_statement_dereference (&lease -> on_expiry,
+                                                         MDL);
+
+               /* Send the release time (should be == cur_time) to the
+                  peer. */
+               lease -> tstp = lease -> ends;
        }
-#endif
 
-       /* Return zero if we didn't commit the lease to permanent storage;
-          nonzero if we did. */
-       return commit && write_lease (comp) && commit_leases ()
-#if defined (FAILOVER_PROTOCOL)
-               && (!propogate || dhcp_failover_queue_update (comp))
-#endif
-               ;
+       lease -> binding_state = lease -> next_binding_state;
 }
 
 /* Copy the contents of one lease into another, correctly maintaining
@@ -1242,6 +1190,8 @@ int lease_copy (struct lease **lp,
        lt -> tstp = lease -> tstp;
        lt -> tsfp = lease -> tsfp;
        lt -> cltt = lease -> cltt;
+       lt -> binding_state = lease -> binding_state;
+       lt -> next_binding_state = lease -> next_binding_state;
        status = lease_reference (lp, lt, file, line);
        lease_dereference (&lt, MDL);
        return status == ISC_R_SUCCESS;
@@ -1252,8 +1202,6 @@ void release_lease (lease, packet)
        struct lease *lease;
        struct packet *packet;
 {
-       struct lease *lt;
-
        /* If there are statements to execute when the lease is
           released, execute them. */
        if (lease -> on_release) {
@@ -1272,21 +1220,26 @@ void release_lease (lease, packet)
                executable_statement_dereference (&lease -> on_expiry, MDL);
 
        if (lease -> ends > cur_time) {
-               if (!lease_copy (&lt, lease, MDL))
-                       return;
-
-               if (lt -> on_commit)
-                       executable_statement_dereference (&lt -> on_commit,
+               if (lease -> on_commit)
+                       executable_statement_dereference (&lease -> on_commit,
                                                          MDL);
 
                /* Blow away any bindings. */
-               lt -> scope.bindings = (struct binding *)0;
-
-               lt -> ends = cur_time;
-               if (lt -> billing_class)
-                       class_dereference (&lt -> billing_class, MDL);
-               supersede_lease (lease, lt, 1, 1);
-               lease_dereference (&lt, MDL);
+               /* XXX free them?!? */
+               lease -> scope.bindings = (struct binding *)0;
+               lease -> ends = cur_time;
+#if defined (FAILOVER_PROTOCOL)
+               if (lease -> pool && lease -> pool -> failover_peer) {
+                       lease -> next_binding_state = FTS_RELEASED;
+               } else {
+                       lease -> next_binding_state = FTS_FREE;
+               }
+#else
+               lease -> next_binding_state = FTS_FREE;
+#endif
+               if (lease -> billing_class)
+                       class_dereference (&lease -> billing_class, MDL);
+               supersede_lease (lease, (struct lease *)0, 1, 1, 1);
        }
 }
 
@@ -1299,7 +1252,6 @@ void abandon_lease (lease, message)
 {
        struct lease *lt = (struct lease *)0;
 
-       lease -> flags |= ABANDONED_LEASE;
        if (!lease_copy (&lt, lease, MDL))
                return;
 
@@ -1312,8 +1264,9 @@ void abandon_lease (lease, message)
 
        /* Blow away any bindings. */
        lt -> scope.bindings = (struct binding *)0;
-
        lt -> ends = cur_time; /* XXX */
+       lt -> next_binding_state = FTS_ABANDONED;
+
        log_error ("Abandoning IP address %s: %s",
              piaddr (lease -> ip_addr), message);
        lt -> hardware_addr.hlen = 0;
@@ -1324,7 +1277,7 @@ void abandon_lease (lease, message)
        lt -> uid_max = 0;
        if (lt -> billing_class)
                class_dereference (&lt -> billing_class, MDL);
-       supersede_lease (lease, lt, 1, 1);
+       supersede_lease (lease, lt, 1, 1, 1);
        lease_dereference (&lt, MDL);
 }
 
@@ -1349,6 +1302,15 @@ void dissociate_lease (lease)
        /* Blow away any bindings. */
        lt -> scope.bindings = (struct binding *)0;
 
+#if defined (FAILOVER_PROTOCOL)
+       if (lease -> pool && lease -> pool -> failover_peer) {
+               lt -> next_binding_state = FTS_RESET;
+       } else {
+               lt -> next_binding_state = FTS_FREE;
+       }
+#else
+       lt -> next_binding_state = FTS_FREE;
+#endif
        lt -> ends = cur_time; /* XXX */
        lt -> hardware_addr.hlen = 0;
        if (lt -> uid != lt -> uid_buf)
@@ -1358,7 +1320,7 @@ void dissociate_lease (lease)
        lt -> uid_max = 0;
        if (lt -> billing_class)
                class_dereference (&lt -> billing_class, MDL);
-       supersede_lease (lease, lt, 1, 1);
+       supersede_lease (lease, lt, 1, 1, 1);
        lease_dereference (&lt, MDL);
 }
 
@@ -1367,72 +1329,75 @@ void pool_timer (vpool)
        void *vpool;
 {
        struct pool *pool;
-       struct lease *lease;
+       struct lease *lt = (struct lease *)0;
+       struct lease *next = (struct lease *)0;
+       struct lease *lease = (struct lease *)0;
+       struct lease **lptr [5];
+       TIME next_expiry = MAX_TIME;
+       int i;
 
        pool = (struct pool *)vpool;
-       for (lease = pool -> next_expiry; lease; lease = lease -> prev) {
-               /* Stop processing when we get to the first lease that has not
-                   yet expired. */
-               if (lease -> ends > cur_time)
-                       break;
 
-               /* Skip entries that aren't set to expire. */
-               if (lease -> on_expiry) {
-                       /* Okay, the current lease needs to expire, so
-                           do it. */
-                       execute_statements ((struct packet *)0, lease,
-                                           (struct option_state *)0,
-                                           (struct option_state *)0, /* XXX */
-                                           &lease -> scope,
-                                           lease -> on_expiry);
-                       if (lease -> on_expiry)
-                               executable_statement_dereference
-                                       (&lease -> on_expiry, MDL);
-               }                       
-
-               /* If there's an on_release event, blow it away. */
-               if (lease -> on_release)
-                       executable_statement_dereference (&lease -> on_release,
-                                                         MDL);
+#define FREE_LEASES 0
+       lptr [FREE_LEASES] = &pool -> free;
+#define ACTIVE_LEASES 1
+       lptr [ACTIVE_LEASES] = &pool -> active;
+#define EXPIRED_LEASES 2
+       lptr [EXPIRED_LEASES] = &pool -> expired;
+#define ABANDONED_LEASES 3
+       lptr [ABANDONED_LEASES] = &pool -> abandoned;
+#define BACKUP_LEASES 4
+       lptr [BACKUP_LEASES] = &pool -> backup;
+
+       for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
+               /* If there's nothing on the queue, skip it. */
+               if (!*(lptr [i]))
+                       continue;
                
-               /* There are two problems with writing the lease out here.
-
-                  The first is that we've just done a commit, and the write
-                  may fail, in which case we will redo the operation.  If the
-                  operation is not idempotent, we're in trouble here.  I have
-                  no proposed solution for this problem - make the event
-                  idempotent, or make sure that it at least isn't harmful to
-                  do it twice.
-
-                  The second is that if we just read in the lease file and ran
-                  all the expiry events, we're going to rewrite all expiring
-                  leases twice.  There's no real answer for this - if we
-                  postpone writing until we've expired all leases, we're
-                  increasing the window to lose as described above.  I guess a
-                  dirty bit on the lease would work.  Hm. */
-               if (!write_lease (lease)) {
-                       log_error ("Error updating lease %s after expiry",
-                                  piaddr (lease -> ip_addr));
-               }
-               if (!commit_leases ()) {
-                       log_error ("Error committing after writing lease %s",
-                                  piaddr (lease -> ip_addr));
+               lease_reference (&lease, *(lptr [i]), MDL);
+
+               while (lease) {
+                       /* Remember the next lease in the list. */
+                       if (next)
+                               lease_dereference (&next, MDL);
+                       if (lease -> next)
+                               lease_reference (&next, lease -> next, MDL);
+
+                       /* If we've run out of things to expire on this list,
+                          stop. */
+                       if (lease -> sort_time > cur_time) {
+                               if (lease -> sort_time < next_expiry)
+                                       next_expiry = lease -> sort_time;
+                               break;
+                       }
+
+                       /* If there is a pending state change, and
+                          this lease has gotten to the time when the
+                          state change should happen, just call
+                          supersede_lease on it to make the change
+                          happen. */
+                       if (lease -> next_binding_state !=
+                           lease -> binding_state)
+                               supersede_lease (lease,
+                                                (struct lease *)0, 1, 1, 1);
+
+                       lease_dereference (&lease, MDL);
+                       if (next)
+                               lease_reference (&lease, next, MDL);
                }
-#if defined (FAILOVER_PROTOCOL)
-               if (lease -> flags & PEER_IS_OWNER)
-                       pool -> peer_leases++;
-               else
-                       pool -> local_leases++;
-#endif
-       }
-       if (pool -> next_expiry)
-               lease_dereference (&pool -> next_expiry, MDL);
-       if (lease) {
-               lease_reference (&pool -> next_expiry, lease, MDL);
-               add_timeout (lease -> ends, pool_timer, pool,
+               if (next)
+                       lease_dereference (&next, MDL);
+               if (lease)
+                       lease_dereference (&lease, MDL);
+       }
+       if (next_expiry != MAX_TIME) {
+               pool -> next_event_time = next_expiry;
+               add_timeout (pool -> next_event_time, pool_timer, pool,
                             (tvref_t)pool_reference,
                             (tvunref_t)pool_dereference);
-       }
+       } else
+               pool -> next_event_time = MIN_TIME;
+
 }
 
 /* Locate the lease associated with a given IP address... */
@@ -1618,6 +1583,7 @@ void write_leases ()
        struct hash_bucket *hb;
        int i;
        int num_written;
+       struct lease **lptr [5];
 
        /* Write all the dynamically-created group declarations. */
        if (group_name_hash) {
@@ -1682,18 +1648,25 @@ void write_leases ()
        /* Write all the leases. */
        num_written = 0;
        for (s = shared_networks; s; s = s -> next) {
-               for (p = s -> pools; p; p = p -> next) {
-                       for (l = p -> leases; l; l = l -> next) {
-                               if (l -> hardware_addr.hlen ||
-                                   l -> uid_len ||
-                                   (l -> flags & ABANDONED_LEASE)) {
-                                       if (!write_lease (l))
-                                               log_fatal ("Can't rewrite %s",
-                                                          "lease database");
-                                       num_written++;
-                               }
+           for (p = s -> pools; p; p = p -> next) {
+               lptr [FREE_LEASES] = &p -> free;
+               lptr [ACTIVE_LEASES] = &p -> active;
+               lptr [EXPIRED_LEASES] = &p -> expired;
+               lptr [ABANDONED_LEASES] = &p -> abandoned;
+               lptr [BACKUP_LEASES] = &p -> backup;
+
+               for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
+                   for (l = *(lptr [i]); l; l = l -> next) {
+                       if (l -> hardware_addr.hlen ||
+                           l -> uid_len ||
+                           (l -> binding_state != FTS_FREE)) {
+                           if (!write_lease (l))
+                               log_fatal ("Can't rewrite lease database");
+                           num_written++;
                        }
+                   }
                }
+           }
        }
        log_info ("Wrote %d leases to leases file.", num_written);
        if (!commit_leases ())
@@ -1712,6 +1685,7 @@ void expire_all_pools ()
        struct hash_bucket *hb;
        int i;
        struct lease *l;
+       struct lease **lptr [5];
 
        /* Loop through each pool in each shared network and call the
           expiry routine on the pool. */
@@ -1721,21 +1695,29 @@ void expire_all_pools ()
 
 #if defined (FAILOVER_PROTOCOL)
                p -> lease_count = 0;
-               p -> local_leases = 0;
-               p -> peer_leases = 0;
+               p -> free_leases = 0;
+               p -> backup_leases = 0;
                
-               for (l = p -> leases; l; l = l -> next) {
+               lptr [FREE_LEASES] = &p -> free;
+               lptr [ACTIVE_LEASES] = &p -> active;
+               lptr [EXPIRED_LEASES] = &p -> expired;
+               lptr [ABANDONED_LEASES] = &p -> abandoned;
+               lptr [BACKUP_LEASES] = &p -> backup;
+
+               for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
+                   for (l = *(lptr [i]); l; l = l -> next) {
                        p -> lease_count++;
                        if (l -> ends <= cur_time) {
-                               if (l -> flags & PEER_IS_OWNER)
-                                       p -> peer_leases++;
-                               else
-                                       p -> local_leases++;
+                               if (l -> binding_state == FTS_FREE)
+                                       p -> free_leases++;
+                               else if (l -> binding_state == FTS_BACKUP)
+                                       p -> backup_leases++;
                        }
                        if (p -> failover_peer &&
                            l -> tstp > l -> tsfp &&
                            !(l -> flags & ON_UPDATE_QUEUE))
-                               dhcp_failover_queue_update (l);
+                               dhcp_failover_queue_update (l, 1);
+                   }
                }
 #endif
            }
@@ -1748,6 +1730,8 @@ void dump_subnets ()
        struct shared_network *s;
        struct subnet *n;
        struct pool *p;
+       struct lease **lptr [5];
+       int i;
 
        log_info ("Subnets:");
        for (n = subnets; n; n = n -> next_subnet) {
@@ -1757,14 +1741,20 @@ void dump_subnets ()
        }
        log_info ("Shared networks:");
        for (s = shared_networks; s; s = s -> next) {
-               log_info ("  %s", s -> name);
-               for (p = s -> pools; p; p = p -> next) {
-                       for (l = p -> leases; l; l = l -> next) {
-                               print_lease (l);
-                       }
-                       log_debug ("Last Lease:");
-                       print_lease (p -> last_lease);
+           log_info ("  %s", s -> name);
+           for (p = s -> pools; p; p = p -> next) {
+               lptr [FREE_LEASES] = &p -> free;
+               lptr [ACTIVE_LEASES] = &p -> active;
+               lptr [EXPIRED_LEASES] = &p -> expired;
+               lptr [ABANDONED_LEASES] = &p -> abandoned;
+               lptr [BACKUP_LEASES] = &p -> backup;
+
+               for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
+                   for (l = *(lptr [i]); l; l = l -> next) {
+                           print_lease (l);
+                   }
                }
+           }
        }
 }
 
index 9938668b3729e5ff389db7295016ac245840c5a5..c49660a3bd99e0afa69a7c1ec122885f557d2807 100644 (file)
@@ -50,7 +50,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: omapi.c,v 1.28 2000/05/16 23:03:48 mellon Exp $ Copyright (c) 1999-2000 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: omapi.c,v 1.29 2000/06/02 21:27:20 mellon Exp $ Copyright (c) 1999-2000 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -179,27 +179,21 @@ isc_result_t dhcp_lease_set_value  (omapi_object_t *h,
        lease = (struct lease *)h;
 
        /* We're skipping a lot of things it might be interesting to
-          set - for now, we just make it possible to whack the abandoned
-          flag. */
-       if (!omapi_ds_strcmp (name, "abandoned")) {
-               int bar;
-
-               if (value -> type == omapi_datatype_int)
-                       bar = value -> u.integer;
-               else if (value -> type == omapi_datatype_data &&
-                        value -> u.buffer.len == sizeof (int)) {
-                       memcpy (&bar, value -> u.buffer.value, sizeof bar);
-                       /* No need to byte-swap here. */
-               } else
-                       return ISC_R_INVALIDARG;
+          set - for now, we just make it possible to whack the state. */
+       if (!omapi_ds_strcmp (name, "state")) {
+               unsigned long bar;
+               status = omapi_get_int_value (&bar, value);
+               if (status != ISC_R_SUCCESS)
+                       return status;
 
-               foo = lease -> flags;
-               if (bar)
-                       lease -> flags |= ABANDONED_LEASE;
-               else
-                       lease -> flags &= ~ABANDONED_LEASE;
-               if (foo != lease -> flags)
-                       return ISC_R_SUCCESS;
+               if (bar < 1 || bar > FTS_BOOTP)
+                       return ISC_R_INVALIDARG;
+               if (lease -> binding_state != bar) {
+                       lease -> next_binding_state = bar;
+                       if (supersede_lease (lease, 0, 1, 1, 1))
+                               return ISC_R_SUCCESS;
+                       return ISC_R_IOERROR;
+               }
                return ISC_R_UNCHANGED;
        }
 
@@ -226,14 +220,9 @@ isc_result_t dhcp_lease_get_value (omapi_object_t *h, omapi_object_t *id,
                return ISC_R_INVALIDARG;
        lease = (struct lease *)h;
 
-       if (!omapi_ds_strcmp (name, "abandoned"))
-               return omapi_make_int_value (value, name,
-                                            (lease -> flags &
-                                             ABANDONED_LEASE) ? 1 : 0, MDL);
-       else if (!omapi_ds_strcmp (name, "bootpp"))
+       if (!omapi_ds_strcmp (name, "state"))
                return omapi_make_int_value (value, name,
-                                            (lease -> flags &
-                                             BOOTP_LEASE) ? 1 : 0, MDL);
+                                            (int)lease -> binding_state, MDL);
        else if (!omapi_ds_strcmp (name, "ip-address"))
                return omapi_make_const_value (value, name,
                                               lease -> ip_addr.iabuf,
@@ -362,7 +351,7 @@ isc_result_t dhcp_lease_signal_handler (omapi_object_t *h,
                        return ISC_R_INVALIDARG;
                if (!write_lease (lease) || !commit_leases ()
 #if defined (FAILOVER_PROTOCOL)
-                   || !dhcp_failover_queue_update (lease)
+                   || !dhcp_failover_queue_update (lease, 1)
 #endif
                        ) {
                        return ISC_R_IOERROR;
@@ -395,26 +384,13 @@ isc_result_t dhcp_lease_stuff_values (omapi_object_t *c,
 
        /* Write out all the values. */
 
-       status = omapi_connection_put_name (c, "abandoned");
-       if (status != ISC_R_SUCCESS)
-               return status;
-       status = omapi_connection_put_uint32 (c, sizeof (int));
-       if (status != ISC_R_SUCCESS)
-               return status;
-       status = omapi_connection_put_uint32 (c, (lease -> flags &
-                                                 ABANDONED_LEASE) ? 1U : 0U);
-       if (status != ISC_R_SUCCESS)
-               return status;
-
-       status = omapi_connection_put_name (c, "bootpp");
+       status = omapi_connection_put_name (c, "state");
        if (status != ISC_R_SUCCESS)
                return status;
        status = omapi_connection_put_uint32 (c, sizeof (int));
        if (status != ISC_R_SUCCESS)
                return status;
-       status = omapi_connection_put_uint32 (c, ((unsigned) 
-                                                 ((lease -> flags &
-                                                   BOOTP_LEASE) ? 1 : 0)));
+       status = omapi_connection_put_uint32 (c, lease -> binding_state);
        if (status != ISC_R_SUCCESS)
                return status;
 
index 65cc3619a02e4f804313d0f5e82b9fa00c21beba..eca393f874dfba733176cd30efe384ac206f0b17 100644 (file)
@@ -43,7 +43,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: stables.c,v 1.12 2000/05/16 23:03:49 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: stables.c,v 1.13 2000/06/02 21:27:21 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -176,6 +176,12 @@ const char *dhcp_flink_state_names [] = {
 };
 #endif /* FAILOVER_PROTOCOL */
 
+/* Failover binding state names.   These are used even if there is no
+   failover protocol support. */
+const char *binding_state_names [] = {
+       "free", "active", "expired", "released", "abandoned",
+       "reset", "backup", "reserved", "bootp" };
+
 struct universe agent_universe;
 struct option agent_options [256] = {
        { "pad", "",                                    &agent_universe, 0 },