Merge from vendor branch HEIMDAL:
[dragonfly.git] / contrib / dhcp-3.0 / server / failover.c
1 /* failover.c
2
3    Failover protocol support code... */
4
5 /*
6  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
7  * Copyright (c) 1999-2003 by Internet Software Consortium
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  *
21  *   Internet Systems Consortium, Inc.
22  *   950 Charter Street
23  *   Redwood City, CA 94063
24  *   <info@isc.org>
25  *   http://www.isc.org/
26  *
27  * This software has been written for Internet Systems Consortium
28  * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29  * To learn more about Internet Systems Consortium, see
30  * ``http://www.isc.org/''.  To learn more about Vixie Enterprises,
31  * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
32  * ``http://www.nominum.com''.
33  */
34
35 #ifndef lint
36 static char copyright[] =
37 "$Id: failover.c,v 1.53.2.35 2004/11/24 17:39:19 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
38 #endif /* not lint */
39
40 #include "dhcpd.h"
41 #include "version.h"
42 #include <omapip/omapip_p.h>
43
44 #if defined (FAILOVER_PROTOCOL)
45 dhcp_failover_state_t *failover_states;
46 static isc_result_t do_a_failover_option (omapi_object_t *,
47                                           dhcp_failover_link_t *);
48 dhcp_failover_listener_t *failover_listeners;
49
50 static isc_result_t failover_message_reference (failover_message_t **,
51                                                 failover_message_t *,
52                                                 const char *file, int line);
53 static isc_result_t failover_message_dereference (failover_message_t **,
54                                                   const char *file, int line);
55
56 void dhcp_failover_startup ()
57 {
58         dhcp_failover_state_t *state;
59         isc_result_t status;
60         dhcp_failover_listener_t *l;
61
62         for (state = failover_states; state; state = state -> next) {
63                 dhcp_failover_state_transition (state, "startup");
64
65                 if (state -> pool_count == 0) {
66                         log_error ("failover peer declaration with no %s",
67                                    "referring pools.");
68                         log_error ("In order to use failover, you MUST %s",
69                                    "refer to your main failover declaration");
70                         log_error ("in each pool declaration.   You MUST %s",
71                                    "NOT use range declarations outside");
72                         log_fatal ("of pool declarations.");
73                 }
74                 /* In case the peer is already running, immediately try
75                    to establish a connection with it. */
76                 status = dhcp_failover_link_initiate ((omapi_object_t *)state);
77                 if (status != ISC_R_SUCCESS && status != ISC_R_INCOMPLETE) {
78 #if defined (DEBUG_FAILOVER_TIMING)
79                         log_info ("add_timeout +90 dhcp_failover_reconnect");
80 #endif
81                         add_timeout (cur_time + 90,
82                                      dhcp_failover_reconnect, state,
83                                      (tvref_t)
84                                      dhcp_failover_state_reference,
85                                      (tvunref_t)
86                                      dhcp_failover_state_dereference);
87                         log_error ("failover peer %s: %s", state -> name,
88                                    isc_result_totext (status));
89                 }
90
91                 status = (dhcp_failover_listen
92                           ((omapi_object_t *)state));
93                 if (status != ISC_R_SUCCESS) {
94 #if defined (DEBUG_FAILOVER_TIMING)
95                         log_info ("add_timeout +90 %s",
96                                   "dhcp_failover_listener_restart");
97 #endif
98                         add_timeout (cur_time + 90,
99                                      dhcp_failover_listener_restart,
100                                      state,
101                                      (tvref_t)omapi_object_reference,
102                                      (tvunref_t)omapi_object_dereference);
103                 }
104         }
105 }
106
107 int dhcp_failover_write_all_states ()
108 {
109         dhcp_failover_state_t *state;
110
111         for (state = failover_states; state; state = state -> next) {
112                 if (!write_failover_state (state))
113                         return 0;
114         }
115         return 1;
116 }
117
118 isc_result_t enter_failover_peer (peer)
119         dhcp_failover_state_t *peer;
120 {
121         dhcp_failover_state_t *dup = (dhcp_failover_state_t *)0;
122         isc_result_t status;
123
124         status = find_failover_peer (&dup, peer -> name, MDL);
125         if (status == ISC_R_NOTFOUND) {
126                 if (failover_states) {
127                         dhcp_failover_state_reference (&peer -> next,
128                                                       failover_states, MDL);
129                         dhcp_failover_state_dereference (&failover_states,
130                                                          MDL);
131                 }
132                 dhcp_failover_state_reference (&failover_states, peer, MDL);
133                 return ISC_R_SUCCESS;
134         }
135         dhcp_failover_state_dereference (&dup, MDL);
136         if (status == ISC_R_SUCCESS)
137                 return ISC_R_EXISTS;
138         return status;
139 }
140
141 isc_result_t find_failover_peer (peer, name, file, line)
142         dhcp_failover_state_t **peer;
143         const char *name;
144         const char *file;
145         int line;
146 {
147         dhcp_failover_state_t *p;
148
149         for (p = failover_states; p; p = p -> next)
150                 if (!strcmp (name, p -> name))
151                         break;
152         if (p)
153                 return dhcp_failover_state_reference (peer, p, file, line);
154         return ISC_R_NOTFOUND;
155 }
156
157 /* The failover protocol has three objects associated with it.  For
158    each failover partner declaration in the dhcpd.conf file, primary
159    or secondary, there is a failover_state object.  For any primary or
160    secondary state object that has a connection to its peer, there is
161    also a failover_link object, which has its own input state seperate
162    from the failover protocol state for managing the actual bytes
163    coming in off the wire.  Finally, there will be one listener object
164    for every distinct port number associated with a secondary
165    failover_state object.  Normally all secondary failover_state
166    objects are expected to listen on the same port number, so there
167    need be only one listener object, but if different port numbers are
168    specified for each failover object, there could be as many as one
169    listener object for each secondary failover_state object. */
170
171 /* This, then, is the implemention of the failover link object. */
172
173 isc_result_t dhcp_failover_link_initiate (omapi_object_t *h)
174 {
175         isc_result_t status;
176         dhcp_failover_link_t *obj;
177         omapi_value_t *value = (omapi_value_t *)0;
178         dhcp_failover_state_t *state;
179         omapi_object_t *o;
180         int i;
181         struct data_string ds;
182         omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
183         omapi_addr_t local_addr;
184
185         /* Find the failover state in the object chain. */
186         for (o = h; o -> outer; o = o -> outer)
187                 ;
188         for (; o; o = o -> inner) {
189                 if (o -> type == dhcp_type_failover_state)
190                         break;
191         }
192         if (!o)
193                 return ISC_R_INVALIDARG;
194         state = (dhcp_failover_state_t *)o;
195
196         obj = (dhcp_failover_link_t *)0;
197         status = dhcp_failover_link_allocate (&obj, MDL);
198         if (status != ISC_R_SUCCESS)
199                 return status;
200         option_cache_reference (&obj -> peer_address,
201                                 state -> partner.address, MDL);
202         obj -> peer_port = state -> partner.port;
203         dhcp_failover_state_reference (&obj -> state_object, state, MDL);
204
205         memset (&ds, 0, sizeof ds);
206         if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
207                                     (struct client_state *)0,
208                                     (struct option_state *)0,
209                                     (struct option_state *)0,
210                                     &global_scope, obj -> peer_address, MDL)) {
211                 dhcp_failover_link_dereference (&obj, MDL);
212                 return ISC_R_UNEXPECTED;
213         }
214
215         /* Make an omapi address list out of a buffer containing zero or more
216            IPv4 addresses. */
217         status = omapi_addr_list_new (&addrs, ds.len / 4, MDL);
218         if (status != ISC_R_SUCCESS) {
219                 dhcp_failover_link_dereference (&obj, MDL);
220                 return status;
221         }
222
223         for (i = 0; i  < addrs -> count; i++) {
224                 addrs -> addresses [i].addrtype = AF_INET;
225                 addrs -> addresses [i].addrlen = sizeof (struct in_addr);
226                 memcpy (addrs -> addresses [i].address,
227                         &ds.data [i * 4], sizeof (struct in_addr));
228                 addrs -> addresses [i].port = obj -> peer_port;
229         }
230         data_string_forget (&ds, MDL);
231
232         /* Now figure out the local address that we're supposed to use. */
233         if (!state -> me.address ||
234             !evaluate_option_cache (&ds, (struct packet *)0,
235                                     (struct lease *)0,
236                                     (struct client_state *)0,
237                                     (struct option_state *)0,
238                                     (struct option_state *)0,
239                                     &global_scope, state -> me.address,
240                                     MDL)) {
241                 memset (&local_addr, 0, sizeof local_addr);
242                 local_addr.addrtype = AF_INET;
243                 local_addr.addrlen = sizeof (struct in_addr);
244                 if (!state -> server_identifier.len) {
245                         log_fatal ("failover peer %s: no local address.",
246                                    state -> name);
247                 }
248         } else {
249                 if (ds.len != sizeof (struct in_addr)) {
250                         data_string_forget (&ds, MDL);
251                         dhcp_failover_link_dereference (&obj, MDL);
252                         omapi_addr_list_dereference (&addrs, MDL);
253                         return ISC_R_INVALIDARG;
254                 }
255                 local_addr.addrtype = AF_INET;
256                 local_addr.addrlen = ds.len;
257                 memcpy (local_addr.address, ds.data, ds.len);
258                 if (!state -> server_identifier.len)
259                         data_string_copy (&state -> server_identifier,
260                                           &ds, MDL);
261                 data_string_forget (&ds, MDL);
262                 local_addr.port = 0;  /* Let the O.S. choose. */
263         }
264
265         status = omapi_connect_list ((omapi_object_t *)obj,
266                                      addrs, &local_addr);
267         omapi_addr_list_dereference (&addrs, MDL);
268
269         dhcp_failover_link_dereference (&obj, MDL);
270         return status;
271 }
272
273 isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
274                                         const char *name, va_list ap)
275 {
276         isc_result_t status;
277         dhcp_failover_link_t *link;
278         omapi_object_t *c;
279         u_int16_t nlen;
280         u_int32_t vlen;
281         dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
282
283         if (h -> type != dhcp_type_failover_link) {
284                 /* XXX shouldn't happen.   Put an assert here? */
285                 return ISC_R_UNEXPECTED;
286         }
287         link = (dhcp_failover_link_t *)h;
288
289         if (!strcmp (name, "connect")) {
290             if (link -> state_object -> i_am == primary) {
291                 status = dhcp_failover_send_connect (h);
292                 if (status != ISC_R_SUCCESS) {
293                     log_info ("dhcp_failover_send_connect: %s",
294                               isc_result_totext (status));
295                     omapi_disconnect (h -> outer, 1);
296                 }
297             } else
298                 status = ISC_R_SUCCESS;
299             /* Allow the peer fifteen seconds to send us a
300                startup message. */
301 #if defined (DEBUG_FAILOVER_TIMING)
302             log_info ("add_timeout +15 %s",
303                       "dhcp_failover_link_startup_timeout");
304 #endif
305             add_timeout (cur_time + 15,
306                          dhcp_failover_link_startup_timeout,
307                          link,
308                          (tvref_t)dhcp_failover_link_reference,
309                          (tvunref_t)dhcp_failover_link_dereference);
310             return status;
311         }
312
313         if (!strcmp (name, "disconnect")) {
314             if (link -> state_object) {
315                 dhcp_failover_state_reference (&state,
316                                                link -> state_object, MDL);
317                 link -> state = dhcp_flink_disconnected;
318
319                 /* Make the transition. */
320                 if (state -> link_to_peer == link) {
321                     dhcp_failover_state_transition (link -> state_object,
322                                                     name);
323
324                     /* Start trying to reconnect. */
325 #if defined (DEBUG_FAILOVER_TIMING)
326                     log_info ("add_timeout +5 %s",
327                               "dhcp_failover_reconnect");
328 #endif
329                     add_timeout (cur_time + 5, dhcp_failover_reconnect,
330                                  state,
331                                  (tvref_t)dhcp_failover_state_reference,
332                                  (tvunref_t)dhcp_failover_state_dereference);
333                 }
334                 dhcp_failover_state_dereference (&state, MDL);
335             }
336             return ISC_R_SUCCESS;
337         }
338
339         if (!strcmp (name, "status")) {
340           if (link -> state_object) {
341             isc_result_t        status;
342
343             status = va_arg(ap, isc_result_t);
344
345             if ((status == ISC_R_HOSTUNREACH) || (status == ISC_R_TIMEDOUT)) {
346               dhcp_failover_state_reference (&state,
347                                              link -> state_object, MDL);
348               link -> state = dhcp_flink_disconnected;
349
350               /* Make the transition. */
351               dhcp_failover_state_transition (link -> state_object,
352                                               "disconnect");
353
354               /* Start trying to reconnect. */
355 #if defined (DEBUG_FAILOVER_TIMING)
356               log_info ("add_timeout +5 %s",
357                         "dhcp_failover_reconnect");
358 #endif
359               add_timeout (cur_time + 5, dhcp_failover_reconnect,
360                            state,
361                            (tvref_t)dhcp_failover_state_reference,
362                            (tvunref_t)dhcp_failover_state_dereference);
363             }
364             dhcp_failover_state_dereference (&state, MDL);
365           }
366           return ISC_R_SUCCESS;
367         }
368
369         /* Not a signal we recognize? */
370         if (strcmp (name, "ready")) {
371                 if (h -> inner && h -> inner -> type -> signal_handler)
372                         return (*(h -> inner -> type -> signal_handler))
373                                 (h -> inner, name, ap);
374                 return ISC_R_NOTFOUND;
375         }
376
377         if (!h -> outer || h -> outer -> type != omapi_type_connection)
378                 return ISC_R_INVALIDARG;
379         c = h -> outer;
380
381         /* We get here because we requested that we be woken up after
382            some number of bytes were read, and that number of bytes
383            has in fact been read. */
384         switch (link -> state) {
385               case dhcp_flink_start:
386                 link -> state = dhcp_flink_message_length_wait;
387                 if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS)
388                         break;
389               case dhcp_flink_message_length_wait:
390               next_message:
391                 link -> state = dhcp_flink_message_wait;
392                 link -> imsg = dmalloc (sizeof (failover_message_t), MDL);
393                 if (!link -> imsg) {
394                         status = ISC_R_NOMEMORY;
395                       dhcp_flink_fail:
396                         if (link -> imsg) {
397                                 failover_message_dereference (&link->imsg,
398                                                               MDL);
399                         }
400                         link -> state = dhcp_flink_disconnected;
401                         log_info ("message length wait: %s",
402                                   isc_result_totext (status));
403                         omapi_disconnect (c, 1);
404                         /* XXX just blow away the protocol state now?
405                            XXX or will disconnect blow it away? */
406                         return ISC_R_UNEXPECTED;
407                 }
408                 memset (link -> imsg, 0, sizeof (failover_message_t));
409                 link -> imsg -> refcnt = 1;
410                 /* Get the length: */
411                 omapi_connection_get_uint16 (c, &link -> imsg_len);
412                 link -> imsg_count = 0; /* Bytes read. */
413                 
414                 /* Maximum of 2048 bytes in any failover message. */
415                 if (link -> imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE) {
416                         status = ISC_R_UNEXPECTED;
417                         goto dhcp_flink_fail;
418                 }
419
420                 if ((omapi_connection_require (c, link -> imsg_len - 2U)) !=
421                     ISC_R_SUCCESS)
422                         break;
423               case dhcp_flink_message_wait:
424                 /* Read in the message.  At this point we have the
425                    entire message in the input buffer.  For each
426                    incoming value ID, set a bit in the bitmask
427                    indicating that we've gotten it.  Maybe flag an
428                    error message if the bit is already set.  Once
429                    we're done reading, we can check the bitmask to
430                    make sure that the required fields for each message
431                    have been included. */
432
433                 link -> imsg_count += 2;        /* Count the length as read. */
434
435                 /* Get message type. */
436                 omapi_connection_copyout (&link -> imsg -> type, c, 1);
437                 link -> imsg_count++;
438
439                 /* Get message payload offset. */
440                 omapi_connection_copyout (&link -> imsg_payoff, c, 1);
441                 link -> imsg_count++;
442
443                 /* Get message time. */
444                 omapi_connection_get_uint32 (c, &link -> imsg -> time);
445                 link -> imsg_count += 4;
446
447                 /* Get transaction ID. */
448                 omapi_connection_get_uint32 (c, &link -> imsg -> xid);
449                 link -> imsg_count += 4;
450
451 #if defined (DEBUG_FAILOVER_MESSAGES)
452                 log_info ("link: message %s  payoff %d  time %ld  xid %ld",
453                           dhcp_failover_message_name (link -> imsg -> type),
454                           link -> imsg_payoff,
455                           (unsigned long)link -> imsg -> time,
456                           (unsigned long)link -> imsg -> xid);
457 #endif
458                 /* Skip over any portions of the message header that we
459                    don't understand. */
460                 if (link -> imsg_payoff - link -> imsg_count) {
461                         omapi_connection_copyout ((unsigned char *)0, c,
462                                                   (link -> imsg_payoff -
463                                                    link -> imsg_count));
464                         link -> imsg_count = link -> imsg_payoff;
465                 }
466                                 
467                 /* Now start sucking options off the wire. */
468                 while (link -> imsg_count < link -> imsg_len) {
469                         status = do_a_failover_option (c, link);
470                         if (status != ISC_R_SUCCESS)
471                                 goto dhcp_flink_fail;
472                 }
473
474                 /* If it's a connect message, try to associate it with
475                    a state object. */
476                 /* XXX this should be authenticated! */
477                 if (link -> imsg -> type == FTM_CONNECT) {
478                     const char *errmsg;
479                     int reason;
480                     /* See if we can find a failover_state object that
481                        matches this connection.  This message should only
482                        be received by a secondary from a primary. */
483                     for (s = failover_states; s; s = s -> next) {
484                         if (dhcp_failover_state_match
485                             (s, (u_int8_t *)&link -> imsg -> server_addr,
486                              sizeof link -> imsg -> server_addr))
487                                 state = s;
488                     }           
489
490                     /* If we can't find a failover protocol state
491                        for this remote host, drop the connection */
492                     if (!state) {
493                             errmsg = "unknown server";
494                             reason = FTR_INVALID_PARTNER;
495
496                           badconnect:
497                                 /* XXX Send a refusal message first?
498                                    XXX Look in protocol spec for guidance. */
499                             log_error ("Failover CONNECT from %u.%u.%u.%u: %s",
500                                        ((u_int8_t *)
501                                         (&link -> imsg -> server_addr)) [0],
502                                        ((u_int8_t *)
503                                         (&link -> imsg -> server_addr)) [1],
504                                        ((u_int8_t *)
505                                         (&link -> imsg -> server_addr)) [2],
506                                        ((u_int8_t *)
507                                         (&link -> imsg -> server_addr)) [3],
508                                        errmsg);
509                             dhcp_failover_send_connectack
510                                     ((omapi_object_t *)link, state,
511                                      reason, errmsg);
512                             log_info ("failover: disconnect: %s", errmsg);
513                             omapi_disconnect (c, 0);
514                             link -> state = dhcp_flink_disconnected;
515                             return ISC_R_SUCCESS;
516                     }
517
518                     if ((cur_time > link -> imsg -> time &&
519                          cur_time - link -> imsg -> time > 60) ||
520                         (cur_time < link -> imsg -> time &&
521                          link -> imsg -> time - cur_time > 60)) {
522                             errmsg = "time offset too large";
523                             reason = FTR_TIMEMISMATCH;
524                             goto badconnect;
525                     }
526
527                     if (!(link -> imsg -> options_present & FTB_HBA) ||
528                         link -> imsg -> hba.count != 32) {
529                             errmsg = "invalid HBA";
530                             reason = FTR_HBA_CONFLICT; /* XXX */
531                             goto badconnect;
532                     }
533                     if (state -> hba)
534                             dfree (state -> hba, MDL);
535                     state -> hba = dmalloc (32, MDL);
536                     if (!state -> hba) {
537                             errmsg = "no memory";
538                             reason = FTR_MISC_REJECT;
539                             goto badconnect;
540                     }
541                     memcpy (state -> hba, link -> imsg -> hba.data, 32);
542
543                     if (!link -> state_object)
544                             dhcp_failover_state_reference
545                                     (&link -> state_object, state, MDL);
546                     if (!link -> peer_address)
547                             option_cache_reference
548                                     (&link -> peer_address,
549                                      state -> partner.address, MDL);
550                 }
551
552                 /* If we don't have a state object at this point, it's
553                    some kind of bogus situation, so just drop the
554                    connection. */
555                 if (!link -> state_object) {
556                         log_info ("failover: connect: no matching state.");
557                         omapi_disconnect (c, 1);
558                         link -> state = dhcp_flink_disconnected;
559                         return ISC_R_INVALIDARG;
560                 }
561
562                 /* Once we have the entire message, and we've validated
563                    it as best we can here, pass it to the parent. */
564                 omapi_signal ((omapi_object_t *)link -> state_object,
565                               "message", link);
566                 link -> state = dhcp_flink_message_length_wait;
567                 failover_message_dereference (&link -> imsg, MDL);
568                 /* XXX This is dangerous because we could get into a tight
569                    XXX loop reading input without servicing any other stuff.
570                    XXX There needs to be a way to relinquish control but
571                    XXX get it back immediately if there's no other work to
572                    XXX do. */
573                 if ((omapi_connection_require (c, 2)) == ISC_R_SUCCESS)
574                         goto next_message;
575                 break;
576
577               default:
578                 /* XXX should never get here.   Assertion? */
579                 break;
580         }
581         return ISC_R_SUCCESS;
582 }
583
584 static isc_result_t do_a_failover_option (c, link)
585         omapi_object_t *c;
586         dhcp_failover_link_t *link;
587 {
588         u_int16_t option_code;
589         u_int16_t option_len;
590         unsigned char *op;
591         unsigned op_size;
592         unsigned op_count;
593         int i;
594         isc_result_t status;
595         
596         if (link -> imsg_count + 2 > link -> imsg_len) {
597                 log_error ("FAILOVER: message overflow at option code.");
598                 return ISC_R_PROTOCOLERROR;
599         }
600
601         /* Get option code. */
602         omapi_connection_get_uint16 (c, &option_code);
603         link -> imsg_count += 2;
604         
605         if (link -> imsg_count + 2 > link -> imsg_len) {
606                 log_error ("FAILOVER: message overflow at length.");
607                 return ISC_R_PROTOCOLERROR;
608         }
609
610         /* Get option length. */
611         omapi_connection_get_uint16 (c, &option_len);
612         link -> imsg_count += 2;
613
614         if (link -> imsg_count + option_len > link -> imsg_len) {
615                 log_error ("FAILOVER: message overflow at data.");
616                 return ISC_R_PROTOCOLERROR;
617         }
618
619         /* If it's an unknown code, skip over it. */
620         if (option_code > FTO_MAX) {
621 #if defined (DEBUG_FAILOVER_MESSAGES)
622                 log_debug ("  option code %d (%s) len %d (not recognized)",
623                            option_code,
624                            dhcp_failover_option_name (option_code),
625                            option_len);
626 #endif
627                 omapi_connection_copyout ((unsigned char *)0, c, option_len);
628                 link -> imsg_count += option_len;
629                 return ISC_R_SUCCESS;
630         }
631
632         /* If it's the digest, do it now. */
633         if (ft_options [option_code].type == FT_DIGEST) {
634                 link -> imsg_count += option_len;
635                 if (link -> imsg_count != link -> imsg_len) {
636                         log_error ("FAILOVER: digest not at end of message");
637                         return ISC_R_PROTOCOLERROR;
638                 }
639 #if defined (DEBUG_FAILOVER_MESSAGES)
640                 log_debug ("  option %s len %d",
641                            ft_options [option_code].name, option_len);
642 #endif
643                 /* For now, just dump it. */
644                 omapi_connection_copyout ((unsigned char *)0, c, option_len);
645                 return ISC_R_SUCCESS;
646         }
647         
648         /* Only accept an option once. */
649         if (link -> imsg -> options_present & ft_options [option_code].bit) {
650                 log_error ("FAILOVER: duplicate option %s",
651                            ft_options [option_code].name);
652                 return ISC_R_PROTOCOLERROR;
653         }
654
655         /* Make sure the option is appropriate for this type of message.
656            Really, any option is generally allowed for any message, and the
657            cases where this is not true are too complicated to represent in
658            this way - what this code is doing is to just avoid saving the
659            value of an option we don't have any way to use, which allows
660            us to make the failover_message structure smaller. */
661         if (ft_options [option_code].bit &&
662             !(fto_allowed [link -> imsg -> type] &
663               ft_options [option_code].bit)) {
664                 omapi_connection_copyout ((unsigned char *)0, c, option_len);
665                 link -> imsg_count += option_len;
666                 return ISC_R_SUCCESS;
667         }               
668
669         /* Figure out how many elements, how big they are, and where
670            to store them. */
671         if (ft_options [option_code].num_present) {
672                 /* If this option takes a fixed number of elements,
673                    we expect the space for them to be preallocated,
674                    and we can just read the data in. */
675
676                 op = ((unsigned char *)link -> imsg) +
677                         ft_options [option_code].offset;
678                 op_size = ft_sizes [ft_options [option_code].type];
679                 op_count = ft_options [option_code].num_present;
680
681                 if (option_len != op_size * op_count) {
682                         log_error ("FAILOVER: option size (%d:%d), option %s",
683                                    option_len,
684                                    (ft_sizes [ft_options [option_code].type] *
685                                     ft_options [option_code].num_present),
686                                    ft_options [option_code].name);
687                         return ISC_R_PROTOCOLERROR;
688                 }
689         } else {
690                 failover_option_t *fo;
691
692                 /* FT_DDNS* are special - one or two bytes of status
693                    followed by the client FQDN. */
694                 if (ft_options [option_code].type == FT_DDNS1 ||
695                     ft_options [option_code].type == FT_DDNS1) {
696                         ddns_fqdn_t *ddns =
697                                 ((ddns_fqdn_t *)
698                                  (((char *)link -> imsg) +
699                                   ft_options [option_code].offset));
700
701                         op_count = (ft_options [option_code].type == FT_DDNS1
702                                     ? 1 : 2);
703
704                         omapi_connection_copyout (&ddns -> codes [0],
705                                                   c, op_count);
706                         link -> imsg_count += op_count;
707                         if (op_count == 1)
708                                 ddns -> codes [1] = 0;
709                         op_size = 1;
710                         op_count = option_len - op_count;
711
712                         ddns -> length = op_count;
713                         ddns -> data = dmalloc (op_count, MDL);
714                         if (!ddns -> data) {
715                                 log_error ("FAILOVER: no memory getting%s(%d)",
716                                            " DNS data ", op_count);
717
718                                 /* Actually, NO_MEMORY, but if we lose here
719                                    we have to drop the connection. */
720                                 return ISC_R_PROTOCOLERROR;
721                         }
722                         omapi_connection_copyout (ddns -> data, c, op_count);
723                         goto out;
724                 }
725
726                 /* A zero for num_present means that any number of
727                    elements can appear, so we have to figure out how
728                    many we got from the length of the option, and then
729                    fill out a failover_option structure describing the
730                    data. */
731                 op_size = ft_sizes [ft_options [option_code].type];
732
733                 /* Make sure that option data length is a multiple of the
734                    size of the data type being sent. */
735                 if (op_size > 1 && option_len % op_size) {
736                         log_error ("FAILOVER: option_len %d not %s%d",
737                                    option_len, "multiple of ", op_size);
738                         return ISC_R_PROTOCOLERROR;
739                 }
740
741                 op_count = option_len / op_size;
742                 
743                 fo = ((failover_option_t *)
744                       (((char *)link -> imsg) +
745                        ft_options [option_code].offset));
746
747                 fo -> count = op_count;
748                 fo -> data = dmalloc (option_len, MDL);
749                 if (!fo -> data) {
750                         log_error ("FAILOVER: no memory getting %s (%d)",
751                                    "option data", op_count);
752
753                         return ISC_R_PROTOCOLERROR;
754                 }                       
755                 op = fo -> data;
756         }
757
758         /* For single-byte message values and multi-byte values that
759            don't need swapping, just read them in all at once. */
760         if (op_size == 1 || ft_options [option_code].type == FT_IPADDR) {
761                 omapi_connection_copyout ((unsigned char *)op, c, option_len);
762                 link -> imsg_count += option_len;
763                 goto out;
764         }
765
766         /* For values that require swapping, read them in one at a time
767            using routines that swap bytes. */
768         for (i = 0; i < op_count; i++) {
769                 switch (ft_options [option_code].type) {
770                       case FT_UINT32:
771                         omapi_connection_get_uint32 (c, (u_int32_t *)op);
772                         op += 4;
773                         link -> imsg_count += 4;
774                         break;
775                         
776                       case FT_UINT16:
777                         omapi_connection_get_uint16 (c, (u_int16_t *)op);
778                         op += 2;
779                         link -> imsg_count += 2;
780                         break;
781                         
782                       default:
783                         /* Everything else should have been handled
784                            already. */
785                         log_error ("FAILOVER: option %s: bad type %d",
786                                    ft_options [option_code].name,
787                                    ft_options [option_code].type);
788                         return ISC_R_PROTOCOLERROR;
789                 }
790         }
791       out:
792         /* Remember that we got this option. */
793         link -> imsg -> options_present |= ft_options [option_code].bit;
794         return ISC_R_SUCCESS;
795 }
796
797 isc_result_t dhcp_failover_link_set_value (omapi_object_t *h,
798                                            omapi_object_t *id,
799                                            omapi_data_string_t *name,
800                                            omapi_typed_data_t *value)
801 {
802         if (h -> type != omapi_type_protocol)
803                 return ISC_R_INVALIDARG;
804
805         /* Never valid to set these. */
806         if (!omapi_ds_strcmp (name, "link-port") ||
807             !omapi_ds_strcmp (name, "link-name") ||
808             !omapi_ds_strcmp (name, "link-state"))
809                 return ISC_R_NOPERM;
810
811         if (h -> inner && h -> inner -> type -> set_value)
812                 return (*(h -> inner -> type -> set_value))
813                         (h -> inner, id, name, value);
814         return ISC_R_NOTFOUND;
815 }
816
817 isc_result_t dhcp_failover_link_get_value (omapi_object_t *h,
818                                            omapi_object_t *id,
819                                            omapi_data_string_t *name,
820                                            omapi_value_t **value)
821 {
822         dhcp_failover_link_t *link;
823
824         if (h -> type != omapi_type_protocol)
825                 return ISC_R_INVALIDARG;
826         link = (dhcp_failover_link_t *)h;
827         
828         if (!omapi_ds_strcmp (name, "link-port")) {
829                 return omapi_make_int_value (value, name,
830                                              (int)link -> peer_port, MDL);
831         } else if (!omapi_ds_strcmp (name, "link-state")) {
832                 if (link -> state < 0 ||
833                     link -> state >= dhcp_flink_state_max)
834                         return omapi_make_string_value (value, name,
835                                                         "invalid link state",
836                                                         MDL);
837                 return omapi_make_string_value
838                         (value, name,
839                          dhcp_flink_state_names [link -> state], MDL);
840         }
841
842         if (h -> inner && h -> inner -> type -> get_value)
843                 return (*(h -> inner -> type -> get_value))
844                         (h -> inner, id, name, value);
845         return ISC_R_NOTFOUND;
846 }
847
848 isc_result_t dhcp_failover_link_destroy (omapi_object_t *h,
849                                          const char *file, int line)
850 {
851         dhcp_failover_link_t *link;
852         if (h -> type != dhcp_type_failover_link)
853                 return ISC_R_INVALIDARG;
854         link = (dhcp_failover_link_t *)h;
855
856         if (link -> peer_address)
857                 option_cache_dereference (&link -> peer_address, file, line);
858         if (link -> imsg)
859                 failover_message_dereference (&link -> imsg, file, line);
860         if (link -> state_object)
861                 dhcp_failover_state_dereference (&link -> state_object,
862                                                  file, line);
863         return ISC_R_SUCCESS;
864 }
865
866 /* Write all the published values associated with the object through the
867    specified connection. */
868
869 isc_result_t dhcp_failover_link_stuff_values (omapi_object_t *c,
870                                               omapi_object_t *id,
871                                               omapi_object_t *l)
872 {
873         dhcp_failover_link_t *link;
874         isc_result_t status;
875
876         if (l -> type != dhcp_type_failover_link)
877                 return ISC_R_INVALIDARG;
878         link = (dhcp_failover_link_t *)l;
879         
880         status = omapi_connection_put_name (c, "link-port");
881         if (status != ISC_R_SUCCESS)
882                 return status;
883         status = omapi_connection_put_uint32 (c, sizeof (int));
884         if (status != ISC_R_SUCCESS)
885                 return status;
886         status = omapi_connection_put_uint32 (c, link -> peer_port);
887         if (status != ISC_R_SUCCESS)
888                 return status;
889         
890         status = omapi_connection_put_name (c, "link-state");
891         if (status != ISC_R_SUCCESS)
892                 return status;
893         if (link -> state < 0 ||
894             link -> state >= dhcp_flink_state_max)
895                 status = omapi_connection_put_string (c, "invalid link state");
896         else
897                 status = (omapi_connection_put_string
898                           (c, dhcp_flink_state_names [link -> state]));
899         if (status != ISC_R_SUCCESS)
900                 return status;
901
902         if (link -> inner && link -> inner -> type -> stuff_values)
903                 return (*(link -> inner -> type -> stuff_values)) (c, id,
904                                                                 link -> inner);
905         return ISC_R_SUCCESS;
906 }
907
908 /* Set up a listener for the omapi protocol.    The handle stored points to
909    a listener object, not a protocol object. */
910
911 isc_result_t dhcp_failover_listen (omapi_object_t *h)
912 {
913         isc_result_t status;
914         dhcp_failover_listener_t *obj, *l;
915         omapi_value_t *value = (omapi_value_t *)0;
916         omapi_addr_t local_addr;
917         unsigned long port;
918
919         status = omapi_get_value_str (h, (omapi_object_t *)0,
920                                       "local-port", &value);
921         if (status != ISC_R_SUCCESS)
922                 return status;
923         if (!value -> value) {
924                 omapi_value_dereference (&value, MDL);
925                 return ISC_R_INVALIDARG;
926         }
927         
928         status = omapi_get_int_value (&port, value -> value);
929         omapi_value_dereference (&value, MDL);
930         if (status != ISC_R_SUCCESS)
931                 return status;
932         local_addr.port = port;
933
934         status = omapi_get_value_str (h, (omapi_object_t *)0,
935                                       "local-address", &value);
936         if (status != ISC_R_SUCCESS)
937                 return status;
938         if (!value -> value) {
939               nogood:
940                 omapi_value_dereference (&value, MDL);
941                 return ISC_R_INVALIDARG;
942         }
943         
944         if (value -> value -> type != omapi_datatype_data ||
945             value -> value -> u.buffer.len != sizeof (struct in_addr))
946                 goto nogood;
947
948         memcpy (local_addr.address, value -> value -> u.buffer.value,
949                 value -> value -> u.buffer.len);
950         local_addr.addrlen = value -> value -> u.buffer.len;
951         local_addr.addrtype = AF_INET;
952
953         omapi_value_dereference (&value, MDL);
954
955         /* Are we already listening on this port and address? */
956         for (l = failover_listeners; l; l = l -> next) {
957                 if (l -> address.port == local_addr.port &&
958                     l -> address.addrtype == local_addr.addrtype &&
959                     l -> address.addrlen == local_addr.addrlen &&
960                     !memcmp (l -> address.address, local_addr.address,
961                              local_addr.addrlen))
962                         break;
963         }
964         /* Already listening. */
965         if (l)
966                 return ISC_R_SUCCESS;
967
968         obj = (dhcp_failover_listener_t *)0;
969         status = dhcp_failover_listener_allocate (&obj, MDL);
970         if (status != ISC_R_SUCCESS)
971                 return status;
972         obj -> address = local_addr;
973         
974         status = omapi_listen_addr ((omapi_object_t *)obj, &obj -> address, 1);
975         if (status != ISC_R_SUCCESS)
976                 return status;
977
978         status = omapi_object_reference (&h -> outer,
979                                          (omapi_object_t *)obj, MDL);
980         if (status != ISC_R_SUCCESS) {
981                 dhcp_failover_listener_dereference (&obj, MDL);
982                 return status;
983         }
984         status = omapi_object_reference (&obj -> inner, h, MDL);
985         if (status != ISC_R_SUCCESS) {
986                 dhcp_failover_listener_dereference (&obj, MDL);
987                 return status;
988         }
989
990         /* Put this listener on the list. */
991         if (failover_listeners) {
992                 dhcp_failover_listener_reference (&obj -> next,
993                                                   failover_listeners, MDL);
994                 dhcp_failover_listener_dereference (&failover_listeners, MDL);
995         }
996         dhcp_failover_listener_reference (&failover_listeners, obj, MDL);
997
998         return dhcp_failover_listener_dereference (&obj, MDL);
999 }
1000
1001 /* Signal handler for protocol listener - if we get a connect signal,
1002    create a new protocol connection, otherwise pass the signal down. */
1003
1004 isc_result_t dhcp_failover_listener_signal (omapi_object_t *o,
1005                                             const char *name, va_list ap)
1006 {
1007         isc_result_t status;
1008         omapi_connection_object_t *c;
1009         dhcp_failover_link_t *obj;
1010         dhcp_failover_listener_t *p;
1011         dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
1012
1013         if (!o || o -> type != dhcp_type_failover_listener)
1014                 return ISC_R_INVALIDARG;
1015         p = (dhcp_failover_listener_t *)o;
1016
1017         /* Not a signal we recognize? */
1018         if (strcmp (name, "connect")) {
1019                 if (p -> inner && p -> inner -> type -> signal_handler)
1020                         return (*(p -> inner -> type -> signal_handler))
1021                                 (p -> inner, name, ap);
1022                 return ISC_R_NOTFOUND;
1023         }
1024
1025         c = va_arg (ap, omapi_connection_object_t *);
1026         if (!c || c -> type != omapi_type_connection)
1027                 return ISC_R_INVALIDARG;
1028
1029         /* See if we can find a failover_state object that
1030            matches this connection. */
1031         for (s = failover_states; s; s = s -> next) {
1032                 if (dhcp_failover_state_match
1033                     (s, (u_int8_t *)&c -> remote_addr.sin_addr,
1034                     sizeof c -> remote_addr.sin_addr)) {
1035                         state = s;
1036                         break;
1037                 }
1038         }               
1039         if (!state) {
1040                 log_info ("failover: listener: no matching state");
1041                 return omapi_disconnect ((omapi_object_t *)c, 1);
1042         }
1043
1044         obj = (dhcp_failover_link_t *)0;
1045         status = dhcp_failover_link_allocate (&obj, MDL);
1046         if (status != ISC_R_SUCCESS)
1047                 return status;
1048         obj -> peer_port = ntohs (c -> remote_addr.sin_port);
1049
1050         status = omapi_object_reference (&obj -> outer,
1051                                          (omapi_object_t *)c, MDL);
1052         if (status != ISC_R_SUCCESS) {
1053               lose:
1054                 dhcp_failover_link_dereference (&obj, MDL);
1055                 log_info ("failover: listener: picayune failure.");
1056                 omapi_disconnect ((omapi_object_t *)c, 1);
1057                 return status;
1058         }
1059
1060         status = omapi_object_reference (&c -> inner,
1061                                          (omapi_object_t *)obj, MDL);
1062         if (status != ISC_R_SUCCESS)
1063                 goto lose;
1064
1065         status = dhcp_failover_state_reference (&obj -> state_object,
1066                                                 state, MDL);
1067         if (status != ISC_R_SUCCESS)
1068                 goto lose;
1069
1070         omapi_signal_in ((omapi_object_t *)obj, "connect");
1071
1072         return dhcp_failover_link_dereference (&obj, MDL);
1073 }
1074
1075 isc_result_t dhcp_failover_listener_set_value (omapi_object_t *h,
1076                                                 omapi_object_t *id,
1077                                                 omapi_data_string_t *name,
1078                                                 omapi_typed_data_t *value)
1079 {
1080         if (h -> type != dhcp_type_failover_listener)
1081                 return ISC_R_INVALIDARG;
1082         
1083         if (h -> inner && h -> inner -> type -> set_value)
1084                 return (*(h -> inner -> type -> set_value))
1085                         (h -> inner, id, name, value);
1086         return ISC_R_NOTFOUND;
1087 }
1088
1089 isc_result_t dhcp_failover_listener_get_value (omapi_object_t *h,
1090                                                 omapi_object_t *id,
1091                                                 omapi_data_string_t *name,
1092                                                 omapi_value_t **value)
1093 {
1094         if (h -> type != dhcp_type_failover_listener)
1095                 return ISC_R_INVALIDARG;
1096         
1097         if (h -> inner && h -> inner -> type -> get_value)
1098                 return (*(h -> inner -> type -> get_value))
1099                         (h -> inner, id, name, value);
1100         return ISC_R_NOTFOUND;
1101 }
1102
1103 isc_result_t dhcp_failover_listener_destroy (omapi_object_t *h,
1104                                               const char *file, int line)
1105 {
1106         dhcp_failover_listener_t *l;
1107
1108         if (h -> type != dhcp_type_failover_listener)
1109                 return ISC_R_INVALIDARG;
1110         l = (dhcp_failover_listener_t *)h;
1111         if (l -> next)
1112                 dhcp_failover_listener_dereference (&l -> next, file, line);
1113
1114         return ISC_R_SUCCESS;
1115 }
1116
1117 /* Write all the published values associated with the object through the
1118    specified connection. */
1119
1120 isc_result_t dhcp_failover_listener_stuff (omapi_object_t *c,
1121                                            omapi_object_t *id,
1122                                            omapi_object_t *p)
1123 {
1124         int i;
1125
1126         if (p -> type != dhcp_type_failover_listener)
1127                 return ISC_R_INVALIDARG;
1128
1129         if (p -> inner && p -> inner -> type -> stuff_values)
1130                 return (*(p -> inner -> type -> stuff_values)) (c, id,
1131                                                                 p -> inner);
1132         return ISC_R_SUCCESS;
1133 }
1134
1135 /* Set up master state machine for the failover protocol. */
1136
1137 isc_result_t dhcp_failover_register (omapi_object_t *h)
1138 {
1139         isc_result_t status;
1140         dhcp_failover_state_t *obj;
1141         unsigned long port;
1142         omapi_value_t *value = (omapi_value_t *)0;
1143
1144         status = omapi_get_value_str (h, (omapi_object_t *)0,
1145                                       "local-port", &value);
1146         if (status != ISC_R_SUCCESS)
1147                 return status;
1148         if (!value -> value) {
1149                 omapi_value_dereference (&value, MDL);
1150                 return ISC_R_INVALIDARG;
1151         }
1152         
1153         status = omapi_get_int_value (&port, value -> value);
1154         omapi_value_dereference (&value, MDL);
1155         if (status != ISC_R_SUCCESS)
1156                 return status;
1157
1158         obj = (dhcp_failover_state_t *)0;
1159         dhcp_failover_state_allocate (&obj, MDL);
1160         obj -> me.port = port;
1161         
1162         status = omapi_listen ((omapi_object_t *)obj, port, 1);
1163         if (status != ISC_R_SUCCESS) {
1164                 dhcp_failover_state_dereference (&obj, MDL);
1165                 return status;
1166         }
1167
1168         status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj,
1169                                          MDL);
1170         if (status != ISC_R_SUCCESS) {
1171                 dhcp_failover_state_dereference (&obj, MDL);
1172                 return status;
1173         }
1174         status = omapi_object_reference (&obj -> inner, h, MDL);
1175         dhcp_failover_state_dereference (&obj, MDL);
1176         return status;
1177 }
1178
1179 /* Signal handler for protocol state machine. */
1180
1181 isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
1182                                          const char *name, va_list ap)
1183 {
1184         isc_result_t status;
1185         omapi_connection_object_t *c;
1186         omapi_protocol_object_t *obj;
1187         dhcp_failover_state_t *state;
1188         dhcp_failover_link_t *link;
1189         char *peer_name;
1190
1191         if (!o || o -> type != dhcp_type_failover_state)
1192                 return ISC_R_INVALIDARG;
1193         state = (dhcp_failover_state_t *)o;
1194
1195         /* Not a signal we recognize? */
1196         if (strcmp (name, "disconnect") &&
1197             strcmp (name, "message")) {
1198                 if (state -> inner && state -> inner -> type -> signal_handler)
1199                         return (*(state -> inner -> type -> signal_handler))
1200                                 (state -> inner, name, ap);
1201                 return ISC_R_NOTFOUND;
1202         }
1203
1204         /* Handle connect signals by seeing what state we're in
1205            and potentially doing a state transition. */
1206         if (!strcmp (name, "disconnect")) {
1207                 link = va_arg (ap, dhcp_failover_link_t *);
1208
1209                 dhcp_failover_link_dereference (&state -> link_to_peer, MDL);
1210                 dhcp_failover_state_transition (state, "disconnect");
1211                 if (state -> i_am == primary) {
1212 #if defined (DEBUG_FAILOVER_TIMING)
1213                         log_info ("add_timeout +90 %s",
1214                                   "dhcp_failover_reconnect");
1215 #endif
1216                         add_timeout (cur_time + 90, dhcp_failover_reconnect,
1217                                      state,
1218                                      (tvref_t)dhcp_failover_state_reference,
1219                                      (tvunref_t)
1220                                      dhcp_failover_state_dereference);
1221                 }
1222         } else if (!strcmp (name, "message")) {
1223                 link = va_arg (ap, dhcp_failover_link_t *);
1224
1225                 if (link -> imsg -> type == FTM_CONNECT) {
1226                         /* If we already have a link to the peer, it must be
1227                            dead, so drop it.
1228                            XXX Is this the right thing to do?
1229                            XXX Probably not - what if both peers start at
1230                            XXX the same time? */
1231                         if (state -> link_to_peer) {
1232                                 dhcp_failover_send_connectack
1233                                         ((omapi_object_t *)link, state,
1234                                          FTR_DUP_CONNECTION,
1235                                          "already connected");
1236                                 omapi_disconnect (link -> outer, 1);
1237                                 return ISC_R_SUCCESS;
1238                         }
1239                         if (!(link -> imsg -> options_present & FTB_MCLT)) {
1240                                 dhcp_failover_send_connectack
1241                                         ((omapi_object_t *)link, state,
1242                                          FTR_INVALID_MCLT,
1243                                          "no MCLT provided");
1244                                 omapi_disconnect (link -> outer, 1);
1245                                 return ISC_R_SUCCESS;
1246                         }                               
1247
1248                         dhcp_failover_link_reference (&state -> link_to_peer,
1249                                                       link, MDL);
1250                         status = (dhcp_failover_send_connectack
1251                                   ((omapi_object_t *)link, state, 0, 0));
1252                         if (status != ISC_R_SUCCESS) {
1253                                 dhcp_failover_link_dereference
1254                                         (&state -> link_to_peer, MDL);
1255                                 log_info ("dhcp_failover_send_connectack: %s",
1256                                           isc_result_totext (status));
1257                                 omapi_disconnect (link -> outer, 1);
1258                                 return ISC_R_SUCCESS;
1259                         }
1260                         if (link -> imsg -> options_present & FTB_MAX_UNACKED)
1261                                 state -> partner.max_flying_updates =
1262                                         link -> imsg -> max_unacked;
1263                         if (link -> imsg -> options_present &
1264                             FTB_RECEIVE_TIMER)
1265                                 state -> partner.max_response_delay =
1266                                         link -> imsg -> receive_timer;
1267                         state -> mclt = link -> imsg -> mclt;
1268                         dhcp_failover_send_state (state);
1269                         cancel_timeout (dhcp_failover_link_startup_timeout,
1270                                         link);
1271                 } else if (link -> imsg -> type == FTM_CONNECTACK) {
1272                     const char *errmsg;
1273                     int reason;
1274
1275                     cancel_timeout (dhcp_failover_link_startup_timeout,
1276                                     link);
1277
1278                     if (link -> imsg -> reject_reason) {
1279                         log_error ("Failover CONNECT to %u.%u.%u.%u%s%s",
1280                                    ((u_int8_t *)
1281                                     (&link -> imsg -> server_addr)) [0],
1282                                    ((u_int8_t *)
1283                                     (&link -> imsg -> server_addr)) [1],
1284                                    ((u_int8_t *)
1285                                     (&link -> imsg -> server_addr)) [2],
1286                                    ((u_int8_t *)
1287                                     (&link -> imsg -> server_addr)) [3],
1288                                    " rejected: ",
1289                                    (dhcp_failover_reject_reason_print
1290                                     (link -> imsg -> reject_reason)));
1291                         /* XXX print message from peer if peer sent message. */
1292                         omapi_disconnect (link -> outer, 1);
1293                         return ISC_R_SUCCESS;
1294                     }
1295                                       
1296                     if (!dhcp_failover_state_match
1297                         (state,
1298                          (u_int8_t *)&link -> imsg -> server_addr,
1299                          sizeof link -> imsg -> server_addr)) {
1300                         errmsg = "unknown server";
1301                         reason = FTR_INVALID_PARTNER;
1302                       badconnectack:
1303                         log_error ("Failover CONNECTACK from %u.%u.%u.%u: %s",
1304                                    ((u_int8_t *)
1305                                     (&link -> imsg -> server_addr)) [0],
1306                                    ((u_int8_t *)
1307                                     (&link -> imsg -> server_addr)) [1],
1308                                    ((u_int8_t *)
1309                                     (&link -> imsg -> server_addr)) [2],
1310                                    ((u_int8_t *)
1311                                     (&link -> imsg -> server_addr)) [3],
1312                                    errmsg);
1313                         dhcp_failover_send_disconnect ((omapi_object_t *)link,
1314                                                        reason, errmsg);
1315                         omapi_disconnect (link -> outer, 0);
1316                         return ISC_R_SUCCESS;
1317                     }
1318
1319                     if (state -> link_to_peer) {
1320                         errmsg = "already connected";
1321                         reason = FTR_DUP_CONNECTION;
1322                         goto badconnectack;
1323                     }
1324
1325                     if ((cur_time > link -> imsg -> time &&
1326                          cur_time - link -> imsg -> time > 60) ||
1327                         (cur_time < link -> imsg -> time &&
1328                          link -> imsg -> time - cur_time > 60)) {
1329                             errmsg = "time offset too large";
1330                             reason = FTR_TIMEMISMATCH;
1331                             goto badconnectack;
1332                     }
1333
1334                     dhcp_failover_link_reference (&state -> link_to_peer,
1335                                                   link, MDL);
1336 #if 0
1337                     /* XXX This is probably the right thing to do, but
1338                        XXX for release three, to make the smallest possible
1339                        XXX change, we are doing this when the peer state
1340                        XXX changes instead. */
1341                     if (state -> me.state == startup)
1342                             dhcp_failover_set_state (state,
1343                                                      state -> saved_state);
1344                     else
1345 #endif
1346                             dhcp_failover_send_state (state);
1347
1348                     if (link -> imsg -> options_present & FTB_MAX_UNACKED)
1349                             state -> partner.max_flying_updates =
1350                                     link -> imsg -> max_unacked;
1351                     if (link -> imsg -> options_present & FTB_RECEIVE_TIMER)
1352                             state -> partner.max_response_delay =
1353                                     link -> imsg -> receive_timer;
1354 #if defined (DEBUG_FAILOVER_TIMING)
1355                     log_info ("add_timeout +%d %s",
1356                               (int)state -> partner.max_response_delay / 3,
1357                               "dhcp_failover_send_contact");
1358 #endif
1359                     add_timeout (cur_time +
1360                                  (int)state -> partner.max_response_delay / 3,
1361                                  dhcp_failover_send_contact, state,
1362                                  (tvref_t)dhcp_failover_state_reference,
1363                                  (tvunref_t)dhcp_failover_state_dereference);
1364 #if defined (DEBUG_FAILOVER_TIMING)
1365                     log_info ("add_timeout +%d %s",
1366                               (int)state -> me.max_response_delay,
1367                               "dhcp_failover_timeout");
1368 #endif
1369                     add_timeout (cur_time +
1370                                  (int)state -> me.max_response_delay,
1371                                  dhcp_failover_timeout, state,
1372                                  (tvref_t)dhcp_failover_state_reference,
1373                                  (tvunref_t)dhcp_failover_state_dereference);
1374                 } else if (link -> imsg -> type == FTM_DISCONNECT) {
1375                     if (link -> imsg -> reject_reason) {
1376                         log_error ("Failover DISCONNECT from %u.%u.%u.%u%s%s",
1377                                    ((u_int8_t *)
1378                                     (&link -> imsg -> server_addr)) [0],
1379                                    ((u_int8_t *)
1380                                     (&link -> imsg -> server_addr)) [1],
1381                                    ((u_int8_t *)
1382                                     (&link -> imsg -> server_addr)) [2],
1383                                    ((u_int8_t *)
1384                                     (&link -> imsg -> server_addr)) [3],
1385                                    ": ",
1386                                    (dhcp_failover_reject_reason_print
1387                                     (link -> imsg -> reject_reason)));
1388                     }
1389                     omapi_disconnect (link -> outer, 1);
1390                 } else if (link -> imsg -> type == FTM_BNDUPD) {
1391                         dhcp_failover_process_bind_update (state,
1392                                                            link -> imsg);
1393                 } else if (link -> imsg -> type == FTM_BNDACK) {
1394                         dhcp_failover_process_bind_ack (state, link -> imsg);
1395                 } else if (link -> imsg -> type == FTM_UPDREQ) {
1396                         dhcp_failover_process_update_request (state,
1397                                                               link -> imsg);
1398                 } else if (link -> imsg -> type == FTM_UPDREQALL) {
1399                         dhcp_failover_process_update_request_all
1400                                 (state, link -> imsg);
1401                 } else if (link -> imsg -> type == FTM_UPDDONE) {
1402                         dhcp_failover_process_update_done (state,
1403                                                            link -> imsg);
1404                 } else if (link -> imsg -> type == FTM_POOLREQ) {
1405                         dhcp_failover_pool_rebalance (state);
1406                 } else if (link -> imsg -> type == FTM_POOLRESP) {
1407                         log_info ("pool response: %ld leases",
1408                                   (unsigned long)
1409                                   link -> imsg -> addresses_transferred);
1410                 } else if (link -> imsg -> type == FTM_STATE) {
1411                         dhcp_failover_peer_state_changed (state,
1412                                                           link -> imsg);
1413                 }
1414
1415                 /* Add a timeout so that if the partner doesn't send
1416                    another message for the maximum transmit idle time
1417                    plus a grace of one second, we close the
1418                    connection. */
1419                 if (state -> link_to_peer &&
1420                     state -> link_to_peer == link &&
1421                     state -> link_to_peer -> state != dhcp_flink_disconnected)
1422                 {
1423 #if defined (DEBUG_FAILOVER_TIMING)
1424                     log_info ("add_timeout +%d %s",
1425                               (int)state -> me.max_response_delay,
1426                               "dhcp_failover_timeout");
1427 #endif
1428                     add_timeout (cur_time +
1429                                  (int)state -> me.max_response_delay,
1430                                  dhcp_failover_timeout, state,
1431                                  (tvref_t)dhcp_failover_state_reference,
1432                                  (tvunref_t)dhcp_failover_state_dereference);
1433
1434                 }
1435         }
1436
1437         /* Handle all the events we care about... */
1438         return ISC_R_SUCCESS;
1439 }
1440
1441 isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state,
1442                                              const char *name)
1443 {
1444         isc_result_t status;
1445
1446         /* XXX Check these state transitions against the spec! */
1447         if (!strcmp (name, "disconnect")) {
1448                 if (state -> link_to_peer) {
1449                     log_info ("peer %s: disconnected", state -> name);
1450                     if (state -> link_to_peer -> state_object)
1451                         dhcp_failover_state_dereference
1452                                 (&state -> link_to_peer -> state_object, MDL);
1453                     dhcp_failover_link_dereference (&state -> link_to_peer,
1454                                                     MDL);
1455                 }
1456                 cancel_timeout (dhcp_failover_send_contact, state);
1457                 cancel_timeout (dhcp_failover_timeout, state);
1458                 cancel_timeout (dhcp_failover_startup_timeout, state);
1459
1460                 switch (state -> me.state == startup ?
1461                         state -> saved_state : state -> me.state) {
1462                       case resolution_interrupted:
1463                       case partner_down:
1464                       case communications_interrupted:
1465                       case recover:
1466                         /* Already in the right state? */
1467                         if (state -> me.state == startup)
1468                                 return (dhcp_failover_set_state
1469                                         (state, state -> saved_state));
1470                         return ISC_R_SUCCESS;
1471                 
1472                       case potential_conflict:
1473                         return dhcp_failover_set_state
1474                                 (state, resolution_interrupted);
1475                                 
1476                       case normal:
1477                         return dhcp_failover_set_state
1478                                 (state, communications_interrupted);
1479
1480                       case unknown_state:
1481                         return dhcp_failover_set_state
1482                                 (state, resolution_interrupted);
1483                       case startup:
1484                         break;  /* can't happen. */
1485                 }
1486         } else if (!strcmp (name, "connect")) {
1487                 switch (state -> me.state) {
1488                       case communications_interrupted:
1489                         status = dhcp_failover_set_state (state, normal);
1490                         dhcp_failover_send_updates (state);
1491                         return status;
1492
1493                       case resolution_interrupted:
1494                         return dhcp_failover_set_state (state,
1495                                                         potential_conflict);
1496
1497                       case partner_down:
1498                       case potential_conflict:
1499                       case normal:
1500                       case recover:
1501                       case shut_down:
1502                       case paused:
1503                       case unknown_state:
1504                       case recover_done:
1505                       case startup:
1506                       case recover_wait:
1507                         return dhcp_failover_send_state (state);
1508                 }
1509         } else if (!strcmp (name, "startup")) {
1510                 dhcp_failover_set_state (state, startup);
1511                 return ISC_R_SUCCESS;
1512         } else if (!strcmp (name, "connect-timeout")) {
1513                 switch (state -> me.state) {
1514                       case communications_interrupted:
1515                       case partner_down:
1516                       case resolution_interrupted:
1517                         return ISC_R_SUCCESS;
1518
1519                       case normal:
1520                       case recover:
1521                         return dhcp_failover_set_state
1522                                 (state, communications_interrupted);
1523
1524                       case potential_conflict:
1525                         return dhcp_failover_set_state
1526                                 (state, resolution_interrupted);
1527
1528                       case unknown_state:
1529                         return dhcp_failover_set_state
1530                                 (state, communications_interrupted);
1531
1532                       default:
1533                         return dhcp_failover_set_state
1534                                 (state, resolution_interrupted);
1535                 }
1536         }
1537         return ISC_R_INVALIDARG;
1538 }
1539
1540 isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state)
1541 {
1542         switch (state -> me.state) {
1543               case unknown_state:
1544                 state -> service_state = not_responding;
1545                 state -> nrr = " (my state unknown)";
1546                 break;
1547
1548               case partner_down:
1549                 state -> service_state = service_partner_down;
1550                 state -> nrr = "";
1551                 break;
1552
1553               case normal:
1554                 state -> service_state = cooperating;
1555                 state -> nrr = "";
1556                 break;
1557
1558               case communications_interrupted:
1559                 state -> service_state = not_cooperating;
1560                 state -> nrr = "";
1561                 break;
1562
1563               case resolution_interrupted:
1564               case potential_conflict:
1565                 state -> service_state = not_responding;
1566                 state -> nrr = " (resolving conflicts)";
1567                 break;
1568
1569               case recover:
1570                 state -> service_state = not_responding;
1571                 state -> nrr = " (recovering)";
1572                 break;
1573
1574               case shut_down:
1575                 state -> service_state = not_responding;
1576                 state -> nrr = " (shut down)";
1577                 break;
1578
1579               case paused:
1580                 state -> service_state = not_responding;
1581                 state -> nrr = " (paused)";
1582                 break;
1583
1584               case recover_wait:
1585                 state -> service_state = not_responding;
1586                 state -> nrr = " (recover wait)";
1587                 break;
1588
1589               case recover_done:
1590                 state -> service_state = not_responding;
1591                 state -> nrr = " (recover done)";
1592                 break;
1593
1594               case startup:
1595                 state -> service_state = service_startup;
1596                 state -> nrr = " (startup)";
1597                 break;
1598         }
1599
1600         /* Some peer states can require us not to respond, even if our
1601            state doesn't. */
1602         /* XXX hm.   I suspect this isn't true anymore. */
1603         if (state -> service_state != not_responding) {
1604                 switch (state -> partner.state) {
1605                       case partner_down:
1606                         state -> service_state = not_responding;
1607                         state -> nrr = " (recovering)";
1608                         break;
1609
1610                       case potential_conflict:
1611                         state -> service_state = not_responding;
1612                         state -> nrr = " (resolving conflicts)";
1613                         break;
1614
1615                         /* Other peer states don't affect our behaviour. */
1616                       default:
1617                         break;
1618                 }
1619         }
1620
1621         return ISC_R_SUCCESS;
1622 }
1623
1624 isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state,
1625                                       enum failover_state new_state)
1626 {
1627     enum failover_state saved_state;
1628     TIME saved_stos;
1629     struct pool *p;
1630     struct shared_network *s;
1631     struct lease *l;
1632
1633     /* First make the transition out of the current state. */
1634     switch (state -> me.state) {
1635       case normal:
1636         /* Any updates that haven't been acked yet, we have to
1637            resend, just in case. */
1638         if (state -> ack_queue_tail) {
1639             struct lease *lp;
1640                 
1641             /* Zap the flags. */
1642             for (lp = state -> ack_queue_head; lp; lp = lp -> next_pending)
1643                     lp -> flags = ((lp -> flags & ~ON_ACK_QUEUE) |
1644                                    ON_UPDATE_QUEUE);
1645                 
1646             /* Now hook the ack queue to the beginning of the update
1647                queue. */
1648             if (state -> update_queue_head) {
1649                 lease_reference (&state -> ack_queue_tail -> next_pending,
1650                                  state -> update_queue_head, MDL);
1651                 lease_dereference (&state -> update_queue_head, MDL);
1652             }
1653             lease_reference (&state -> update_queue_head,
1654                              state -> ack_queue_head, MDL);
1655             if (!state -> update_queue_tail) {
1656 #if defined (POINTER_DEBUG)
1657                 if (state -> ack_queue_tail -> next_pending) {
1658                     log_error ("next pending on ack queue tail.");
1659                     abort ();
1660                 }
1661 #endif
1662                 lease_reference (&state -> update_queue_tail,
1663                                  state -> ack_queue_tail, MDL);
1664             }
1665             lease_dereference (&state -> ack_queue_tail, MDL);
1666             lease_dereference (&state -> ack_queue_head, MDL);
1667             state -> cur_unacked_updates = 0;
1668         }
1669         cancel_timeout (dhcp_failover_keepalive, state);
1670         break;
1671         
1672       case recover:
1673       case recover_wait:
1674       case recover_done:
1675       case potential_conflict:
1676       case partner_down:
1677       case communications_interrupted:
1678       case resolution_interrupted:
1679       case startup:
1680       default:
1681         break;
1682     }
1683
1684     /* Tentatively make the transition. */
1685     saved_state = state -> me.state;
1686     saved_stos = state -> me.stos;
1687
1688     /* Keep the old stos if we're going into recover_wait or if we're
1689        coming into or out of startup. */
1690     if (new_state != recover_wait && new_state != startup &&
1691         saved_state != startup)
1692             state -> me.stos = cur_time;
1693
1694     /* If we're in shutdown, peer is in partner_down, and we're moving
1695        to recover, we can skip waiting for MCLT to expire.    This happens
1696        when a server is moved administratively into shutdown prior to
1697        actually shutting down.   Of course, if there are any updates
1698        pending we can't actually do this. */
1699     if (new_state == recover && saved_state == shut_down &&
1700         state -> partner.state == partner_down &&
1701         !state -> update_queue_head && !state -> ack_queue_head)
1702             state -> me.stos = cur_time - state -> mclt;
1703
1704     state -> me.state = new_state;
1705     if (new_state == startup && saved_state != startup)
1706         state -> saved_state = saved_state;
1707
1708     /* If we can't record the new state, we can't make a state transition. */
1709     if (!write_failover_state (state) || !commit_leases ()) {
1710             log_error ("Unable to record current failover state for %s",
1711                        state -> name);
1712             state -> me.state = saved_state;
1713             state -> me.stos = saved_stos;
1714             return ISC_R_IOERROR;
1715     }
1716
1717     log_info ("failover peer %s: I move from %s to %s",
1718               state -> name, dhcp_failover_state_name_print (saved_state),
1719               dhcp_failover_state_name_print (state -> me.state));
1720     
1721     /* If we were in startup and we just left it, cancel the timeout. */
1722     if (new_state != startup && saved_state == startup)
1723         cancel_timeout (dhcp_failover_startup_timeout, state);
1724
1725     /* Set our service state. */
1726     dhcp_failover_set_service_state (state);
1727
1728     /* Tell the peer about it. */
1729     if (state -> link_to_peer)
1730             dhcp_failover_send_state (state);
1731
1732     switch (new_state) {
1733           case normal:
1734             if (state -> partner.state == normal)
1735                     dhcp_failover_state_pool_check (state);
1736             break;
1737             
1738           case potential_conflict:
1739             if (state -> i_am == primary)
1740                     dhcp_failover_send_update_request (state);
1741             break;
1742             
1743           case startup:
1744 #if defined (DEBUG_FAILOVER_TIMING)
1745             log_info ("add_timeout +15 %s",
1746                       "dhcp_failover_startup_timeout");
1747 #endif
1748             add_timeout (cur_time + 15,
1749                          dhcp_failover_startup_timeout,
1750                          state,
1751                          (tvref_t)omapi_object_reference,
1752                          (tvunref_t)
1753                          omapi_object_dereference);
1754             break;
1755             
1756             /* If we come back in recover_wait and there's still waiting
1757                to do, set a timeout. */
1758           case recover_wait:
1759             if (state -> me.stos + state -> mclt > cur_time) {
1760 #if defined (DEBUG_FAILOVER_TIMING)
1761                     log_info ("add_timeout +%d %s",
1762                               (int)(cur_time -
1763                                     state -> me.stos + state -> mclt),
1764                               "dhcp_failover_startup_timeout");
1765 #endif
1766                     add_timeout ((int)(state -> me.stos + state -> mclt),
1767                                  dhcp_failover_recover_done,
1768                                  state,
1769                                  (tvref_t)omapi_object_reference,
1770                                  (tvunref_t)
1771                                  omapi_object_dereference);
1772             } else
1773                     dhcp_failover_recover_done (state);
1774             break;
1775             
1776           case recover:
1777             if (state -> link_to_peer)
1778                     dhcp_failover_send_update_request_all (state);
1779             break;
1780
1781           case partner_down:
1782             /* For every expired lease, set a timeout for it to become free. */
1783             for (s = shared_networks; s; s = s -> next) {
1784                 for (p = s -> pools; p; p = p -> next) {
1785                     if (p -> failover_peer == state) {
1786                         for (l = p -> expired; l; l = l -> next)
1787                             l -> tsfp = state -> me.stos + state -> mclt;
1788                         if (p -> next_event_time >
1789                             state -> me.stos + state -> mclt) {
1790                             p -> next_event_time =
1791                                         state -> me.stos + state -> mclt;
1792 #if defined (DEBUG_FAILOVER_TIMING)
1793                             log_info ("add_timeout +%d %s",
1794                                       (int)(cur_time - p -> next_event_time),
1795                                       "pool_timer");
1796 #endif
1797                             add_timeout (p -> next_event_time, pool_timer, p,
1798                                          (tvref_t)pool_reference,
1799                                          (tvunref_t)pool_dereference);
1800                         }
1801                     }
1802                 }
1803             }
1804             break;
1805                                 
1806
1807           default:
1808             break;
1809     }
1810
1811     return ISC_R_SUCCESS;
1812 }
1813
1814 isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state,
1815                                                failover_message_t *msg)
1816 {
1817         enum failover_state previous_state = state -> partner.state;
1818         enum failover_state new_state;
1819         int startupp;
1820         isc_result_t status;
1821
1822         new_state = msg -> server_state;
1823         startupp = (msg -> server_flags & FTF_STARTUP) ? 1 : 0;
1824
1825         if (state -> partner.state == new_state && state -> me.state) {
1826                 switch (state -> me.state) {
1827                       case startup:
1828                         dhcp_failover_set_state (state, state -> saved_state);
1829                         return ISC_R_SUCCESS;
1830
1831                       case unknown_state:
1832                       case normal:
1833                       case potential_conflict:
1834                       case recover_done:
1835                       case shut_down:
1836                       case paused:
1837                       case recover_wait:
1838                         return ISC_R_SUCCESS;
1839
1840                         /* If we get a peer state change when we're
1841                            disconnected, we always process it. */
1842                       case partner_down:
1843                       case communications_interrupted:
1844                       case resolution_interrupted:
1845                       case recover:
1846                         break;
1847                 }
1848         }
1849
1850         state -> partner.state = new_state;
1851
1852         log_info ("failover peer %s: peer moves from %s to %s",
1853                   state -> name,
1854                   dhcp_failover_state_name_print (previous_state),
1855                   dhcp_failover_state_name_print (state -> partner.state));
1856     
1857         if (!write_failover_state (state) || !commit_leases ()) {
1858                 /* This is bad, but it's not fatal.  Of course, if we
1859                    can't write to the lease database, we're not going to
1860                    get much done anyway. */
1861                 log_error ("Unable to record current failover state for %s",
1862                            state -> name);
1863         }
1864
1865         /* Do any state transitions that are required as a result of the
1866            peer's state transition. */
1867
1868         switch (state -> me.state == startup ?
1869                 state -> saved_state : state -> me.state) {
1870               case startup: /* can't happen. */
1871                 break;
1872
1873               case normal:
1874                 switch (new_state) {
1875                       case normal:
1876                         dhcp_failover_state_pool_check (state);
1877                         break;
1878
1879                       case communications_interrupted:
1880                         break;
1881
1882                       case partner_down:
1883                         if (state -> me.state == startup)
1884                                 dhcp_failover_set_state (state, recover);
1885                         else
1886                                 dhcp_failover_set_state (state,
1887                                                          potential_conflict);
1888                         break;
1889
1890                       case potential_conflict:
1891                       case resolution_interrupted:
1892                         /* None of these transitions should ever occur. */
1893                         dhcp_failover_set_state (state, shut_down);
1894                         break;
1895                            
1896                       case recover:
1897                         dhcp_failover_set_state (state, partner_down);
1898                         break;
1899
1900                       case shut_down:
1901                         /* XXX This one is specified, but it's specified in
1902                            XXX the documentation for the shut_down state,
1903                            XXX not the normal state. */
1904                         dhcp_failover_set_state (state, partner_down);
1905                         break;
1906
1907                       case paused:
1908                         dhcp_failover_set_state (state,
1909                                                  communications_interrupted);
1910                         break;
1911
1912                       case recover_wait:
1913                       case recover_done:
1914                         /* We probably don't need to do anything here. */
1915                         break;
1916
1917                       case unknown_state:
1918                       case startup:
1919                         break;
1920                 }
1921                 break;
1922
1923               case recover:
1924                 switch (new_state) {
1925                       case recover:
1926                         log_info ("failover peer %s: requesting %s",
1927                                   state -> name, "full update from peer");
1928                         /* Don't send updreqall if we're really in the
1929                            startup state, because that will result in two
1930                            being sent. */
1931                         if (state -> me.state == recover)
1932                                 dhcp_failover_send_update_request_all (state);
1933                         break;
1934
1935                       case potential_conflict:
1936                       case resolution_interrupted:
1937                       case normal:
1938                         dhcp_failover_set_state (state, potential_conflict);
1939                         break;
1940
1941                       case partner_down:
1942                       case communications_interrupted:
1943                         /* We're supposed to send an update request at this
1944                            point. */
1945                         /* XXX we don't currently have code here to do any
1946                            XXX clever detection of when we should send an
1947                            XXX UPDREQALL message rather than an UPDREQ
1948                            XXX message.   What to do, what to do? */
1949                         dhcp_failover_send_update_request (state);
1950                         break;
1951
1952                       case shut_down:
1953                         /* XXX We're not explicitly told what to do in this
1954                            XXX case, but this transition is consistent with
1955                            XXX what is elsewhere in the draft. */
1956                         dhcp_failover_set_state (state, partner_down);
1957                         break;
1958
1959                         /* We can't really do anything in this case. */
1960                       case paused:
1961                         break;
1962
1963                         /* We should have asked for an update already. */
1964                       case recover_done:
1965                       case recover_wait:
1966                         break;
1967
1968                       case unknown_state:
1969                       case startup:
1970                         break;
1971                 }
1972                 break;
1973
1974               case potential_conflict:
1975                 switch (new_state) {
1976                       case normal:
1977                         if (previous_state == potential_conflict &&
1978                             state -> i_am == secondary)
1979                                 dhcp_failover_send_update_request (state);
1980                         break;
1981
1982                       case recover_done:
1983                       case recover_wait:
1984                       case potential_conflict:
1985                       case partner_down:
1986                       case communications_interrupted:
1987                       case resolution_interrupted:
1988                       case paused:
1989                         break;
1990
1991                       case recover:
1992                         dhcp_failover_set_state (state, recover);
1993                         break;
1994
1995                       case shut_down:
1996                         dhcp_failover_set_state (state, partner_down);
1997                         break;
1998
1999                       case unknown_state:
2000                       case startup:
2001                         break;
2002                 }
2003                 break;
2004
2005               case partner_down:
2006                 /* Take no action if other server is starting up. */
2007                 if (startupp)
2008                         break;
2009
2010                 switch (new_state) {
2011                         /* This is where we should be. */
2012                       case recover:
2013                       case recover_wait:
2014                         break;
2015
2016                       case recover_done:
2017                         dhcp_failover_set_state (state, normal);
2018                         break;
2019
2020                       case normal:
2021                       case potential_conflict:
2022                       case partner_down:
2023                       case communications_interrupted:
2024                       case resolution_interrupted:
2025                         dhcp_failover_set_state (state, potential_conflict);
2026                         break;
2027
2028                         /* These don't change anything. */
2029                       case shut_down:
2030                       case paused:
2031                         break;
2032
2033                       case unknown_state:
2034                       case startup:
2035                         break;
2036                 }
2037                 break;
2038
2039               case communications_interrupted:
2040                 switch (new_state) {
2041                       case paused:
2042                         /* Stick with the status quo. */
2043                         break;
2044
2045                         /* If we're in communications-interrupted and an
2046                            amnesiac peer connects, go to the partner_down
2047                            state immediately. */
2048                       case recover:
2049                         dhcp_failover_set_state (state, partner_down);
2050                         break;
2051
2052                       case normal:
2053                       case communications_interrupted:
2054                       case recover_done:
2055                       case recover_wait:
2056                         /* XXX so we don't need to do this specially in
2057                            XXX the CONNECT and CONNECTACK handlers. */
2058                         dhcp_failover_send_updates (state);
2059                         dhcp_failover_set_state (state, normal);
2060                         break;
2061
2062                       case potential_conflict:
2063                       case partner_down:
2064                       case resolution_interrupted:
2065                         dhcp_failover_set_state (state, potential_conflict);
2066                         break;
2067
2068                       case shut_down:
2069                         dhcp_failover_set_state (state, partner_down);
2070                         break;
2071
2072                       case unknown_state:
2073                       case startup:
2074                         break;
2075                 }
2076                 break;
2077
2078               case resolution_interrupted:
2079                 switch (new_state) {
2080                       case normal:
2081                       case recover:
2082                       case potential_conflict:
2083                       case partner_down:
2084                       case communications_interrupted:
2085                       case resolution_interrupted:
2086                       case recover_done:
2087                       case recover_wait:
2088                         dhcp_failover_set_state (state, potential_conflict);
2089                         break;
2090
2091                       case shut_down:
2092                         dhcp_failover_set_state (state, partner_down);
2093                         break;
2094
2095                       case paused:
2096                         break;
2097
2098                       case unknown_state:
2099                       case startup:
2100                         break;
2101                 }
2102                 break;
2103
2104               case recover_done:
2105                 switch (new_state) {
2106                       case normal:
2107                       case recover_done:
2108                         dhcp_failover_set_state (state, normal);
2109                         break;
2110
2111                       case potential_conflict:
2112                       case partner_down:
2113                       case communications_interrupted:
2114                       case resolution_interrupted:
2115                       case paused:
2116                       case recover:
2117                       case recover_wait:
2118                         break;
2119
2120                       case shut_down:
2121                         dhcp_failover_set_state (state, partner_down);
2122                         break;
2123
2124                       case unknown_state:
2125                       case startup:
2126                         break;
2127                 }
2128                 break;
2129
2130                 /* We are essentially dead in the water when we're in
2131                    either shut_down or paused states, and do not do any
2132                    automatic state transitions. */
2133               case shut_down:
2134               case paused:
2135                 break;
2136
2137                 /* We still have to wait... */
2138               case recover_wait:
2139                 break;
2140
2141               case unknown_state:
2142                 break;  
2143         }
2144
2145         /* If we didn't make a transition out of startup as a result of
2146            the peer's state change, do it now as a result of the fact that
2147            we got a state change from the peer. */
2148         if (state -> me.state == startup && state -> saved_state != startup)
2149                 dhcp_failover_set_state (state, state -> saved_state);
2150         
2151         /* For now, just set the service state based on the peer's state
2152            if necessary. */
2153         dhcp_failover_set_service_state (state);
2154
2155         return ISC_R_SUCCESS;
2156 }
2157
2158 int dhcp_failover_pool_rebalance (dhcp_failover_state_t *state)
2159 {
2160         int lts;
2161         int leases_queued = 0;
2162         struct lease *lp = (struct lease *)0;
2163         struct lease *next = (struct lease *)0;
2164         struct shared_network *s;
2165         struct pool *p;
2166         int polarity;
2167         binding_state_t peer_lease_state;
2168         binding_state_t my_lease_state;
2169         struct lease **lq;
2170         int tenper;
2171
2172         if (state -> me.state != normal || state -> i_am == secondary)
2173                 return 0;
2174
2175         for (s = shared_networks; s; s = s -> next) {
2176             for (p = s -> pools; p; p = p -> next) {
2177                 if (p -> failover_peer != state)
2178                     continue;
2179
2180                 /* Right now we're giving the peer half of the free leases.
2181                    If we have more leases than the peer (i.e., more than
2182                    half), then the number of leases we have, less the number
2183                    of leases the peer has, will be how many more leases we
2184                    have than the peer has.   So if we send half that number
2185                    to the peer, we should be even. */
2186                 if (p -> failover_peer -> i_am == primary) {
2187                         lts = (p -> free_leases - p -> backup_leases) / 2;
2188                         peer_lease_state = FTS_BACKUP;
2189                         my_lease_state = FTS_FREE;
2190                         lq = &p -> free;
2191                 } else {
2192                         lts = (p -> backup_leases - p -> free_leases) / 2;
2193                         peer_lease_state = FTS_FREE;
2194                         my_lease_state = FTS_BACKUP;
2195                         lq = &p -> backup;
2196                 }
2197
2198                 tenper = (p -> backup_leases + p -> free_leases) / 10;
2199                 if (tenper == 0)
2200                         tenper = 1;
2201                 if (lts > tenper) {
2202                     log_info ("pool %lx %s  total %d  free %d  %s %d  lts %d",
2203                           (unsigned long)p,
2204                           (p -> shared_network ?
2205                            p -> shared_network -> name : ""), p -> lease_count,
2206                           p -> free_leases, "backup", p -> backup_leases, lts);
2207
2208                     lease_reference (&lp, *lq, MDL);
2209
2210                     while (lp && lts) {
2211                         /* Remember the next lease in the list. */
2212                         if (next)
2213                             lease_dereference (&next, MDL);
2214                         if (lp -> next)
2215                             lease_reference (&next, lp -> next, MDL);
2216
2217                         --lts;
2218                         ++leases_queued;
2219                         lp -> next_binding_state = peer_lease_state;
2220                         lp -> tstp = cur_time;
2221                         lp -> starts = cur_time;
2222
2223                         if (!supersede_lease (lp, (struct lease *)0, 0, 1, 0)
2224                             || !write_lease (lp))
2225                         {
2226                             log_info ("can't commit lease %s on giveaway",
2227                                       piaddr (lp -> ip_addr));
2228                         }
2229
2230                         lease_dereference (&lp, MDL);
2231                         if (next)
2232                                 lease_reference (&lp, next, MDL);
2233                     }
2234                     if (next)
2235                         lease_dereference (&next, MDL);
2236                     if (lp)
2237                         lease_dereference (&lp, MDL);
2238
2239                 }
2240                 if (lts > 1) {
2241                         log_info ("lease imbalance - lts = %d", lts);
2242                 }
2243             }
2244         }
2245         commit_leases();
2246         dhcp_failover_send_poolresp (state, leases_queued);
2247         dhcp_failover_send_updates (state);
2248         return leases_queued;
2249 }
2250
2251 int dhcp_failover_pool_check (struct pool *pool)
2252 {
2253         int lts;
2254         struct lease *lp;
2255         int tenper;
2256
2257         if (!pool -> failover_peer ||
2258             pool -> failover_peer -> me.state != normal)
2259                 return 0;
2260
2261         if (pool -> failover_peer -> i_am == primary)
2262                 lts = (pool -> backup_leases - pool -> free_leases) / 2;
2263         else
2264                 lts = (pool -> free_leases - pool -> backup_leases) / 2;
2265
2266         log_info ("pool %lx %s total %d  free %d  backup %d  lts %d",
2267                   (unsigned long)pool,
2268                   pool -> shared_network ? pool -> shared_network -> name : "",
2269                   pool -> lease_count,
2270                   pool -> free_leases, pool -> backup_leases, lts);
2271
2272         tenper = (pool -> backup_leases + pool -> free_leases) / 10;
2273         if (tenper == 0)
2274                 tenper = 1;
2275         if (lts > tenper) {
2276                 /* XXX What about multiple pools? */
2277                 if (pool -> failover_peer -> i_am == secondary) {
2278                         /* Ask the primary to send us leases. */
2279                         dhcp_failover_send_poolreq (pool -> failover_peer);
2280                         return 1;
2281                 } else {
2282                         /* Figure out how many leases to skip on the backup
2283                            list.   We skip the earliest leases on the list
2284                            to reduce the chance of trying to steal a lease
2285                            that the secondary is about to allocate. */
2286                         int i = pool -> backup_leases - lts;
2287                         log_info ("Taking %d leases from secondary.", lts);
2288                         for (lp = pool -> backup; lp; lp = lp -> next) {
2289                                 /* Skip to the last leases on the free
2290                                    list, because they are less likely
2291                                    to already have been allocated. */
2292                                 if (i)
2293                                         --i;
2294                                 else {
2295                                         lp -> desired_binding_state = FTS_FREE;
2296                                         dhcp_failover_queue_update (lp, 1);
2297                                         --lts;
2298                                 }
2299                         }
2300                         if (lts)
2301                                 log_info ("failed to take %d leases.", lts);
2302                 }
2303         }
2304         return 0;
2305 }
2306
2307 int dhcp_failover_state_pool_check (dhcp_failover_state_t *state)
2308 {
2309         struct lease *lp;
2310         struct shared_network *s;
2311         struct pool *p;
2312
2313         for (s = shared_networks; s; s = s -> next) {
2314                 for (p = s -> pools; p; p = p -> next) {
2315                         if (p -> failover_peer != state)
2316                                 continue;
2317                         /* Only need to request rebalance on one pool. */
2318                         if (dhcp_failover_pool_check (p))
2319                                 return 1;
2320                 }
2321         }
2322         return 0;
2323 }
2324
2325 isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *state)
2326 {
2327         struct lease *lp = (struct lease *)0;
2328         isc_result_t status;
2329
2330         /* Can't update peer if we're not talking to it! */
2331         if (!state -> link_to_peer)
2332                 return ISC_R_SUCCESS;
2333
2334         while ((state -> partner.max_flying_updates >
2335                 state -> cur_unacked_updates) && state -> update_queue_head) {
2336                 /* Grab the head of the update queue. */
2337                 lease_reference (&lp, state -> update_queue_head, MDL);
2338
2339                 /* Send the update to the peer. */
2340                 status = dhcp_failover_send_bind_update (state, lp);
2341                 if (status != ISC_R_SUCCESS) {
2342                         lease_dereference (&lp, MDL);
2343                         return status;
2344                 }
2345                 lp -> flags &= ~ON_UPDATE_QUEUE;
2346
2347                 /* Take it off the head of the update queue and put the next
2348                    item in the update queue at the head. */
2349                 lease_dereference (&state -> update_queue_head, MDL);
2350                 if (lp -> next_pending) {
2351                         lease_reference (&state -> update_queue_head,
2352                                          lp -> next_pending, MDL);
2353                         lease_dereference (&lp -> next_pending, MDL);
2354                 } else {
2355                         lease_dereference (&state -> update_queue_tail, MDL);
2356                 }
2357
2358                 if (state -> ack_queue_head) {
2359                         lease_reference
2360                                 (&state -> ack_queue_tail -> next_pending,
2361                                  lp, MDL);
2362                         lease_dereference (&state -> ack_queue_tail, MDL);
2363                 } else {
2364                         lease_reference (&state -> ack_queue_head, lp, MDL);
2365                 }
2366 #if defined (POINTER_DEBUG)
2367                 if (lp -> next_pending) {
2368                         log_error ("ack_queue_tail: lp -> next_pending");
2369                         abort ();
2370                 }
2371 #endif
2372                 lease_reference (&state -> ack_queue_tail, lp, MDL);
2373                 lp -> flags |= ON_ACK_QUEUE;
2374                 lease_dereference (&lp, MDL);
2375
2376                 /* Count the object as an unacked update. */
2377                 state -> cur_unacked_updates++;
2378         }
2379         return ISC_R_SUCCESS;
2380 }
2381
2382 /* Queue an update for a lease.   Always returns 1 at this point - it's
2383    not an error for this to be called on a lease for which there's no
2384    failover peer. */
2385
2386 int dhcp_failover_queue_update (struct lease *lease, int immediate)
2387 {
2388         dhcp_failover_state_t *state;
2389
2390         if (!lease -> pool ||
2391             !lease -> pool -> failover_peer)
2392                 return 1;
2393
2394         /* If it's already on the update queue, leave it there. */
2395         if (lease -> flags & ON_UPDATE_QUEUE)
2396                 return 1;
2397
2398         /* Get the failover state structure for this lease. */
2399         state = lease -> pool -> failover_peer;
2400
2401         /* If it's on the ack queue, take it off. */
2402         if (lease -> flags & ON_ACK_QUEUE)
2403                 dhcp_failover_ack_queue_remove (state, lease);
2404
2405         if (state -> update_queue_head) {
2406                 lease_reference (&state -> update_queue_tail -> next_pending,
2407                                  lease, MDL);
2408                 lease_dereference (&state -> update_queue_tail, MDL);
2409         } else {
2410                 lease_reference (&state -> update_queue_head, lease, MDL);
2411         }
2412 #if defined (POINTER_DEBUG)
2413         if (lease -> next_pending) {
2414                 log_error ("next pending on update queue lease.");
2415 #if defined (DEBUG_RC_HISTORY)
2416                 dump_rc_history (lease);
2417 #endif
2418                 abort ();
2419         }
2420 #endif
2421         lease_reference (&state -> update_queue_tail, lease, MDL);
2422         lease -> flags |= ON_UPDATE_QUEUE;
2423         if (immediate)
2424                 dhcp_failover_send_updates (state);
2425         return 1;
2426 }
2427
2428 int dhcp_failover_send_acks (dhcp_failover_state_t *state)
2429 {
2430         failover_message_t *msg = (failover_message_t *)0;
2431
2432         /* Must commit all leases prior to acking them. */
2433         if (!commit_leases ())
2434                 return 0;
2435
2436         while (state -> toack_queue_head) {
2437                 failover_message_reference
2438                         (&msg, state -> toack_queue_head, MDL);
2439                 failover_message_dereference
2440                         (&state -> toack_queue_head, MDL);
2441                 if (msg -> next) {
2442                         failover_message_reference
2443                                 (&state -> toack_queue_head, msg -> next, MDL);
2444                 }
2445
2446                 dhcp_failover_send_bind_ack (state, msg, 0, (const char *)0);
2447
2448                 failover_message_dereference (&msg, MDL);
2449         }
2450
2451         if (state -> toack_queue_tail)
2452                 failover_message_dereference (&state -> toack_queue_tail, MDL);
2453         state -> pending_acks = 0;
2454
2455         return 1;
2456 }
2457
2458 void dhcp_failover_toack_queue_timeout (void *vs)
2459 {
2460         dhcp_failover_state_t *state = vs;
2461
2462 #if defined (DEBUG_FAILOVER_TIMING)
2463         log_info ("dhcp_failover_toack_queue_timeout");
2464 #endif
2465
2466         dhcp_failover_send_acks (state);
2467 }
2468
2469 /* Queue an ack for a message.  There is currently no way to queue a
2470    negative ack -- these need to be sent directly. */
2471
2472 int dhcp_failover_queue_ack (dhcp_failover_state_t *state,
2473                              failover_message_t *msg)
2474 {
2475         if (state -> toack_queue_head) {
2476                 failover_message_reference
2477                         (&state -> toack_queue_tail -> next, msg, MDL);
2478                 failover_message_dereference (&state -> toack_queue_tail, MDL);
2479         } else {
2480                 failover_message_reference (&state -> toack_queue_head,
2481                                             msg, MDL);
2482         }
2483         failover_message_reference (&state -> toack_queue_tail, msg, MDL);
2484
2485         state -> pending_acks++;
2486
2487         /* Flush the toack queue whenever we exceed half the number of
2488            allowed unacked updates. */
2489         if (state -> pending_acks >= state -> partner.max_flying_updates / 2) {
2490                 dhcp_failover_send_acks (state);
2491         }
2492
2493         /* Schedule a timeout to flush the ack queue. */
2494         if (state -> pending_acks > 0) {
2495 #if defined (DEBUG_FAILOVER_TIMING)
2496                 log_info ("add_timeout +2 %s",
2497                           "dhcp_failover_toack_queue_timeout");
2498 #endif
2499                 add_timeout (cur_time + 2,
2500                              dhcp_failover_toack_queue_timeout, state,
2501                              (tvref_t)dhcp_failover_state_reference,
2502                              (tvunref_t)dhcp_failover_state_dereference);
2503         }
2504
2505         return 1;
2506 }
2507
2508 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *state,
2509                                      struct lease *lease)
2510 {
2511         struct lease *lp;
2512
2513         if (!(lease -> flags & ON_ACK_QUEUE))
2514                 return;
2515
2516         if (state -> ack_queue_head == lease) {
2517                 lease_dereference (&state -> ack_queue_head, MDL);
2518                 if (lease -> next_pending) {
2519                         lease_reference (&state -> ack_queue_head,
2520                                          lease -> next_pending, MDL);
2521                         lease_dereference (&lease -> next_pending, MDL);
2522                 } else {
2523                         lease_dereference (&state -> ack_queue_tail, MDL);
2524                 }
2525         } else {
2526                 for (lp = state -> ack_queue_head;
2527                      lp && lp -> next_pending != lease;
2528                      lp = lp -> next_pending)
2529                         ;
2530
2531                 if (!lp)
2532                         return;
2533
2534                 lease_dereference (&lp -> next_pending, MDL);
2535                 if (lease -> next_pending) {
2536                         lease_reference (&lp -> next_pending,
2537                                          lease -> next_pending, MDL);
2538                         lease_dereference (&lease -> next_pending, MDL);
2539                 } else {
2540                         lease_dereference (&state -> ack_queue_tail, MDL);
2541                         if (lp -> next_pending) {
2542                                 log_error ("state -> ack_queue_tail");
2543                                 abort ();
2544                         }
2545                         lease_reference (&state -> ack_queue_tail, lp, MDL);
2546                 }
2547         }
2548
2549         lease -> flags &= ~ON_ACK_QUEUE;
2550         state -> cur_unacked_updates--;
2551
2552         /*
2553          * When updating leases as a result of an ack, we defer the commit
2554          * for performance reasons.  When there are no more acks pending,
2555          * do a commit.
2556          */
2557         if (state -> cur_unacked_updates == 0) {
2558                 commit_leases();
2559         }
2560 }
2561
2562 isc_result_t dhcp_failover_state_set_value (omapi_object_t *h,
2563                                             omapi_object_t *id,
2564                                             omapi_data_string_t *name,
2565                                             omapi_typed_data_t *value)
2566 {
2567         isc_result_t status;
2568
2569         if (h -> type != dhcp_type_failover_state)
2570                 return ISC_R_INVALIDARG;
2571
2572         /* This list of successful returns is completely wrong, but the
2573            fastest way to make dhcpctl do something vaguely sane when
2574            you try to change the local state. */
2575
2576         if (!omapi_ds_strcmp (name, "name")) {
2577                 return ISC_R_SUCCESS;
2578         } else if (!omapi_ds_strcmp (name, "partner-address")) {
2579                 return ISC_R_SUCCESS;
2580         } else if (!omapi_ds_strcmp (name, "local-address")) {
2581                 return ISC_R_SUCCESS;
2582         } else if (!omapi_ds_strcmp (name, "partner-port")) {
2583                 return ISC_R_SUCCESS;
2584         } else if (!omapi_ds_strcmp (name, "local-port")) {
2585                 return ISC_R_SUCCESS;
2586         } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
2587                 return ISC_R_SUCCESS;
2588         } else if (!omapi_ds_strcmp (name, "mclt")) {
2589                 return ISC_R_SUCCESS;
2590         } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
2591                 return ISC_R_SUCCESS;
2592         } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
2593                 return ISC_R_SUCCESS;
2594         } else if (!omapi_ds_strcmp (name, "partner-state")) {
2595                 return ISC_R_SUCCESS;
2596         } else if (!omapi_ds_strcmp (name, "local-state")) {
2597                 unsigned long l;
2598                 status = omapi_get_int_value (&l, value);
2599                 if (status != ISC_R_SUCCESS)
2600                         return status;
2601                 return dhcp_failover_set_state ((dhcp_failover_state_t *)h, l);
2602         } else if (!omapi_ds_strcmp (name, "partner-stos")) {
2603                 return ISC_R_SUCCESS;
2604         } else if (!omapi_ds_strcmp (name, "local-stos")) {
2605                 return ISC_R_SUCCESS;
2606         } else if (!omapi_ds_strcmp (name, "hierarchy")) {
2607                 return ISC_R_SUCCESS;
2608         } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
2609                 return ISC_R_SUCCESS;
2610         } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
2611                 return ISC_R_SUCCESS;
2612         } else if (!omapi_ds_strcmp (name, "skew")) {
2613                 return ISC_R_SUCCESS;
2614         } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
2615                 return ISC_R_SUCCESS;
2616         } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
2617                 return ISC_R_SUCCESS;
2618         }
2619                 
2620         if (h -> inner && h -> inner -> type -> set_value)
2621                 return (*(h -> inner -> type -> set_value))
2622                         (h -> inner, id, name, value);
2623         return ISC_R_NOTFOUND;
2624 }
2625
2626 void dhcp_failover_keepalive (void *vs)
2627 {
2628         dhcp_failover_state_t *state = vs;
2629 }
2630
2631 void dhcp_failover_reconnect (void *vs)
2632 {
2633         dhcp_failover_state_t *state = vs;
2634         isc_result_t status;
2635
2636 #if defined (DEBUG_FAILOVER_TIMING)
2637         log_info ("dhcp_failover_reconnect");
2638 #endif
2639         /* If we already connected the other way, let the connection
2640            recovery code initiate any retry that may be required. */
2641         if (state -> link_to_peer)
2642                 return;
2643
2644         status = dhcp_failover_link_initiate ((omapi_object_t *)state);
2645         if (status != ISC_R_SUCCESS && status != ISC_R_INCOMPLETE) {
2646                 log_info ("failover peer %s: %s", state -> name,
2647                           isc_result_totext (status));
2648 #if defined (DEBUG_FAILOVER_TIMING)
2649                 log_info ("add_timeout +90 %s",
2650                           "dhcp_failover_listener_restart");
2651 #endif
2652                 add_timeout (cur_time + 90,
2653                              dhcp_failover_listener_restart, state,
2654                              (tvref_t)dhcp_failover_state_reference,
2655                              (tvunref_t)dhcp_failover_state_dereference);
2656         }
2657 }
2658
2659 void dhcp_failover_startup_timeout (void *vs)
2660 {
2661         dhcp_failover_state_t *state = vs;
2662         isc_result_t status;
2663
2664 #if defined (DEBUG_FAILOVER_TIMING)
2665         log_info ("dhcp_failover_startup_timeout");
2666 #endif
2667
2668         dhcp_failover_state_transition (state, "disconnect");
2669 }
2670
2671 void dhcp_failover_link_startup_timeout (void *vl)
2672 {
2673         dhcp_failover_link_t *link = vl;
2674         isc_result_t status;
2675         omapi_object_t *p;
2676
2677         for (p = (omapi_object_t *)link; p -> inner; p = p -> inner)
2678                 ;
2679         for (; p; p = p -> outer)
2680                 if (p -> type == omapi_type_connection)
2681                         break;
2682         if (p) {
2683                 log_info ("failover: link startup timeout");
2684                 omapi_disconnect (p, 1);
2685         }
2686 }
2687
2688 void dhcp_failover_listener_restart (void *vs)
2689 {
2690         dhcp_failover_state_t *state = vs;
2691         isc_result_t status;
2692
2693 #if defined (DEBUG_FAILOVER_TIMING)
2694         log_info ("dhcp_failover_listener_restart");
2695 #endif
2696
2697         status = dhcp_failover_listen ((omapi_object_t *)state);
2698         if (status != ISC_R_SUCCESS) {
2699                 log_info ("failover peer %s: %s", state -> name,
2700                           isc_result_totext (status));
2701 #if defined (DEBUG_FAILOVER_TIMING)
2702                 log_info ("add_timeout +90 %s",
2703                           "dhcp_failover_listener_restart");
2704 #endif
2705                 add_timeout (cur_time + 90,
2706                              dhcp_failover_listener_restart, state,
2707                              (tvref_t)dhcp_failover_state_reference,
2708                              (tvunref_t)dhcp_failover_state_dereference);
2709         }
2710 }
2711
2712 isc_result_t dhcp_failover_state_get_value (omapi_object_t *h,
2713                                             omapi_object_t *id,
2714                                             omapi_data_string_t *name,
2715                                             omapi_value_t **value)
2716 {
2717         dhcp_failover_state_t *s;
2718         struct option_cache *oc;
2719         struct data_string ds;
2720         isc_result_t status;
2721
2722         if (h -> type != dhcp_type_failover_state)
2723                 return ISC_R_INVALIDARG;
2724         s = (dhcp_failover_state_t *)h;
2725         
2726         if (!omapi_ds_strcmp (name, "name")) {
2727                 if (s -> name)
2728                         return omapi_make_string_value (value,
2729                                                         name, s -> name, MDL);
2730                 return ISC_R_NOTFOUND;
2731         } else if (!omapi_ds_strcmp (name, "partner-address")) {
2732                 oc = s -> partner.address;
2733               getaddr:
2734                 memset (&ds, 0, sizeof ds);
2735                 if (!evaluate_option_cache (&ds, (struct packet *)0,
2736                                             (struct lease *)0,
2737                                             (struct client_state *)0,
2738                                             (struct option_state *)0,
2739                                             (struct option_state *)0,
2740                                             &global_scope, oc, MDL)) {
2741                         return ISC_R_NOTFOUND;
2742                 }
2743                 status = omapi_make_const_value (value,
2744                                                  name, ds.data, ds.len, MDL);
2745                 /* Disgusting kludge: */
2746                 if (oc == s -> me.address && !s -> server_identifier.len)
2747                         data_string_copy (&s -> server_identifier, &ds, MDL);
2748                 data_string_forget (&ds, MDL);
2749                 return status;
2750         } else if (!omapi_ds_strcmp (name, "local-address")) {
2751                 oc = s -> me.address;
2752                 goto getaddr;
2753         } else if (!omapi_ds_strcmp (name, "partner-port")) {
2754                 return omapi_make_int_value (value, name,
2755                                              s -> partner.port, MDL);
2756         } else if (!omapi_ds_strcmp (name, "local-port")) {
2757                 return omapi_make_int_value (value,
2758                                              name, s -> me.port, MDL);
2759         } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
2760                 return omapi_make_uint_value (value, name,
2761                                               s -> me.max_flying_updates,
2762                                               MDL);
2763         } else if (!omapi_ds_strcmp (name, "mclt")) {
2764                 return omapi_make_uint_value (value, name, s -> mclt, MDL);
2765         } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
2766                 return omapi_make_int_value (value, name,
2767                                              s -> load_balance_max_secs, MDL);
2768         } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
2769                 if (s -> hba)
2770                         return omapi_make_const_value (value, name,
2771                                                        s -> hba, 32, MDL);
2772                 return ISC_R_NOTFOUND;
2773         } else if (!omapi_ds_strcmp (name, "partner-state")) {
2774                 return omapi_make_uint_value (value, name,
2775                                              s -> partner.state, MDL);
2776         } else if (!omapi_ds_strcmp (name, "local-state")) {
2777                 return omapi_make_uint_value (value, name,
2778                                               s -> me.state, MDL);
2779         } else if (!omapi_ds_strcmp (name, "partner-stos")) {
2780                 return omapi_make_int_value (value, name,
2781                                              s -> partner.stos, MDL);
2782         } else if (!omapi_ds_strcmp (name, "local-stos")) {
2783                 return omapi_make_int_value (value, name,
2784                                              s -> me.stos, MDL);
2785         } else if (!omapi_ds_strcmp (name, "hierarchy")) {
2786                 return omapi_make_uint_value (value, name, s -> i_am, MDL);
2787         } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
2788                 return omapi_make_int_value (value, name,
2789                                              s -> last_packet_sent, MDL);
2790         } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
2791                 return omapi_make_int_value (value, name,
2792                                              s -> last_timestamp_received,
2793                                              MDL);
2794         } else if (!omapi_ds_strcmp (name, "skew")) {
2795                 return omapi_make_int_value (value, name, s -> skew, MDL);
2796         } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
2797                 return omapi_make_uint_value (value, name,
2798                                              s -> me.max_response_delay,
2799                                               MDL);
2800         } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
2801                 return omapi_make_int_value (value, name,
2802                                              s -> cur_unacked_updates, MDL);
2803         }
2804                 
2805         if (h -> inner && h -> inner -> type -> get_value)
2806                 return (*(h -> inner -> type -> get_value))
2807                         (h -> inner, id, name, value);
2808         return ISC_R_NOTFOUND;
2809 }
2810
2811 isc_result_t dhcp_failover_state_destroy (omapi_object_t *h,
2812                                               const char *file, int line)
2813 {
2814         dhcp_failover_state_t *s;
2815
2816         if (h -> type != dhcp_type_failover_state)
2817                 return ISC_R_INVALIDARG;
2818         s = (dhcp_failover_state_t *)h;
2819
2820         if (s -> link_to_peer)
2821             dhcp_failover_link_dereference (&s -> link_to_peer, file, line);
2822         if (s -> name) {
2823                 dfree (s -> name, MDL);
2824                 s -> name = (char *)0;
2825         }
2826         if (s -> partner.address)
2827                 option_cache_dereference (&s -> partner.address, file, line);
2828         if (s -> me.address)
2829                 option_cache_dereference (&s -> me.address, file, line);
2830         if (s -> hba) {
2831                 dfree (s -> hba, file, line);
2832                 s -> hba = (u_int8_t *)0;
2833         }
2834         if (s -> update_queue_head)
2835                 lease_dereference (&s -> update_queue_head, file, line);
2836         if (s -> update_queue_tail)
2837                 lease_dereference (&s -> update_queue_tail, file, line);
2838         if (s -> ack_queue_head)
2839                 lease_dereference (&s -> ack_queue_head, file, line);
2840         if (s -> ack_queue_tail)
2841                 lease_dereference (&s -> ack_queue_tail, file, line);
2842         if (s -> send_update_done)
2843                 lease_dereference (&s -> send_update_done, file, line);
2844         if (s -> toack_queue_head)
2845                 failover_message_dereference (&s -> toack_queue_head,
2846                                               file, line);
2847         if (s -> toack_queue_tail)
2848                 failover_message_dereference (&s -> toack_queue_tail,
2849                                               file, line);
2850         return ISC_R_SUCCESS;
2851 }
2852
2853 /* Write all the published values associated with the object through the
2854    specified connection. */
2855
2856 isc_result_t dhcp_failover_state_stuff (omapi_object_t *c,
2857                                         omapi_object_t *id,
2858                                         omapi_object_t *h)
2859 {
2860         dhcp_failover_state_t *s;
2861         omapi_connection_object_t *conn;
2862         isc_result_t status;
2863
2864         if (c -> type != omapi_type_connection)
2865                 return ISC_R_INVALIDARG;
2866         conn = (omapi_connection_object_t *)c;
2867
2868         if (h -> type != dhcp_type_failover_state)
2869                 return ISC_R_INVALIDARG;
2870         s = (dhcp_failover_state_t *)h;
2871         
2872         status = omapi_connection_put_name (c, "name");
2873         if (status != ISC_R_SUCCESS)
2874                 return status;
2875         status = omapi_connection_put_string (c, s -> name);
2876         if (status != ISC_R_SUCCESS)
2877                 return status;
2878
2879         status = omapi_connection_put_name (c, "partner-address");
2880         if (status != ISC_R_SUCCESS)
2881                 return status;
2882         status = omapi_connection_put_uint32 (c, sizeof s -> partner.address);
2883         if (status != ISC_R_SUCCESS)
2884                 return status;
2885         status = omapi_connection_copyin (c, (u_int8_t *)&s -> partner.address,
2886                                           sizeof s -> partner.address);
2887         if (status != ISC_R_SUCCESS)
2888                 return status;
2889         
2890         status = omapi_connection_put_name (c, "partner-port");
2891         if (status != ISC_R_SUCCESS)
2892                 return status;
2893         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2894         if (status != ISC_R_SUCCESS)
2895                 return status;
2896         status = omapi_connection_put_uint32 (c, (u_int32_t)s -> partner.port);
2897         if (status != ISC_R_SUCCESS)
2898                 return status;
2899         
2900         status = omapi_connection_put_name (c, "local-address");
2901         if (status != ISC_R_SUCCESS)
2902                 return status;
2903         status = omapi_connection_put_uint32 (c, sizeof s -> me.address);
2904         if (status != ISC_R_SUCCESS)
2905                 return status;
2906         status = omapi_connection_copyin (c, (u_int8_t *)&s -> me.address,
2907                                           sizeof s -> me.address);
2908         if (status != ISC_R_SUCCESS)
2909                 return status;
2910         
2911         status = omapi_connection_put_name (c, "local-port");
2912         if (status != ISC_R_SUCCESS)
2913                 return status;
2914         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2915         if (status != ISC_R_SUCCESS)
2916                 return status;
2917         status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.port);
2918         if (status != ISC_R_SUCCESS)
2919                 return status;
2920         
2921         status = omapi_connection_put_name (c, "max-outstanding-updates");
2922         if (status != ISC_R_SUCCESS)
2923                 return status;
2924         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2925         if (status != ISC_R_SUCCESS)
2926                 return status;
2927         status = omapi_connection_put_uint32 (c,
2928                                               s -> me.max_flying_updates);
2929         if (status != ISC_R_SUCCESS)
2930                 return status;
2931
2932         status = omapi_connection_put_name (c, "mclt");
2933         if (status != ISC_R_SUCCESS)
2934                 return status;
2935         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2936         if (status != ISC_R_SUCCESS)
2937                 return status;
2938         status = omapi_connection_put_uint32 (c, s -> mclt);
2939         if (status != ISC_R_SUCCESS)
2940                 return status;
2941
2942         status = omapi_connection_put_name (c, "load-balance-max-secs");
2943         if (status != ISC_R_SUCCESS)
2944                 return status;
2945         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2946         if (status != ISC_R_SUCCESS)
2947                 return status;
2948         status = (omapi_connection_put_uint32
2949                   (c, (u_int32_t)s -> load_balance_max_secs));
2950         if (status != ISC_R_SUCCESS)
2951                 return status;
2952
2953         
2954         if (s -> hba) {
2955                 status = omapi_connection_put_name (c, "load-balance-hba");
2956                 if (status != ISC_R_SUCCESS)
2957                         return status;
2958                 status = omapi_connection_put_uint32 (c, 32);
2959                 if (status != ISC_R_SUCCESS)
2960                         return status;
2961                 status = omapi_connection_copyin (c, s -> hba, 32);
2962                 if (status != ISC_R_SUCCESS)
2963                         return status;
2964         }
2965
2966         status = omapi_connection_put_name (c, "partner-state");
2967         if (status != ISC_R_SUCCESS)
2968                 return status;
2969         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2970         if (status != ISC_R_SUCCESS)
2971                 return status;
2972         status = omapi_connection_put_uint32 (c, s -> partner.state);
2973         if (status != ISC_R_SUCCESS)
2974                 return status;
2975         
2976         status = omapi_connection_put_name (c, "local-state");
2977         if (status != ISC_R_SUCCESS)
2978                 return status;
2979         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2980         if (status != ISC_R_SUCCESS)
2981                 return status;
2982         status = omapi_connection_put_uint32 (c, s -> me.state);
2983         if (status != ISC_R_SUCCESS)
2984                 return status;
2985         
2986         status = omapi_connection_put_name (c, "partner-stos");
2987         if (status != ISC_R_SUCCESS)
2988                 return status;
2989         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2990         if (status != ISC_R_SUCCESS)
2991                 return status;
2992         status = omapi_connection_put_uint32 (c,
2993                                               (u_int32_t)s -> partner.stos);
2994         if (status != ISC_R_SUCCESS)
2995                 return status;
2996
2997         status = omapi_connection_put_name (c, "local-stos");
2998         if (status != ISC_R_SUCCESS)
2999                 return status;
3000         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3001         if (status != ISC_R_SUCCESS)
3002                 return status;
3003         status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.stos);
3004         if (status != ISC_R_SUCCESS)
3005                 return status;
3006
3007         status = omapi_connection_put_name (c, "hierarchy");
3008         if (status != ISC_R_SUCCESS)
3009                 return status;
3010         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3011         if (status != ISC_R_SUCCESS)
3012                 return status;
3013         status = omapi_connection_put_uint32 (c, s -> i_am);
3014         if (status != ISC_R_SUCCESS)
3015                 return status;
3016
3017         status = omapi_connection_put_name (c, "last-packet-sent");
3018         if (status != ISC_R_SUCCESS)
3019                 return status;
3020         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3021         if (status != ISC_R_SUCCESS)
3022                 return status;
3023         status = (omapi_connection_put_uint32
3024                   (c, (u_int32_t)s -> last_packet_sent));
3025         if (status != ISC_R_SUCCESS)
3026                 return status;
3027
3028         status = omapi_connection_put_name (c, "last-timestamp-received");
3029         if (status != ISC_R_SUCCESS)
3030                 return status;
3031         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3032         if (status != ISC_R_SUCCESS)
3033                 return status;
3034         status = (omapi_connection_put_uint32
3035                   (c, (u_int32_t)s -> last_timestamp_received));
3036         if (status != ISC_R_SUCCESS)
3037                 return status;
3038
3039         status = omapi_connection_put_name (c, "skew");
3040         if (status != ISC_R_SUCCESS)
3041                 return status;
3042         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3043         if (status != ISC_R_SUCCESS)
3044                 return status;
3045         status = omapi_connection_put_uint32 (c, (u_int32_t)s -> skew);
3046         if (status != ISC_R_SUCCESS)
3047                 return status;
3048
3049         status = omapi_connection_put_name (c, "max-response-delay");
3050         if (status != ISC_R_SUCCESS)
3051                 return status;
3052         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3053         if (status != ISC_R_SUCCESS)
3054                 return status;
3055         status = (omapi_connection_put_uint32
3056                   (c, (u_int32_t)s -> me.max_response_delay));
3057         if (status != ISC_R_SUCCESS)
3058                 return status;
3059         
3060         status = omapi_connection_put_name (c, "cur-unacked-updates");
3061         if (status != ISC_R_SUCCESS)
3062                 return status;
3063         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3064         if (status != ISC_R_SUCCESS)
3065                 return status;
3066         status = (omapi_connection_put_uint32
3067                   (c, (u_int32_t)s -> cur_unacked_updates));
3068         if (status != ISC_R_SUCCESS)
3069                 return status;
3070
3071         if (h -> inner && h -> inner -> type -> stuff_values)
3072                 return (*(h -> inner -> type -> stuff_values)) (c, id,
3073                                                                 h -> inner);
3074         return ISC_R_SUCCESS;
3075 }
3076
3077 isc_result_t dhcp_failover_state_lookup (omapi_object_t **sp,
3078                                          omapi_object_t *id,
3079                                          omapi_object_t *ref)
3080 {
3081         omapi_value_t *tv = (omapi_value_t *)0;
3082         isc_result_t status;
3083         dhcp_failover_state_t *s;
3084
3085         if (!ref)
3086                 return ISC_R_NOKEYS;
3087
3088         /* First see if we were sent a handle. */
3089         status = omapi_get_value_str (ref, id, "handle", &tv);
3090         if (status == ISC_R_SUCCESS) {
3091                 status = omapi_handle_td_lookup (sp, tv -> value);
3092
3093                 omapi_value_dereference (&tv, MDL);
3094                 if (status != ISC_R_SUCCESS)
3095                         return status;
3096
3097                 /* Don't return the object if the type is wrong. */
3098                 if ((*sp) -> type != dhcp_type_failover_state) {
3099                         omapi_object_dereference (sp, MDL);
3100                         return ISC_R_INVALIDARG;
3101                 }
3102         }
3103
3104         /* Look the failover state up by peer name. */
3105         status = omapi_get_value_str (ref, id, "name", &tv);
3106         if (status == ISC_R_SUCCESS) {
3107                 for (s = failover_states; s; s = s -> next) {
3108                         unsigned l = strlen (s -> name);
3109                         if (l == tv -> value -> u.buffer.len &&
3110                             !memcmp (s -> name,
3111                                      tv -> value -> u.buffer.value, l))
3112                                 break;
3113                 }
3114                 omapi_value_dereference (&tv, MDL);
3115
3116                 /* If we already have a lease, and it's not the same one,
3117                    then the query was invalid. */
3118                 if (*sp && *sp != (omapi_object_t *)s) {
3119                         omapi_object_dereference (sp, MDL);
3120                         return ISC_R_KEYCONFLICT;
3121                 } else if (!s) {
3122                         if (*sp)
3123                                 omapi_object_dereference (sp, MDL);
3124                         return ISC_R_NOTFOUND;
3125                 } else if (!*sp)
3126                         /* XXX fix so that hash lookup itself creates
3127                            XXX the reference. */
3128                         omapi_object_reference (sp, (omapi_object_t *)s, MDL);
3129         }
3130
3131         /* If we get to here without finding a lease, no valid key was
3132            specified. */
3133         if (!*sp)
3134                 return ISC_R_NOKEYS;
3135         return ISC_R_SUCCESS;
3136 }
3137
3138 isc_result_t dhcp_failover_state_create (omapi_object_t **sp,
3139                                          omapi_object_t *id)
3140 {
3141         return ISC_R_NOTIMPLEMENTED;
3142 }
3143
3144 isc_result_t dhcp_failover_state_remove (omapi_object_t *sp,
3145                                          omapi_object_t *id)
3146 {
3147         return ISC_R_NOTIMPLEMENTED;
3148 }
3149
3150 int dhcp_failover_state_match (dhcp_failover_state_t *state,
3151                                u_int8_t *addr, unsigned addrlen)
3152 {
3153         struct option_cache *oc;
3154         struct data_string ds;
3155         int i;
3156         
3157         memset (&ds, 0, sizeof ds);
3158         if (evaluate_option_cache (&ds, (struct packet *)0,
3159                                    (struct lease *)0,
3160                                    (struct client_state *)0,
3161                                    (struct option_state *)0,
3162                                    (struct option_state *)0,
3163                                    &global_scope,
3164                                    state -> partner.address, MDL)) {
3165                 for (i = 0; i + addrlen - 1 < ds.len; i += addrlen) {
3166                         if (!memcmp (&ds.data [i],
3167                                      addr, addrlen)) {
3168                                 data_string_forget (&ds, MDL);
3169                                 return 1;
3170                         }
3171                 }
3172                 data_string_forget (&ds, MDL);
3173         }
3174         return 0;
3175 }
3176
3177 const char *dhcp_failover_reject_reason_print (int reason)
3178 {
3179     switch (reason) {
3180       case FTR_ILLEGAL_IP_ADDR:
3181         return "Illegal IP address (not part of any address pool).";
3182
3183       case FTR_FATAL_CONFLICT:
3184         return "Fatal conflict exists: address in use by other client.";
3185
3186       case FTR_MISSING_BINDINFO:
3187         return "Missing binding information.";
3188
3189       case FTR_TIMEMISMATCH:
3190         return "Connection rejected, time mismatch too great.";
3191
3192       case FTR_INVALID_MCLT:
3193         return "Connection rejected, invalid MCLT.";
3194
3195       case FTR_MISC_REJECT:
3196         return "Connection rejected, unknown reason.";
3197
3198       case FTR_DUP_CONNECTION:
3199         return "Connection rejected, duplicate connection.";
3200
3201       case FTR_INVALID_PARTNER:
3202         return "Connection rejected, invalid failover partner.";
3203
3204       case FTR_TLS_UNSUPPORTED:
3205         return "TLS not supported.";
3206
3207       case FTR_TLS_UNCONFIGURED:
3208         return "TLS supported but not configured.";
3209
3210       case FTR_TLS_REQUIRED:
3211         return "TLS required but not supported by partner.";
3212
3213       case FTR_DIGEST_UNSUPPORTED:
3214         return "Message digest not supported.";
3215
3216       case FTR_DIGEST_UNCONFIGURED:
3217         return "Message digest not configured.";
3218
3219       case FTR_VERSION_MISMATCH:
3220         return "Protocol version mismatch.";
3221
3222       case FTR_MISSING_BIND_INFO:
3223         return "Missing binding information.";
3224
3225       case FTR_OUTDATED_BIND_INFO:
3226         return "Outdated binding information.";
3227
3228       case FTR_LESS_CRIT_BIND_INFO:
3229         return "Less critical binding information.";
3230
3231       case FTR_NO_TRAFFIC:
3232         return "No traffic within sufficient time.";
3233
3234       case FTR_HBA_CONFLICT:
3235         return "Hash bucket assignment conflict.";
3236
3237       default:
3238       case FTR_UNKNOWN:
3239         return "Unknown: Error occurred but does not match any reason code.";
3240     }
3241 }
3242
3243 const char *dhcp_failover_state_name_print (enum failover_state state)
3244 {
3245         switch (state) {
3246               default:
3247               case unknown_state:
3248                 return "unknown-state";
3249
3250               case partner_down:
3251                 return "partner-down";
3252
3253               case normal:
3254                 return "normal";
3255
3256               case communications_interrupted:
3257                 return "communications-interrupted";
3258
3259               case resolution_interrupted:
3260                 return "resolution-interrupted";
3261
3262               case potential_conflict:
3263                 return "potential-conflict";
3264
3265               case recover:
3266                 return "recover";
3267
3268               case recover_done:
3269                 return "recover-done";
3270
3271               case recover_wait:
3272                 return "recover-wait";
3273
3274               case shut_down:
3275                 return "shutdown";
3276
3277               case paused:
3278                 return "paused";
3279
3280               case startup:
3281                 return "startup";
3282         }
3283 }
3284
3285 const char *dhcp_failover_message_name (unsigned type)
3286 {
3287         switch (type) {
3288               case FTM_POOLREQ:
3289                 return "pool-request";
3290                 
3291               case FTM_POOLRESP:
3292                 return "pool-response";
3293
3294               case FTM_BNDUPD:
3295                 return "bind-update";
3296
3297               case FTM_BNDACK:
3298                 return "bind-ack";
3299
3300               case FTM_CONNECT:
3301                 return "connect";
3302
3303               case FTM_CONNECTACK:
3304                 return "connect-ack";
3305
3306               case FTM_UPDREQ:
3307                 return "update-request";
3308
3309               case FTM_UPDDONE:
3310                 return "update-done";
3311
3312               case FTM_UPDREQALL:
3313                 return "update-request-all";
3314
3315               case FTM_STATE:
3316                 return "state";
3317
3318               case FTM_CONTACT:
3319                 return "contact";
3320
3321               case FTM_DISCONNECT:
3322                 return "disconnect";
3323
3324               default:
3325                 return "<unknown message type>";
3326         }
3327 }
3328
3329 const char *dhcp_failover_option_name (unsigned type)
3330 {
3331         switch (type) {
3332               case FTO_BINDING_STATUS:
3333                 return "binding-status";
3334
3335               case FTO_ASSIGNED_IP_ADDRESS:
3336                 return "assigned-ip-address";
3337
3338               case FTO_SERVER_ADDR:
3339                 return "server-addr";
3340
3341               case FTO_ADDRESSES_TRANSFERRED:
3342                 return "addresses-transferred";
3343
3344               case FTO_CLIENT_IDENTIFIER:
3345                 return "client-identifier";
3346
3347               case FTO_CHADDR:
3348                 return "chaddr";
3349
3350               case FTO_DDNS:
3351                 return "ddns";
3352
3353               case FTO_REJECT_REASON:
3354                 return "reject-reason";
3355
3356               case FTO_MESSAGE:
3357                 return "message";
3358
3359               case FTO_MCLT:
3360                 return "mclt";
3361
3362               case FTO_VENDOR_CLASS:
3363                 return "vendor-class";
3364
3365               case FTO_LEASE_EXPIRY:
3366                 return "lease-expiry";
3367
3368               case FTO_POTENTIAL_EXPIRY:
3369                 return "potential-expiry";
3370
3371               case FTO_GRACE_EXPIRY:
3372                 return "grace-expiry";
3373
3374               case FTO_CLTT:
3375                 return "cltt";
3376
3377               case FTO_STOS:
3378                 return "stos";
3379
3380               case FTO_SERVER_STATE:
3381                 return "server-state";
3382
3383               case FTO_SERVER_FLAGS:
3384                 return "server-flags";
3385
3386               case FTO_VENDOR_OPTIONS:
3387                 return "vendor-options";
3388
3389               case FTO_MAX_UNACKED:
3390                 return "max-unacked";
3391
3392               case FTO_RECEIVE_TIMER:
3393                 return "receive-timer";
3394
3395               case FTO_HBA:
3396                 return "hba";
3397
3398               case FTO_MESSAGE_DIGEST:
3399                 return "message-digest";
3400
3401               case FTO_PROTOCOL_VERSION:
3402                 return "protocol-version";
3403
3404               case FTO_TLS_REQUEST:
3405                 return "tls-request";
3406
3407               case FTO_TLS_REPLY:
3408                 return "tls-reply";
3409
3410               case FTO_REQUEST_OPTIONS:
3411                 return "request-options";
3412
3413               case FTO_REPLY_OPTIONS:
3414                 return "reply-options";
3415
3416               default:
3417                 return "<unknown option>";
3418         }
3419 }
3420
3421 failover_option_t *dhcp_failover_option_printf (unsigned code,
3422                                                 char *obuf,
3423                                                 unsigned *obufix,
3424                                                 unsigned obufmax,
3425                                                 const char *fmt, ...)
3426 {
3427         va_list va;
3428         char tbuf [256];
3429
3430         /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3431          * It is unclear what the effects of truncation here are, or
3432          * how that condition should be handled.  It seems that this
3433          * function is used for formatting messages in the failover
3434          * command channel.  For now the safest thing is for
3435          * overflow-truncation to cause a fatal log.
3436          */
3437         va_start (va, fmt);
3438         if (vsnprintf (tbuf, sizeof tbuf, fmt, va) >= sizeof tbuf)
3439                 log_fatal ("%s: vsnprintf would truncate",
3440                                 "dhcp_failover_make_option");
3441         va_end (va);
3442
3443         return dhcp_failover_make_option (code, obuf, obufix, obufmax,
3444                                           strlen (tbuf), tbuf);
3445 }
3446
3447 failover_option_t *dhcp_failover_make_option (unsigned code,
3448                                               char *obuf, unsigned *obufix,
3449                                               unsigned obufmax, ...)
3450 {
3451         va_list va;
3452         struct failover_option_info *info;
3453         int i;
3454         unsigned size, count;
3455         unsigned val;
3456         u_int8_t *iaddr;
3457         unsigned ilen = 0;
3458         u_int8_t *bval;
3459         char *txt = NULL;
3460 #if defined (DEBUG_FAILOVER_MESSAGES)
3461         char tbuf [256];
3462 #endif
3463
3464         /* Note that the failover_option structure is used differently on
3465            input than on output - on input, count is an element count, and
3466            on output it's the number of bytes total in the option, including
3467            the option code and option length. */
3468         failover_option_t option, *op;
3469
3470
3471         /* Bogus option code? */
3472         if (code < 1 || code > FTO_MAX || ft_options [code].type == FT_UNDEF) {
3473                 return &null_failover_option;
3474         }
3475         info = &ft_options [code];
3476                 
3477         va_start (va, obufmax);
3478
3479         /* Get the number of elements and the size of the buffer we need
3480            to allocate. */
3481         if (info -> type == FT_DDNS || info -> type == FT_DDNS1) {
3482                 count = info -> type == FT_DDNS ? 1 : 2;
3483                 size = va_arg (va, int) + count;
3484         } else {
3485                 /* Find out how many items in this list. */
3486                 if (info -> num_present)
3487                         count = info -> num_present;
3488                 else
3489                         count = va_arg (va, int);
3490
3491                 /* Figure out size. */
3492                 switch (info -> type) {
3493                       case FT_UINT8:
3494                       case FT_BYTES:
3495                       case FT_DIGEST:
3496                         size = count;
3497                         break;
3498
3499                       case FT_TEXT_OR_BYTES:
3500                       case FT_TEXT:
3501                         txt = va_arg (va, char *);
3502                         size = count;
3503                         break;
3504
3505                       case FT_IPADDR:
3506                         ilen = va_arg (va, unsigned);
3507                         size = count * ilen;
3508                         break;
3509
3510                       case FT_UINT32:
3511                         size = count * 4;
3512                         break;
3513
3514                       case FT_UINT16:
3515                         size = count * 2;
3516                         break;
3517
3518                       default:
3519                         /* shouldn't get here. */
3520                         log_fatal ("bogus type in failover_make_option: %d",
3521                                    info -> type);
3522                         return &null_failover_option;
3523                 }
3524         }
3525         
3526         size += 4;
3527
3528         /* Allocate a buffer for the option. */
3529         option.count = size;
3530         option.data = dmalloc (option.count, MDL);
3531         if (!option.data) {
3532                 va_end (va);
3533                 return &null_failover_option;
3534         }
3535
3536         /* Put in the option code and option length. */
3537         putUShort (option.data, code);
3538         putUShort (&option.data [2], size - 4);
3539
3540 #if defined (DEBUG_FAILOVER_MESSAGES)   
3541         /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3542          * It is unclear what the effects of truncation here are, or
3543          * how that condition should be handled.  It seems that this
3544          * message may be sent over the failover command channel.
3545          * For now the safest thing is for overflow-truncation to cause
3546          * a fatal log.
3547          */
3548         if (snprintf (tbuf, sizeof tbuf, " (%s<%d>", info -> name,
3549                         option.count) >= sizeof tbuf)
3550                 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3551         failover_print (obuf, obufix, obufmax, tbuf);
3552 #endif
3553
3554         /* Now put in the data. */
3555         switch (info -> type) {
3556               case FT_UINT8:
3557                 for (i = 0; i < count; i++) {
3558                         val = va_arg (va, unsigned);
3559 #if defined (DEBUG_FAILOVER_MESSAGES)
3560                         /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
3561                         sprintf (tbuf, " %d", val);
3562                         failover_print (obuf, obufix, obufmax, tbuf);
3563 #endif
3564                         option.data [i + 4] = val;
3565                 }
3566                 break;
3567
3568               case FT_IPADDR:
3569                 for (i = 0; i < count; i++) {
3570                         iaddr = va_arg (va, u_int8_t *);
3571                         if (ilen != 4) {
3572                                 dfree (option.data, MDL);
3573                                 log_error ("IP addrlen=%d, should be 4.",
3574                                            ilen);
3575                                 va_end (va);
3576                                 return &null_failover_option;
3577                         }
3578                                 
3579 #if defined (DEBUG_FAILOVER_MESSAGES)
3580                         /*%Audit% Cannot exceed 17 bytes.  %2004.06.17,Safe%*/
3581                         sprintf (tbuf, " %u.%u.%u.%u",
3582                                   iaddr [0], iaddr [1], iaddr [2], iaddr [3]);
3583                         failover_print (obuf, obufix, obufmax, tbuf);
3584 #endif
3585                         memcpy (&option.data [4 + i * ilen], iaddr, ilen);
3586                 }
3587                 break;
3588
3589               case FT_UINT32:
3590                 for (i = 0; i < count; i++) {
3591                         val = va_arg (va, unsigned);
3592 #if defined (DEBUG_FAILOVER_MESSAGES)
3593                         /*%Audit% Cannot exceed 24 bytes.  %2004.06.17,Safe%*/
3594                         sprintf (tbuf, " %d", val);
3595                         failover_print (obuf, obufix, obufmax, tbuf);
3596 #endif
3597                         putULong (&option.data [4 + i * 4], val);
3598                 }
3599                 break;
3600
3601               case FT_BYTES:
3602               case FT_DIGEST:
3603                 bval = va_arg (va, u_int8_t *);
3604 #if defined (DEBUG_FAILOVER_MESSAGES)
3605                 for (i = 0; i < count; i++) {
3606                         /* 23 bytes plus nul, safe. */
3607                         sprintf (tbuf, " %d", bval [i]);
3608                         failover_print (obuf, obufix, obufmax, tbuf);
3609                 }
3610 #endif
3611                 memcpy (&option.data [4], bval, count);
3612                 break;
3613
3614                 /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
3615                    terminated.  Note that the caller should be careful not
3616                    to provide a format and data that amount to more than 256
3617                    bytes of data, since it will cause a fatal error. */
3618               case FT_TEXT_OR_BYTES:
3619               case FT_TEXT:
3620 #if defined (DEBUG_FAILOVER_MESSAGES)
3621                 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3622                  * It is unclear what the effects of truncation here are, or
3623                  * how that condition should be handled.  It seems that this
3624                  * function is used for formatting messages in the failover
3625                  * command channel.  For now the safest thing is for
3626                  * overflow-truncation to cause a fatal log.
3627                  */
3628                 if (snprintf (tbuf, sizeof tbuf, "\"%s\"", txt) >= sizeof tbuf)
3629                         log_fatal ("dhcp_failover_make_option: tbuf overflow");
3630                 failover_print (obuf, obufix, obufmax, tbuf);
3631 #endif
3632                 memcpy (&option.data [4], txt, count);
3633                 break;
3634
3635               case FT_DDNS:
3636               case FT_DDNS1:
3637                 option.data [4] = va_arg (va, unsigned);
3638                 if (count == 2)
3639                         option.data [5] = va_arg (va, unsigned);
3640                 bval = va_arg (va, u_int8_t *);
3641                 memcpy (&option.data [4 + count], bval, size - count - 4);
3642 #if defined (DEBUG_FAILOVER_MESSAGES)
3643                 for (i = 4; i < size; i++) {
3644                         /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3645                         sprintf (tbuf, " %d", option.data [i]);
3646                         failover_print (obuf, obufix, obufmax, tbuf);
3647                 }
3648 #endif
3649                 break;
3650
3651               case FT_UINT16:
3652                 for (i = 0; i < count; i++) {
3653                         val = va_arg (va, u_int32_t);
3654 #if defined (DEBUG_FAILOVER_MESSAGES)
3655                         /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3656                         sprintf (tbuf, " %d", val);
3657                         failover_print (obuf, obufix, obufmax, tbuf);
3658 #endif
3659                         putUShort (&option.data [4 + i * 2], val);
3660                 }
3661                 break;
3662
3663               case FT_UNDEF:
3664               default:
3665                 break;
3666         }
3667
3668 #if defined DEBUG_FAILOVER_MESSAGES
3669         failover_print (obuf, obufix, obufmax, ")");
3670 #endif
3671         va_end (va);
3672
3673         /* Now allocate a place to store what we just set up. */
3674         op = dmalloc (sizeof (failover_option_t), MDL);
3675         if (!op) {
3676                 dfree (option.data, MDL);
3677                 return &null_failover_option;
3678         }
3679
3680         *op = option;
3681         return op;
3682 }
3683
3684 /* Send a failover message header. */
3685
3686 isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link,
3687                                         omapi_object_t *connection,
3688                                         int msg_type, ...)
3689 {
3690         unsigned count = 0;
3691         unsigned size = 0;
3692         int bad_option = 0;
3693         int opix = 0;
3694         va_list list;
3695         failover_option_t *option;
3696         unsigned char *opbuf;
3697         isc_result_t status = ISC_R_SUCCESS;
3698         unsigned char cbuf;
3699
3700         /* Run through the argument list once to compute the length of
3701            the option portion of the message. */
3702         va_start (list, msg_type);
3703         while ((option = va_arg (list, failover_option_t *))) {
3704                 if (option != &skip_failover_option)
3705                         size += option -> count;
3706                 if (option == &null_failover_option)
3707                         bad_option = 1;
3708         }
3709         va_end (list);
3710
3711         /* Allocate an option buffer, unless we got an error. */
3712         if (!bad_option && size) {
3713                 opbuf = dmalloc (size, MDL);
3714                 if (!opbuf)
3715                         status = ISC_R_NOMEMORY;
3716         } else
3717                 opbuf = (unsigned char *)0;
3718
3719         va_start (list, msg_type);
3720         while ((option = va_arg (list, failover_option_t *))) {
3721                 if (option == &skip_failover_option)
3722                     continue;
3723                 if (!bad_option && opbuf)
3724                         memcpy (&opbuf [opix],
3725                                 option -> data, option -> count);
3726                 if (option != &null_failover_option &&
3727                     option != &skip_failover_option) {
3728                         opix += option -> count;
3729                         dfree (option -> data, MDL);
3730                         dfree (option, MDL);
3731                 }
3732         }
3733
3734         if (bad_option)
3735                 return ISC_R_INVALIDARG;
3736
3737         /* Now send the message header. */
3738
3739         /* Message length. */
3740         status = omapi_connection_put_uint16 (connection, size + 12);
3741         if (status != ISC_R_SUCCESS)
3742                 goto err;
3743
3744         /* Message type. */
3745         cbuf = msg_type;
3746         status = omapi_connection_copyin (connection, &cbuf, 1);
3747         if (status != ISC_R_SUCCESS)
3748                 goto err;
3749
3750         /* Payload offset. */
3751         cbuf = 12;
3752         status = omapi_connection_copyin (connection, &cbuf, 1);
3753         if (status != ISC_R_SUCCESS)
3754                 goto err;
3755
3756         /* Current time. */
3757         status = omapi_connection_put_uint32 (connection, (u_int32_t)cur_time);
3758         if (status != ISC_R_SUCCESS)
3759                 goto err;
3760
3761         /* Transaction ID. */
3762         status = omapi_connection_put_uint32 (connection, link -> xid++);
3763         if (status != ISC_R_SUCCESS)
3764                 goto err;
3765
3766         
3767         /* Payload. */
3768         if (opbuf) {
3769                 status = omapi_connection_copyin (connection, opbuf, size);
3770                 if (status != ISC_R_SUCCESS)
3771                         goto err;
3772                 dfree (opbuf, MDL);
3773         }
3774         if (link -> state_object &&
3775             link -> state_object -> link_to_peer == link) {
3776 #if defined (DEBUG_FAILOVER_TIMING)
3777                 log_info ("add_timeout +%d %s",
3778                           (int)(link -> state_object ->
3779                                 partner.max_response_delay) / 3,
3780                           "dhcp_failover_send_contact");
3781 #endif
3782                 add_timeout (cur_time +
3783                              (int)(link -> state_object ->
3784                                    partner.max_response_delay) / 3,
3785                              dhcp_failover_send_contact, link -> state_object,
3786                              (tvref_t)dhcp_failover_state_reference,
3787                              (tvunref_t)dhcp_failover_state_dereference);
3788         }
3789         return status;
3790
3791       err:
3792         if (opbuf)
3793                 dfree (opbuf, MDL);
3794         log_info ("dhcp_failover_put_message: something went wrong.");
3795         omapi_disconnect (connection, 1);
3796         return status;
3797 }
3798
3799 void dhcp_failover_timeout (void *vstate)
3800 {
3801         dhcp_failover_state_t *state = vstate;
3802         dhcp_failover_link_t *link;
3803         isc_result_t status;
3804
3805 #if defined (DEBUG_FAILOVER_TIMING)
3806         log_info ("dhcp_failover_timeout");
3807 #endif
3808
3809         if (!state || state -> type != dhcp_type_failover_state)
3810                 return;
3811         link = state -> link_to_peer;
3812         if (!link ||
3813             !link -> outer ||
3814             link -> outer -> type != omapi_type_connection)
3815                 return;
3816
3817         log_error ("timeout waiting for failover peer %s", state -> name);
3818
3819         /* If we haven't gotten a timely response, blow away the connection.
3820            This will cause the state to change automatically. */
3821         omapi_disconnect (link -> outer, 1);
3822 }
3823
3824 void dhcp_failover_send_contact (void *vstate)
3825 {
3826         dhcp_failover_state_t *state = vstate;
3827         dhcp_failover_link_t *link;
3828         isc_result_t status;
3829
3830 #if defined (DEBUG_FAILOVER_MESSAGES)   
3831         char obuf [64];
3832         unsigned obufix = 0;
3833         
3834 # define FMA obuf, &obufix, sizeof obuf
3835         failover_print (FMA, "(contact");
3836 #else
3837 # define FMA (char *)0, (unsigned *)0, 0
3838 #endif
3839
3840 #if defined (DEBUG_FAILOVER_TIMING)
3841         log_info ("dhcp_failover_send_contact");
3842 #endif
3843
3844         if (!state || state -> type != dhcp_type_failover_state)
3845                 return;
3846         link = state -> link_to_peer;
3847         if (!link ||
3848             !link -> outer ||
3849             link -> outer -> type != omapi_type_connection)
3850                 return;
3851
3852         status = (dhcp_failover_put_message
3853                   (link, link -> outer,
3854                    FTM_CONTACT,
3855                    (failover_option_t *)0));
3856
3857 #if defined (DEBUG_FAILOVER_MESSAGES)
3858         if (status != ISC_R_SUCCESS)
3859                 failover_print (FMA, " (failed)");
3860         failover_print (FMA, ")");
3861         if (obufix) {
3862                 log_debug ("%s", obuf);
3863         }
3864 #endif
3865         return;
3866 }
3867
3868 isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *state)
3869 {
3870         dhcp_failover_link_t *link;
3871         isc_result_t status;
3872
3873 #if defined (DEBUG_FAILOVER_MESSAGES)   
3874         char obuf [64];
3875         unsigned obufix = 0;
3876         
3877 # define FMA obuf, &obufix, sizeof obuf
3878         failover_print (FMA, "(state");
3879 #else
3880 # define FMA (char *)0, (unsigned *)0, 0
3881 #endif
3882
3883         if (!state || state -> type != dhcp_type_failover_state)
3884                 return ISC_R_INVALIDARG;
3885         link = state -> link_to_peer;
3886         if (!link ||
3887             !link -> outer ||
3888             link -> outer -> type != omapi_type_connection)
3889                 return ISC_R_INVALIDARG;
3890
3891         status = (dhcp_failover_put_message
3892                   (link, link -> outer,
3893                    FTM_STATE,
3894                    dhcp_failover_make_option (FTO_SERVER_STATE, FMA,
3895                                               (state -> me.state == startup
3896                                                ? state -> saved_state
3897                                                : state -> me.state)),
3898                    dhcp_failover_make_option
3899                    (FTO_SERVER_FLAGS, FMA,
3900                     (state -> service_state == service_startup
3901                      ? FTF_STARTUP : 0)),
3902                    dhcp_failover_make_option (FTO_STOS, FMA, state -> me.stos),
3903                    (failover_option_t *)0));
3904
3905 #if defined (DEBUG_FAILOVER_MESSAGES)
3906         if (status != ISC_R_SUCCESS)
3907                 failover_print (FMA, " (failed)");
3908         failover_print (FMA, ")");
3909         if (obufix) {
3910                 log_debug ("%s", obuf);
3911         }
3912 #endif
3913         return ISC_R_SUCCESS;
3914 }
3915
3916 /* Send a connect message. */
3917
3918 isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
3919 {
3920         dhcp_failover_link_t *link;
3921         dhcp_failover_state_t *state;
3922         isc_result_t status;
3923         char hba [32];
3924 #if defined (DEBUG_FAILOVER_MESSAGES)   
3925         char obuf [64];
3926         unsigned obufix = 0;
3927         
3928 # define FMA obuf, &obufix, sizeof obuf
3929         failover_print (FMA, "(connect");
3930 #else
3931 # define FMA (char *)0, (unsigned *)0, 0
3932 #endif
3933
3934         if (!l || l -> type != dhcp_type_failover_link)
3935                 return ISC_R_INVALIDARG;
3936         link = (dhcp_failover_link_t *)l;
3937         state = link -> state_object;
3938         if (!l -> outer || l -> outer -> type != omapi_type_connection)
3939                 return ISC_R_INVALIDARG;
3940
3941         status =
3942             (dhcp_failover_put_message
3943              (link, l -> outer,
3944               FTM_CONNECT,
3945               dhcp_failover_make_option (FTO_SERVER_ADDR, FMA,
3946                                          state -> server_identifier.len,
3947                                          state -> server_identifier.data),
3948               dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
3949                                          state -> me.max_flying_updates),
3950               dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
3951                                          state -> me.max_response_delay),
3952               dhcp_failover_option_printf (FTO_VENDOR_CLASS, FMA,
3953                                            "isc-%s", DHCP_VERSION),
3954               dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
3955                                          DHCP_FAILOVER_VERSION),
3956               dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
3957                                          0, 0),
3958               dhcp_failover_make_option (FTO_MCLT, FMA,
3959                                          state -> mclt),
3960               (state -> hba
3961                ? dhcp_failover_make_option (FTO_HBA, FMA, 32, state -> hba)
3962                : &skip_failover_option),
3963               (failover_option_t *)0));
3964         
3965 #if defined (DEBUG_FAILOVER_MESSAGES)
3966         if (status != ISC_R_SUCCESS)
3967                 failover_print (FMA, " (failed)");
3968         failover_print (FMA, ")");
3969         if (obufix) {
3970                 log_debug ("%s", obuf);
3971         }
3972 #endif
3973         return status;
3974 }
3975
3976 isc_result_t dhcp_failover_send_connectack (omapi_object_t *l,
3977                                             dhcp_failover_state_t *state,
3978                                             int reason, const char *errmsg)
3979 {
3980         dhcp_failover_link_t *link;
3981         isc_result_t status;
3982 #if defined (DEBUG_FAILOVER_MESSAGES)   
3983         char obuf [64];
3984         unsigned obufix = 0;
3985         
3986 # define FMA obuf, &obufix, sizeof obuf
3987         failover_print (FMA, "(connectack");
3988 #else
3989 # define FMA (char *)0, (unsigned *)0, 0
3990 #endif
3991
3992         if (!l || l -> type != dhcp_type_failover_link)
3993                 return ISC_R_INVALIDARG;
3994         link = (dhcp_failover_link_t *)l;
3995         if (!l -> outer || l -> outer -> type != omapi_type_connection)
3996                 return ISC_R_INVALIDARG;
3997
3998         status =
3999             (dhcp_failover_put_message
4000              (link, l -> outer,
4001               FTM_CONNECTACK,
4002               (state
4003                ? (dhcp_failover_make_option
4004                   (FTO_SERVER_ADDR, FMA,
4005                    state -> server_identifier.len,
4006                    state -> server_identifier.data))
4007                : &skip_failover_option),
4008               (state
4009                ? dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
4010                                             state -> me.max_flying_updates)
4011                : &skip_failover_option),
4012               (state
4013                ? dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
4014                                             state -> me.max_response_delay)
4015                : &skip_failover_option),
4016               dhcp_failover_option_printf (FTO_VENDOR_CLASS, FMA,
4017                                            "isc-%s", DHCP_VERSION),
4018               dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
4019                                          DHCP_FAILOVER_VERSION),
4020               dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
4021                                          0, 0),
4022               (reason
4023                ? dhcp_failover_make_option (FTO_REJECT_REASON,
4024                                             FMA, reason)
4025                : &skip_failover_option),
4026               (errmsg
4027                ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4028                                             strlen (errmsg), errmsg)
4029                : &skip_failover_option),
4030               (failover_option_t *)0));
4031
4032 #if defined (DEBUG_FAILOVER_MESSAGES)
4033         if (status != ISC_R_SUCCESS)
4034                 failover_print (FMA, " (failed)");
4035         failover_print (FMA, ")");
4036         if (obufix) {
4037                 log_debug ("%s", obuf);
4038         }
4039 #endif
4040         return status;
4041 }
4042
4043 isc_result_t dhcp_failover_send_disconnect (omapi_object_t *l,
4044                                             int reason,
4045                                             const char *message)
4046 {
4047         dhcp_failover_link_t *link;
4048         dhcp_failover_state_t *state;
4049         isc_result_t status;
4050 #if defined (DEBUG_FAILOVER_MESSAGES)   
4051         char obuf [64];
4052         unsigned obufix = 0;
4053         
4054 # define FMA obuf, &obufix, sizeof obuf
4055         failover_print (FMA, "(disconnect");
4056 #else
4057 # define FMA (char *)0, (unsigned *)0, 0
4058 #endif
4059
4060         if (!l || l -> type != dhcp_type_failover_link)
4061                 return ISC_R_INVALIDARG;
4062         link = (dhcp_failover_link_t *)l;
4063         state = link -> state_object;
4064         if (!l -> outer || l -> outer -> type != omapi_type_connection)
4065                 return ISC_R_INVALIDARG;
4066
4067         if (!message && reason)
4068                 message = dhcp_failover_reject_reason_print (reason);
4069
4070         status = (dhcp_failover_put_message
4071                   (link, l -> outer,
4072                    FTM_DISCONNECT,
4073                    dhcp_failover_make_option (FTO_REJECT_REASON,
4074                                               FMA, reason),
4075                    (message
4076                     ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4077                                                  strlen (message), message)
4078                     : &skip_failover_option),
4079                    (failover_option_t *)0));
4080
4081 #if defined (DEBUG_FAILOVER_MESSAGES)
4082         if (status != ISC_R_SUCCESS)
4083                 failover_print (FMA, " (failed)");
4084         failover_print (FMA, ")");
4085         if (obufix) {
4086                 log_debug ("%s", obuf);
4087         }
4088 #endif
4089         return status;
4090 }
4091
4092 /* Send a Bind Update message. */
4093
4094 isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
4095                                              struct lease *lease)
4096 {
4097         dhcp_failover_link_t *link;
4098         isc_result_t status;
4099 #if defined (DEBUG_FAILOVER_MESSAGES)   
4100         char obuf [64];
4101         unsigned obufix = 0;
4102         
4103 # define FMA obuf, &obufix, sizeof obuf
4104         failover_print (FMA, "(bndupd");
4105 #else
4106 # define FMA (char *)0, (unsigned *)0, 0
4107 #endif
4108
4109         if (!state -> link_to_peer ||
4110             state -> link_to_peer -> type != dhcp_type_failover_link)
4111                 return ISC_R_INVALIDARG;
4112         link = (dhcp_failover_link_t *)state -> link_to_peer;
4113
4114         if (!link -> outer || link -> outer -> type != omapi_type_connection)
4115                 return ISC_R_INVALIDARG;
4116
4117         /* Send the update. */
4118         status = (dhcp_failover_put_message
4119                   (link, link -> outer,
4120                    FTM_BNDUPD,
4121                    dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
4122                                               lease -> ip_addr.len,
4123                                               lease -> ip_addr.iabuf),
4124                    dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
4125                                               lease -> desired_binding_state),
4126                    lease -> uid_len
4127                    ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
4128                                                 lease -> uid_len,
4129                                                 lease -> uid)
4130                    : &skip_failover_option,
4131                    lease -> hardware_addr.hlen
4132                    ? dhcp_failover_make_option (FTO_CHADDR, FMA,
4133                                                 lease -> hardware_addr.hlen,
4134                                                 lease -> hardware_addr.hbuf)
4135                    : &skip_failover_option,
4136                    dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
4137                                               lease -> ends),
4138                    dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
4139                                               lease -> tstp),
4140                    dhcp_failover_make_option (FTO_STOS, FMA,
4141                                               lease -> starts),
4142                    dhcp_failover_make_option (FTO_CLTT, FMA,
4143                                               lease -> cltt),
4144                    &skip_failover_option,       /* XXX DDNS */
4145                    &skip_failover_option,       /* XXX request options */
4146                    &skip_failover_option,       /* XXX reply options */
4147                    (failover_option_t *)0));
4148
4149 #if defined (DEBUG_FAILOVER_MESSAGES)
4150         if (status != ISC_R_SUCCESS)
4151                 failover_print (FMA, " (failed)");
4152         failover_print (FMA, ")");
4153         if (obufix) {
4154                 log_debug ("%s", obuf);
4155         }
4156 #endif
4157         return status;
4158 }
4159
4160 /* Send a Bind ACK message. */
4161
4162 isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state,
4163                                           failover_message_t *msg,
4164                                           int reason, const char *message)
4165 {
4166         dhcp_failover_link_t *link;
4167         isc_result_t status;
4168 #if defined (DEBUG_FAILOVER_MESSAGES)   
4169         char obuf [64];
4170         unsigned obufix = 0;
4171         
4172 # define FMA obuf, &obufix, sizeof obuf
4173         failover_print (FMA, "(bndack");
4174 #else
4175 # define FMA (char *)0, (unsigned *)0, 0
4176 #endif
4177
4178         if (!state -> link_to_peer ||
4179             state -> link_to_peer -> type != dhcp_type_failover_link)
4180                 return ISC_R_INVALIDARG;
4181         link = (dhcp_failover_link_t *)state -> link_to_peer;
4182
4183         if (!link -> outer || link -> outer -> type != omapi_type_connection)
4184                 return ISC_R_INVALIDARG;
4185
4186         if (!message && reason)
4187                 message = dhcp_failover_reject_reason_print (reason);
4188
4189         /* Send the update. */
4190         status = (dhcp_failover_put_message
4191                   (link, link -> outer,
4192                    FTM_BNDACK,
4193                    dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
4194                                               sizeof msg -> assigned_addr,
4195                                               &msg -> assigned_addr),
4196                    dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
4197                                               msg -> binding_status),
4198                    (msg -> options_present & FTB_CLIENT_IDENTIFIER)
4199                    ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
4200                                                 msg -> client_identifier.count,
4201                                                 msg -> client_identifier.data)
4202                    : &skip_failover_option,
4203                    (msg -> options_present & FTB_CHADDR)
4204                    ? dhcp_failover_make_option (FTO_CHADDR, FMA,
4205                                                 msg -> chaddr.count,
4206                                                 msg -> chaddr.data)
4207                    : &skip_failover_option,
4208                    dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
4209                                               msg -> expiry),
4210                    dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
4211                                               msg -> potential_expiry),
4212                    dhcp_failover_make_option (FTO_STOS, FMA,
4213                                               msg -> stos),
4214                    dhcp_failover_make_option (FTO_CLTT, FMA,
4215                                               msg -> client_ltt),
4216                    reason
4217                    ? dhcp_failover_make_option (FTO_REJECT_REASON,
4218                                                 FMA, reason)
4219                    : &skip_failover_option,
4220                    (message
4221                     ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4222                                                  strlen (message), message)
4223                     : &skip_failover_option),
4224                    &skip_failover_option,       /* XXX DDNS */
4225                    &skip_failover_option,       /* XXX request options */
4226                    &skip_failover_option,       /* XXX reply options */
4227                    (failover_option_t *)0));
4228
4229 #if defined (DEBUG_FAILOVER_MESSAGES)
4230         if (status != ISC_R_SUCCESS)
4231                 failover_print (FMA, " (failed)");
4232         failover_print (FMA, ")");
4233         if (obufix) {
4234                 log_debug ("%s", obuf);
4235         }
4236 #endif
4237         return status;
4238 }
4239
4240 isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *state)
4241 {
4242         dhcp_failover_link_t *link;
4243         isc_result_t status;
4244 #if defined (DEBUG_FAILOVER_MESSAGES)   
4245         char obuf [64];
4246         unsigned obufix = 0;
4247         
4248 # define FMA obuf, &obufix, sizeof obuf
4249         failover_print (FMA, "(poolreq");
4250 #else
4251 # define FMA (char *)0, (unsigned *)0, 0
4252 #endif
4253
4254         if (!state -> link_to_peer ||
4255             state -> link_to_peer -> type != dhcp_type_failover_link)
4256                 return ISC_R_INVALIDARG;
4257         link = (dhcp_failover_link_t *)state -> link_to_peer;
4258
4259         if (!link -> outer || link -> outer -> type != omapi_type_connection)
4260                 return ISC_R_INVALIDARG;
4261
4262         status = (dhcp_failover_put_message
4263                   (link, link -> outer,
4264                    FTM_POOLREQ,
4265                    (failover_option_t *)0));
4266
4267 #if defined (DEBUG_FAILOVER_MESSAGES)
4268         if (status != ISC_R_SUCCESS)
4269                 failover_print (FMA, " (failed)");
4270         failover_print (FMA, ")");
4271         if (obufix) {
4272                 log_debug ("%s", obuf);
4273         }
4274 #endif
4275         return status;
4276 }
4277
4278 isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *state,
4279                                           int leases)
4280 {
4281         dhcp_failover_link_t *link;
4282         isc_result_t status;
4283 #if defined (DEBUG_FAILOVER_MESSAGES)   
4284         char obuf [64];
4285         unsigned obufix = 0;
4286         
4287 # define FMA obuf, &obufix, sizeof obuf
4288         failover_print (FMA, "(poolresp");
4289 #else
4290 # define FMA (char *)0, (unsigned *)0, 0
4291 #endif
4292
4293         if (!state -> link_to_peer ||
4294             state -> link_to_peer -> type != dhcp_type_failover_link)
4295                 return ISC_R_INVALIDARG;
4296         link = (dhcp_failover_link_t *)state -> link_to_peer;
4297
4298         if (!link -> outer || link -> outer -> type != omapi_type_connection)
4299                 return ISC_R_INVALIDARG;
4300
4301         status = (dhcp_failover_put_message
4302                   (link, link -> outer,
4303                    FTM_POOLRESP,
4304                    dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED, FMA,
4305                                               leases),
4306                    (failover_option_t *)0));
4307
4308 #if defined (DEBUG_FAILOVER_MESSAGES)
4309         if (status != ISC_R_SUCCESS)
4310                 failover_print (FMA, " (failed)");
4311         failover_print (FMA, ")");
4312         if (obufix) {
4313                 log_debug ("%s", obuf);
4314         }
4315 #endif
4316         return status;
4317 }
4318
4319 isc_result_t dhcp_failover_send_update_request (dhcp_failover_state_t *state)
4320 {
4321         dhcp_failover_link_t *link;
4322         isc_result_t status;
4323 #if defined (DEBUG_FAILOVER_MESSAGES)   
4324         char obuf [64];
4325         unsigned obufix = 0;
4326
4327 # define FMA obuf, &obufix, sizeof obuf
4328         failover_print (FMA, "(updreq");
4329 #else
4330 # define FMA (char *)0, (unsigned *)0, 0
4331 #endif
4332
4333         if (!state -> link_to_peer ||
4334             state -> link_to_peer -> type != dhcp_type_failover_link)
4335                 return ISC_R_INVALIDARG;
4336         link = (dhcp_failover_link_t *)state -> link_to_peer;
4337
4338         if (!link -> outer || link -> outer -> type != omapi_type_connection)
4339                 return ISC_R_INVALIDARG;
4340
4341         status = (dhcp_failover_put_message
4342                   (link, link -> outer,
4343                    FTM_UPDREQ,
4344                    (failover_option_t *)0));
4345
4346 #if defined (DEBUG_FAILOVER_MESSAGES)
4347         if (status != ISC_R_SUCCESS)
4348                 failover_print (FMA, " (failed)");
4349         failover_print (FMA, ")");
4350         if (obufix) {
4351                 log_debug ("%s", obuf);
4352         }
4353 #endif
4354         log_info ("Sent update request message to %s", state -> name);
4355         return status;
4356 }
4357
4358 isc_result_t dhcp_failover_send_update_request_all (dhcp_failover_state_t
4359                                                     *state)
4360 {
4361         dhcp_failover_link_t *link;
4362         isc_result_t status;
4363 #if defined (DEBUG_FAILOVER_MESSAGES)   
4364         char obuf [64];
4365         unsigned obufix = 0;
4366         
4367 # define FMA obuf, &obufix, sizeof obuf
4368         failover_print (FMA, "(updreqall");
4369 #else
4370 # define FMA (char *)0, (unsigned *)0, 0
4371 #endif
4372
4373         if (!state -> link_to_peer ||
4374             state -> link_to_peer -> type != dhcp_type_failover_link)
4375                 return ISC_R_INVALIDARG;
4376         link = (dhcp_failover_link_t *)state -> link_to_peer;
4377
4378         if (!link -> outer || link -> outer -> type != omapi_type_connection)
4379                 return ISC_R_INVALIDARG;
4380
4381         status = (dhcp_failover_put_message
4382                   (link, link -> outer,
4383                    FTM_UPDREQALL,
4384                    (failover_option_t *)0));
4385
4386 #if defined (DEBUG_FAILOVER_MESSAGES)
4387         if (status != ISC_R_SUCCESS)
4388                 failover_print (FMA, " (failed)");
4389         failover_print (FMA, ")");
4390         if (obufix) {
4391                 log_debug ("%s", obuf);
4392         }
4393 #endif
4394         log_info ("Sent update request all message to %s", state -> name);
4395         return status;
4396 }
4397
4398 isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *state)
4399 {
4400         dhcp_failover_link_t *link;
4401         isc_result_t status;
4402 #if defined (DEBUG_FAILOVER_MESSAGES)   
4403         char obuf [64];
4404         unsigned obufix = 0;
4405         
4406 # define FMA obuf, &obufix, sizeof obuf
4407         failover_print (FMA, "(upddone");
4408 #else
4409 # define FMA (char *)0, (unsigned *)0, 0
4410 #endif
4411
4412         if (!state -> link_to_peer ||
4413             state -> link_to_peer -> type != dhcp_type_failover_link)
4414                 return ISC_R_INVALIDARG;
4415         link = (dhcp_failover_link_t *)state -> link_to_peer;
4416
4417         if (!link -> outer || link -> outer -> type != omapi_type_connection)
4418                 return ISC_R_INVALIDARG;
4419
4420         status = (dhcp_failover_put_message
4421                   (link, link -> outer,
4422                    FTM_UPDDONE,
4423                    (failover_option_t *)0));
4424
4425 #if defined (DEBUG_FAILOVER_MESSAGES)
4426         if (status != ISC_R_SUCCESS)
4427                 failover_print (FMA, " (failed)");
4428         failover_print (FMA, ")");
4429         if (obufix) {
4430                 log_debug ("%s", obuf);
4431         }
4432 #endif
4433
4434         log_info ("Sent update done message to %s", state -> name);
4435
4436         /* There may be uncommitted leases at this point (since
4437            dhcp_failover_process_bind_ack() doesn't commit leases);
4438            commit the lease file. */
4439         commit_leases();
4440
4441         return status;
4442 }
4443
4444 isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state,
4445                                                failover_message_t *msg)
4446 {
4447         struct lease *lt, *lease;
4448         struct iaddr ia;
4449         int reason = FTR_MISC_REJECT;
4450         const char *message;
4451         int new_binding_state;
4452
4453         ia.len = sizeof msg -> assigned_addr;
4454         memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
4455
4456         lease = (struct lease *)0;
4457         lt = (struct lease *)0;
4458         if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
4459                 message = "unknown IP address";
4460                 reason = FTR_ILLEGAL_IP_ADDR;
4461                 goto bad;
4462         }
4463
4464         /* XXX check for conflicts. */
4465
4466         /* Install the new info. */
4467         if (!lease_copy (&lt, lease, MDL)) {
4468                 message = "no memory";
4469                 goto bad;
4470         }
4471
4472         if (msg -> options_present & FTB_CHADDR) {
4473                 if (msg -> chaddr.count > sizeof lt -> hardware_addr.hbuf) {
4474                         message = "chaddr to long";
4475                         goto bad;
4476                 }
4477                 lt -> hardware_addr.hlen = msg -> chaddr.count;
4478                 memcpy (lt -> hardware_addr.hbuf, msg -> chaddr.data,
4479                         msg -> chaddr.count);
4480         }               
4481
4482         if (msg -> options_present & FTB_CLIENT_IDENTIFIER) {
4483                 lt -> uid_len = msg -> client_identifier.count;
4484                 if (lt -> uid_len > sizeof lt -> uid_buf) {
4485                         lt -> uid_max = lt -> uid_len;
4486                         lt -> uid = dmalloc (lt -> uid_len, MDL);
4487                         if (!lt -> uid) {
4488                                 message = "no memory";
4489                                 goto bad;
4490                         }
4491                 } else {
4492                         lt -> uid_max = sizeof lt -> uid_buf;
4493                         lt -> uid = lt -> uid_buf;
4494                 }
4495                 memcpy (lt -> uid,
4496                         msg -> client_identifier.data, lt -> uid_len);
4497         }
4498                 
4499         /* XXX Times may need to be adjusted based on clock skew! */
4500         if (msg -> options_present & FTB_STOS) {
4501                 lt -> starts = msg -> stos;
4502         }
4503         if (msg -> options_present & FTB_LEASE_EXPIRY) {
4504                 lt -> ends = msg -> expiry;
4505         }
4506         if (msg -> options_present & FTB_CLTT) {
4507                 lt -> cltt = msg -> client_ltt;
4508         }
4509         if (msg -> options_present & FTB_POTENTIAL_EXPIRY) {
4510                 lt -> tsfp = msg -> potential_expiry;
4511         }
4512
4513         if (msg -> options_present & FTB_BINDING_STATUS) {
4514 #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
4515                 log_info ("processing state transition for %s: %s to %s",
4516                           piaddr (lease -> ip_addr),
4517                           binding_state_print (lease -> binding_state),
4518                           binding_state_print (msg -> binding_status));
4519 #endif
4520
4521                 /* If we're in normal state, make sure the state transition
4522                    we got is valid. */
4523                 if (state -> me.state == normal) {
4524                         new_binding_state =
4525                                 (normal_binding_state_transition_check
4526                                  (lease, state, msg -> binding_status,
4527                                   msg -> potential_expiry));
4528                         /* XXX if the transition the peer asked for isn't
4529                            XXX allowed, maybe we should make the transition
4530                            XXX into potential-conflict at this point. */
4531                 } else {
4532                         new_binding_state =
4533                                 (conflict_binding_state_transition_check
4534                                  (lease, state, msg -> binding_status,
4535                                   msg -> potential_expiry));
4536                 }
4537                 if (new_binding_state != msg -> binding_status) {
4538                         char outbuf [100];
4539
4540                         if (snprintf (outbuf, sizeof outbuf,
4541                                   "%s: invalid state transition: %s to %s",
4542                                   piaddr (lease -> ip_addr),
4543                                   binding_state_print (lease -> binding_state),
4544                                   binding_state_print (msg -> binding_status))
4545                                                 >= sizeof outbuf)
4546                                 log_fatal ("%s: impossible outbuf overflow",
4547                                         "dhcp_failover_process_bind_update");
4548
4549                         dhcp_failover_send_bind_ack (state, msg,
4550                                                      FTR_FATAL_CONFLICT,
4551                                                      outbuf);
4552                         goto out;
4553                 }
4554                 if (new_binding_state == FTS_EXPIRED ||
4555                     new_binding_state == FTS_RELEASED ||
4556                     new_binding_state == FTS_RESET)
4557                         lt -> next_binding_state = FTS_FREE;
4558                 else
4559                         lt -> next_binding_state = new_binding_state;
4560                 msg -> binding_status = lt -> next_binding_state;
4561         }
4562
4563         /* Try to install the new information. */
4564         if (!supersede_lease (lease, lt, 0, 0, 0) ||
4565             !write_lease (lease)) {
4566                 message = "database update failed";
4567               bad:
4568                 dhcp_failover_send_bind_ack (state, msg, reason, message);
4569         } else {
4570
4571                 dhcp_failover_queue_ack (state, msg);
4572         }
4573
4574       out:
4575         if (lt)
4576                 lease_dereference (&lt, MDL);
4577         if (lease)
4578                 lease_dereference (&lease, MDL);
4579
4580         return ISC_R_SUCCESS;
4581 }
4582
4583 isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state,
4584                                              failover_message_t *msg)
4585 {
4586         struct lease *lt = (struct lease *)0;
4587         struct lease *lease = (struct lease *)0;
4588         struct iaddr ia;
4589         const char *message = "no memory";
4590
4591         ia.len = sizeof msg -> assigned_addr;
4592         memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
4593
4594         if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
4595                 message = "no such lease";
4596                 goto bad;
4597         }
4598
4599         /* XXX check for conflicts. */
4600         if (msg -> options_present & FTB_REJECT_REASON) {
4601                 log_error ("bind update on %s from %s rejected: %.*s",
4602                            piaddr (ia), state -> name,
4603                            (int)((msg -> options_present & FTB_MESSAGE)
4604                                  ? msg -> message.count
4605                                  : strlen (dhcp_failover_reject_reason_print
4606                                            (msg -> reject_reason))),
4607                            (msg -> options_present & FTB_MESSAGE)
4608                            ? (const char *)(msg -> message.data)
4609                            : (dhcp_failover_reject_reason_print
4610                               (msg -> reject_reason)));
4611                 goto unqueue;
4612         }
4613
4614         /* XXX Times may need to be adjusted based on clock skew! */
4615         if (msg -> options_present & FTB_POTENTIAL_EXPIRY) {
4616                 /* XXX it could be a problem to do this directly if the
4617                    XXX lease is sorted by tsfp. */
4618                 if ((lease -> binding_state == FTS_EXPIRED ||
4619                      lease -> binding_state == FTS_RESET ||
4620                      lease -> binding_state == FTS_RELEASED) &&
4621                     (msg -> options_present & FTB_BINDING_STATUS) &&
4622                     msg -> binding_status == FTS_FREE)
4623                 {
4624                         lease -> tsfp = msg -> potential_expiry;
4625                         lease -> next_binding_state = FTS_FREE;
4626                         supersede_lease (lease, (struct lease *)0, 0, 0, 0);
4627                         write_lease (lease);
4628                         if (state -> me.state == normal)
4629                                 commit_leases ();
4630                 } else {
4631                         lease -> tsfp = msg -> potential_expiry;
4632                         if ((lease -> desired_binding_state !=
4633                              lease -> binding_state) &&
4634                             (msg -> options_present & FTB_BINDING_STATUS) &&
4635                             (msg -> binding_status ==
4636                              lease -> desired_binding_state)) {
4637                                 lease -> next_binding_state =
4638                                         lease -> desired_binding_state;
4639                                 supersede_lease (lease,
4640                                                  (struct lease *)0, 0, 0, 0);
4641                         }
4642                         write_lease (lease);
4643                         /* Commit the lease only after a two-second timeout,
4644                            so that if we get a bunch of acks in quick 
4645                            successtion (e.g., when stealing leases from the
4646                            secondary), we do not do an immediate commit for
4647                            each one. */
4648                         add_timeout (cur_time + 2,
4649                                      commit_leases_timeout, (void *)0, 0, 0);
4650                 }
4651         } else if (lease -> desired_binding_state != lease -> binding_state &&
4652                    (msg -> options_present & FTB_BINDING_STATUS) &&
4653                    msg -> binding_status == lease -> desired_binding_state) {
4654                 lease -> next_binding_state = lease -> desired_binding_state;
4655                 supersede_lease (lease, (struct lease *)0, 0, 0, 0);
4656                 write_lease (lease);
4657                 add_timeout (cur_time + 2, commit_leases_timeout,
4658                              (void *)0, 0, 0);
4659         }
4660
4661       unqueue:
4662         dhcp_failover_ack_queue_remove (state, lease);
4663
4664         /* If we are supposed to send an update done after we send
4665            this lease, go ahead and send it. */
4666         if (state -> send_update_done == lease) {
4667                 lease_dereference (&state -> send_update_done, MDL);
4668                 dhcp_failover_send_update_done (state);
4669         }
4670
4671         /* If there are updates pending, we've created space to send at
4672            least one. */
4673         dhcp_failover_send_updates (state);
4674
4675       out:
4676         lease_dereference (&lease, MDL);
4677         if (lt)
4678                 lease_dereference (&lt, MDL);
4679
4680         return ISC_R_SUCCESS;
4681
4682       bad:
4683         log_info ("bind update on %s from %s: %s.",
4684                   piaddr (ia), state -> name, message);
4685         goto out;
4686 }
4687
4688 isc_result_t dhcp_failover_generate_update_queue (dhcp_failover_state_t *state,
4689                                                   int everythingp)
4690 {
4691         struct shared_network *s;
4692         struct pool *p;
4693         struct lease *l, *n;
4694         int i;
4695         struct lease **lptr [5];
4696 #define FREE_LEASES 0
4697 #define ACTIVE_LEASES 1
4698 #define EXPIRED_LEASES 2
4699 #define ABANDONED_LEASES 3
4700 #define BACKUP_LEASES 4
4701
4702         /* First remove everything from the update and ack queues. */
4703         l = n = (struct lease *)0;
4704         if (state -> update_queue_head) {
4705                 lease_reference (&l, state -> update_queue_head, MDL);
4706                 lease_dereference (&state -> update_queue_head, MDL);
4707                 do {
4708                         l -> flags &= ~ON_UPDATE_QUEUE;
4709                         if (l -> next_pending) {
4710                                 lease_reference (&n,
4711                                                  l -> next_pending, MDL);
4712                                 lease_dereference (&l -> next_pending, MDL);
4713                         }
4714                         lease_dereference (&l, MDL);
4715                         if (n) {
4716                                 lease_reference (&l, n, MDL);
4717                                 lease_dereference (&n, MDL);
4718                         }
4719                 } while (l);
4720                 lease_dereference (&state -> update_queue_tail, MDL);
4721         }
4722
4723         if (state -> ack_queue_head) {
4724                 lease_reference (&l, state -> ack_queue_head, MDL);
4725                 lease_dereference (&state -> ack_queue_head, MDL);
4726                 do {
4727                         l -> flags &= ~ON_ACK_QUEUE;
4728                         if (l -> next_pending) {
4729                                 lease_reference (&n,
4730                                                  l -> next_pending, MDL);
4731                                 lease_dereference (&l -> next_pending, MDL);
4732                         }
4733                         lease_dereference (&l, MDL);
4734                         if (n) {
4735                                 lease_reference (&l, n, MDL);
4736                                 lease_dereference (&n, MDL);
4737                         }
4738                 } while (l);
4739                 lease_dereference (&state -> ack_queue_tail, MDL);
4740         }
4741         if (state -> send_update_done)
4742                 lease_dereference (&state -> send_update_done, MDL);
4743         state -> cur_unacked_updates = 0;
4744                         
4745         /* Loop through each pool in each shared network and call the
4746            expiry routine on the pool. */
4747         for (s = shared_networks; s; s = s -> next) {
4748             for (p = s -> pools; p; p = p -> next) {
4749                 lptr [FREE_LEASES] = &p -> free;
4750                 lptr [ACTIVE_LEASES] = &p -> active;
4751                 lptr [EXPIRED_LEASES] = &p -> expired;
4752                 lptr [ABANDONED_LEASES] = &p -> abandoned;
4753                 lptr [BACKUP_LEASES] = &p -> backup;
4754
4755                 for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
4756                     for (l = *(lptr [i]); l; l = l -> next) {
4757                         if (p -> failover_peer == state &&
4758                             ((everythingp &&
4759                               (l -> starts != MIN_TIME ||
4760                                l -> ends != MIN_TIME)) ||
4761                              l -> tstp > l -> tsfp)) {
4762                                 l -> desired_binding_state = l -> binding_state;
4763                                 dhcp_failover_queue_update (l, 0);
4764                         }
4765                     }
4766                 }
4767             }
4768         }
4769         return ISC_R_SUCCESS;
4770 }
4771
4772 isc_result_t
4773 dhcp_failover_process_update_request (dhcp_failover_state_t *state,
4774                                       failover_message_t *msg)
4775 {
4776         /* Generate a fresh update queue. */
4777         dhcp_failover_generate_update_queue (state, 0);
4778
4779         /* If there's anything on the update queue (there shouldn't be
4780            anything on the ack queue), trigger an update done message
4781            when we get an ack for that lease. */
4782         if (state -> update_queue_tail) {
4783                 lease_reference (&state -> send_update_done,
4784                                  state -> update_queue_tail, MDL);
4785                 dhcp_failover_send_updates (state);
4786                 log_info ("Update request from %s: sending update",
4787                            state -> name);
4788         } else {
4789                 /* Otherwise, there are no updates to send, so we can
4790                    just send an UPDDONE message immediately. */
4791                 dhcp_failover_send_update_done (state);
4792                 log_info ("Update request from %s: nothing pending",
4793                            state -> name);
4794         }
4795
4796         return ISC_R_SUCCESS;
4797 }
4798
4799 isc_result_t
4800 dhcp_failover_process_update_request_all (dhcp_failover_state_t *state,
4801                                           failover_message_t *msg)
4802 {
4803         /* Generate a fresh update queue that includes every lease. */
4804         dhcp_failover_generate_update_queue (state, 1);
4805
4806         if (state -> update_queue_tail) {
4807                 lease_reference (&state -> send_update_done,
4808                                  state -> update_queue_tail, MDL);
4809                 dhcp_failover_send_updates (state);
4810                 log_info ("Update request all from %s: sending update",
4811                            state -> name);
4812         } else {
4813                 /* This should really never happen, but it could happen
4814                    on a server that currently has no leases configured. */
4815                 dhcp_failover_send_update_done (state);
4816                 log_info ("Update request all from %s: nothing pending",
4817                            state -> name);
4818         }
4819
4820         return ISC_R_SUCCESS;
4821 }
4822
4823 isc_result_t
4824 dhcp_failover_process_update_done (dhcp_failover_state_t *state,
4825                                    failover_message_t *msg)
4826 {
4827         log_info ("failover peer %s: peer update completed.",
4828                   state -> name);
4829
4830         switch (state -> me.state) {
4831               case unknown_state:
4832               case partner_down:
4833               case normal:
4834               case communications_interrupted:
4835               case resolution_interrupted:
4836               case shut_down:
4837               case paused:
4838               case recover_done:
4839               case startup:
4840               case recover_wait:
4841                 break;  /* shouldn't happen. */
4842
4843                 /* We got the UPDDONE, so we can go into normal state! */
4844               case potential_conflict:
4845                 dhcp_failover_set_state (state, normal);
4846                 break;
4847
4848               case recover:
4849                 /* Wait for MCLT to expire before moving to recover_done,
4850                    except that if both peers come up in recover, there is
4851                    no point in waiting for MCLT to expire - this probably
4852                    indicates the initial startup of a newly-configured
4853                    failover pair. */
4854                 if (state -> me.stos + state -> mclt > cur_time &&
4855                     state -> partner.state != recover &&
4856                     state -> partner.state != recover_done) {
4857                         dhcp_failover_set_state (state, recover_wait);
4858 #if defined (DEBUG_FAILOVER_TIMING)
4859                         log_info ("add_timeout +%d %s",
4860                                   (int)(cur_time -
4861                                         state -> me.stos + state -> mclt),
4862                                   "dhcp_failover_recover_done");
4863 #endif
4864                         add_timeout ((int)(state -> me.stos + state -> mclt),
4865                                      dhcp_failover_recover_done,
4866                                      state,
4867                                      (tvref_t)omapi_object_reference,
4868                                      (tvunref_t)
4869                                      omapi_object_dereference);
4870                 } else
4871                         dhcp_failover_recover_done (state);
4872         }
4873
4874         return ISC_R_SUCCESS;
4875 }
4876
4877 void dhcp_failover_recover_done (void *sp)
4878 {
4879         dhcp_failover_state_t *state = sp;
4880
4881 #if defined (DEBUG_FAILOVER_TIMING)
4882         log_info ("dhcp_failover_recover_done");
4883 #endif
4884
4885         dhcp_failover_set_state (state, recover_done);
4886 }
4887
4888 #if defined (DEBUG_FAILOVER_MESSAGES)
4889 /* Print hunks of failover messages, doing line breaks as appropriate.
4890    Note that this assumes syslog is being used, rather than, e.g., the
4891    Windows NT logging facility, where just dumping the whole message in
4892    one hunk would be more appropriate. */
4893
4894 void failover_print (char *obuf,
4895                      unsigned *obufix, unsigned obufmax, const char *s)
4896 {
4897         int len = strlen (s);
4898
4899         while (len + *obufix + 1 >= obufmax) {
4900                 log_debug ("%s", obuf);
4901                 if (!*obufix) {
4902                         log_debug ("%s", s);
4903                         *obufix = 0;
4904                         return;
4905                 }
4906                 *obufix = 0;
4907         }
4908         strcpy (&obuf [*obufix], s);
4909         *obufix += len;
4910 }       
4911 #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
4912
4913 /* Taken from draft-ietf-dhc-loadb-01.txt: */
4914 /* A "mixing table" of 256 distinct values, in pseudo-random order. */
4915 unsigned char loadb_mx_tbl[256] = {
4916     251, 175, 119, 215,  81,  14,  79, 191, 103,  49,
4917     181, 143, 186, 157,   0, 232,  31,  32,  55,  60,
4918     152,  58,  17, 237, 174,  70, 160, 144, 220,  90,
4919     57,  223,  59,   3,  18, 140, 111, 166, 203, 196,
4920     134, 243, 124,  95, 222, 179, 197,  65, 180,  48,
4921      36,  15, 107,  46, 233, 130, 165,  30, 123, 161,
4922     209,  23,  97,  16,  40,  91, 219,  61, 100,  10,
4923     210, 109, 250, 127,  22, 138,  29, 108, 244,  67,
4924     207,   9, 178, 204,  74,  98, 126, 249, 167, 116,
4925     34,   77, 193, 200, 121,   5,  20, 113,  71,  35,
4926     128,  13, 182,  94,  25, 226, 227, 199,  75,  27,
4927      41, 245, 230, 224,  43, 225, 177,  26, 155, 150,
4928     212, 142, 218, 115, 241,  73,  88, 105,  39, 114,
4929      62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
4930     154, 122,  12,  84,  82, 163,  44, 139, 228, 236,
4931     205, 242, 217,  11, 187, 146, 159,  64,  86, 239,
4932     195,  42, 106, 198, 118, 112, 184, 172,  87,   2,
4933     173, 117, 176, 229, 247, 253, 137, 185,  99, 164,
4934     102, 147,  45,  66, 231,  52, 141, 211, 194, 206,
4935     246, 238,  56, 110,  78, 248,  63, 240, 189,  93,
4936      92,  51,  53, 183,  19, 171,  72,  50,  33, 104,
4937     101,  69,   8, 252,  83, 120,  76, 135,  85,  54,
4938     202, 125, 188, 213,  96, 235, 136, 208, 162, 129,
4939     190, 132, 156,  38,  47,   1,   7, 254,  24,   4,
4940     216, 131,  89,  21,  28, 133,  37, 153, 149,  80,
4941     170,  68,   6, 169, 234, 151 };
4942
4943 static unsigned char loadb_p_hash (const unsigned char *, unsigned);
4944 static unsigned char loadb_p_hash (const unsigned char *key, unsigned len)
4945 {
4946         unsigned char hash = len;
4947         int i;
4948         for(i = len; i > 0;  )
4949                 hash = loadb_mx_tbl [hash ^ (key [--i])];
4950         return hash;
4951 }
4952
4953 int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
4954 {
4955         struct option_cache *oc;
4956         struct data_string ds;
4957         unsigned char hbaix;
4958         int hm;
4959
4960         if (state -> load_balance_max_secs < ntohs (packet -> raw -> secs)) {
4961                 return 1;
4962         }
4963
4964         /* If we don't have a hash bucket array, we can't tell if this
4965            one's ours, so we assume it's not. */
4966         if (!state -> hba)
4967                 return 0;
4968
4969         oc = lookup_option (&dhcp_universe, packet -> options,
4970                             DHO_DHCP_CLIENT_IDENTIFIER);
4971         memset (&ds, 0, sizeof ds);
4972         if (oc &&
4973             evaluate_option_cache (&ds, packet, (struct lease *)0,
4974                                    (struct client_state *)0,
4975                                    packet -> options, (struct option_state *)0,
4976                                    &global_scope, oc, MDL)) {
4977                 hbaix = loadb_p_hash (ds.data, ds.len);
4978         } else {
4979                 hbaix = loadb_p_hash (packet -> raw -> chaddr,
4980                                       packet -> raw -> hlen);
4981         }
4982         hm = (state -> hba [hbaix / 8] & (1 << (hbaix & 3)));
4983         if (state -> i_am == primary)
4984                 return hm;
4985         else
4986                 return !hm;
4987 }
4988
4989 /* This deals with what to do with bind updates when
4990    we're in the normal state 
4991
4992    Note that tsfp had better be set from the latest bind update
4993    _before_ this function is called! */
4994
4995 binding_state_t
4996 normal_binding_state_transition_check (struct lease *lease,
4997                                        dhcp_failover_state_t *state,
4998                                        binding_state_t binding_state,
4999                                        u_int32_t tsfp)
5000 {
5001         binding_state_t new_state;
5002
5003         /* If there is no transition, it's no problem. */
5004         if (binding_state == lease -> binding_state)
5005                 return binding_state;
5006
5007         switch (lease -> binding_state) {
5008               case FTS_FREE:
5009               case FTS_ABANDONED:
5010                 switch (binding_state) {
5011                       case FTS_ACTIVE:
5012                       case FTS_ABANDONED:
5013                       case FTS_BACKUP:
5014                       case FTS_EXPIRED:
5015                       case FTS_RELEASED:
5016                       case FTS_RESET:
5017                         /* If the lease was free, and our peer is primary,
5018                            then it can make it active, or abandoned, or
5019                            backup.    Abandoned is treated like free in
5020                            this case. */
5021                         if (state -> i_am == secondary)
5022                                 return binding_state;
5023
5024                         /* Otherwise, it can't legitimately do any sort of
5025                            state transition.   Because the lease was free,
5026                            and the error has already been made, we allow the
5027                            peer to change its state anyway, but log a warning
5028                            message in hopes that the error will be fixed. */
5029                       case FTS_FREE: /* for compiler */
5030                         new_state = binding_state;
5031                         goto out;
5032
5033                       default:
5034                         log_fatal ("Impossible case at %s:%d.", MDL);
5035                         return FTS_RESET;
5036                 }
5037               case FTS_ACTIVE:
5038                 /* The secondary can't change the state of an active
5039                    lease. */
5040                 if (state -> i_am == primary) {
5041                         /* Except that the client may send the DHCPRELEASE
5042                            to the secondary, and we have to accept that. */
5043                         if (binding_state == FTS_RELEASED)
5044                                 return binding_state;
5045                         new_state = lease -> binding_state;
5046                         goto out;
5047                 }
5048
5049                 /* So this is only for transitions made by the primary: */
5050                 switch (binding_state) {
5051                       case FTS_FREE:
5052                       case FTS_BACKUP:
5053                         /* Can't set a lease to free or backup until the
5054                            peer agrees that it's expired. */
5055                         if (tsfp > cur_time) {
5056                                 new_state = lease -> binding_state;
5057                                 goto out;
5058                         }
5059                         return binding_state;
5060
5061                       case FTS_EXPIRED:
5062                         /* XXX 65 should be the clock skew between the peers
5063                            XXX plus a fudge factor.   This code will result
5064                            XXX in problems if MCLT is really short or the
5065                            XXX max-lease-time is really short (less than the
5066                            XXX fudge factor. */
5067                         if (lease -> ends - 65 > cur_time) {
5068                                 new_state = lease -> binding_state;
5069                                 goto out;
5070                         }
5071
5072                       case FTS_RELEASED:
5073                       case FTS_ABANDONED:
5074                       case FTS_RESET:
5075                       case FTS_ACTIVE:
5076                         return binding_state;
5077
5078                       default:
5079                         log_fatal ("Impossible case at %s:%d.", MDL);
5080                         return FTS_RESET;
5081                 }
5082                 break;
5083               case FTS_EXPIRED:
5084                 switch (binding_state) {
5085                       case FTS_BACKUP:
5086                       case FTS_FREE:
5087                         /* Can't set a lease to free or backup until the
5088                            peer agrees that it's expired. */
5089                         if (tsfp > cur_time) {
5090                                 new_state = lease -> binding_state;
5091                                 goto out;
5092                         }
5093                         return binding_state;
5094
5095                       case FTS_ACTIVE:
5096                       case FTS_RELEASED:
5097                       case FTS_ABANDONED:
5098                       case FTS_RESET:
5099                       case FTS_EXPIRED:
5100                         return binding_state;
5101
5102                       default:
5103                         log_fatal ("Impossible case at %s:%d.", MDL);
5104                         return FTS_RESET;
5105                 }
5106               case FTS_RELEASED:
5107                 switch (binding_state) {
5108                       case FTS_FREE:
5109                       case FTS_BACKUP:
5110
5111                         /* These are invalid state transitions - should we
5112                            prevent them? */
5113                       case FTS_EXPIRED:
5114                       case FTS_ABANDONED:
5115                       case FTS_RESET:
5116                       case FTS_ACTIVE:
5117                       case FTS_RELEASED:
5118                         return binding_state;
5119
5120                       default:
5121                         log_fatal ("Impossible case at %s:%d.", MDL);
5122                         return FTS_RESET;
5123                 }
5124               case FTS_RESET:
5125                 switch (binding_state) {
5126                       case FTS_FREE:
5127                       case FTS_BACKUP:
5128                         /* Can't set a lease to free or backup until the
5129                            peer agrees that it's expired. */
5130                         if (tsfp > cur_time) {
5131                                 new_state = lease -> binding_state;
5132                                 goto out;
5133                         }
5134                         return binding_state;
5135
5136                       case FTS_ACTIVE:
5137                       case FTS_EXPIRED:
5138                       case FTS_RELEASED:
5139                       case FTS_ABANDONED:
5140                       case FTS_RESET:
5141                         return binding_state;
5142
5143                       default:
5144                         log_fatal ("Impossible case at %s:%d.", MDL);
5145                         return FTS_RESET;
5146                 }
5147               case FTS_BACKUP:
5148                 switch (binding_state) {
5149                       case FTS_ACTIVE:
5150                       case FTS_ABANDONED:
5151                       case FTS_EXPIRED:
5152                       case FTS_RELEASED:
5153                       case FTS_RESET:
5154                         /* If the lease was in backup, and our peer
5155                            is secondary, then it can make it active
5156                            or abandoned. */
5157                         if (state -> i_am == primary)
5158                                 return binding_state;
5159
5160                         /* Either the primary or the secondary can
5161                            reasonably move a lease from the backup
5162                            state to the free state. */
5163                       case FTS_FREE:
5164                         return binding_state;
5165
5166                       case FTS_BACKUP:
5167                         new_state = lease -> binding_state;
5168                         goto out;
5169
5170                       default:
5171                         log_fatal ("Impossible case at %s:%d.", MDL);
5172                         return FTS_RESET;
5173                 }
5174
5175               default:
5176                 log_fatal ("Impossible case at %s:%d.", MDL);
5177                 return FTS_RESET;
5178         }
5179       out:
5180         return new_state;
5181 }
5182
5183 /* Determine whether the state transition is okay when we're potentially
5184    in conflict with the peer. */
5185 binding_state_t
5186 conflict_binding_state_transition_check (struct lease *lease,
5187                                          dhcp_failover_state_t *state,
5188                                          binding_state_t binding_state,
5189                                          u_int32_t tsfp)
5190 {
5191         binding_state_t new_state;
5192
5193         /* If there is no transition, it's no problem. */
5194         if (binding_state == lease -> binding_state)
5195                 new_state = binding_state;
5196         else {
5197                 switch (lease -> binding_state) {
5198                         /* If we think the lease is not in use, then the
5199                            state into which the partner put it is just fine,
5200                            whatever it is. */
5201                       case FTS_FREE:
5202                       case FTS_ABANDONED:
5203                       case FTS_EXPIRED:
5204                       case FTS_RELEASED:
5205                       case FTS_RESET:
5206                       case FTS_BACKUP:
5207                         new_state = binding_state;
5208                         break;
5209
5210                         /* If we think the lease *is* in use, then we're not
5211                            going to take the partner's change if the partner
5212                            thinks it's free. */
5213                       case FTS_ACTIVE:
5214                         switch (binding_state) {
5215                               case FTS_FREE:
5216                               case FTS_BACKUP:
5217                               case FTS_ABANDONED:
5218                                 new_state = lease -> binding_state;
5219                                 break;
5220
5221                               case FTS_EXPIRED:
5222                               case FTS_RELEASED:
5223                               case FTS_RESET:
5224                                 if (lease -> ends > cur_time)
5225                                         new_state =
5226                                                 lease -> binding_state;
5227                                 else
5228                                         new_state = binding_state;
5229                                 break;
5230
5231                               case FTS_ACTIVE:
5232                                 new_state = binding_state;
5233                                 break;
5234
5235                               default:
5236                                 log_fatal ("Impossible case at %s:%d.", MDL);
5237                                 return FTS_RESET;
5238                         }
5239                         break;
5240
5241                       default:
5242                         log_fatal ("Impossible case at %s:%d.", MDL);
5243                         return FTS_RESET;
5244                 }
5245         }
5246         return new_state;
5247 }
5248
5249 /* We can reallocate a lease under the following circumstances:
5250
5251    (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
5252        FTS_BACKUP, and we're secondary.
5253    (2) We're in partner_down, and the lease is not active, and we
5254        can be sure that the other server didn't make it active.
5255        We can only be sure that the server didn't make it active
5256        when we are in the partner_down state and one of the following
5257        two conditions holds:
5258        (a) in the case that the time sent from the peer is earlier than
5259            the time we entered the partner_down state, at least MCLT has
5260            gone by since we entered partner_down, or
5261        (b) in the case that the time sent from the peer is later than
5262            the time when we entered partner_down, the current time is
5263            later than the time sent from the peer by at least MCLT. */
5264
5265 int lease_mine_to_reallocate (struct lease *lease)
5266 {
5267         dhcp_failover_state_t *peer;
5268
5269         if (lease && lease -> pool &&
5270             lease -> pool -> failover_peer) {
5271                 peer = lease -> pool -> failover_peer;
5272                 switch (lease -> binding_state) {
5273                       case FTS_ACTIVE:
5274                         return 0;
5275
5276                       case FTS_FREE:
5277                         if (peer -> i_am == primary)
5278                                 return 1;
5279                         if (peer -> service_state == service_partner_down &&
5280                             (lease -> tsfp < peer -> me.stos
5281                              ? peer -> me.stos + peer -> mclt < cur_time
5282                              : lease -> tsfp + peer -> mclt < cur_time))
5283                                 return 1;
5284                         return 0;
5285
5286                       case FTS_ABANDONED:
5287                       case FTS_RESET:
5288                       case FTS_RELEASED:
5289                       case FTS_EXPIRED:
5290                         if (peer -> service_state == service_partner_down &&
5291                             (lease -> tsfp < peer -> me.stos
5292                              ? peer -> me.stos + peer -> mclt < cur_time
5293                              : lease -> tsfp + peer -> mclt < cur_time))
5294                                 return 1;
5295                         return 0;
5296                       case FTS_BACKUP:
5297                         if (peer -> i_am == secondary)
5298                                 return 1;
5299                         if (peer -> service_state == service_partner_down &&
5300                             (lease -> tsfp < peer -> me.stos
5301                              ? peer -> me.stos + peer -> mclt < cur_time
5302                              : lease -> tsfp + peer -> mclt < cur_time))
5303                                 return 1;
5304                         return 0;
5305                 }
5306                 return 0;
5307         }
5308         if (lease)
5309                 return !(lease -> binding_state != FTS_FREE &&
5310                          lease -> binding_state != FTS_BACKUP);
5311         else
5312                 return 0;
5313 }
5314
5315 static isc_result_t failover_message_reference (failover_message_t **mp,
5316                                                 failover_message_t *m,
5317                                                 const char *file, int line)
5318 {
5319         *mp = m;
5320         m -> refcnt++;
5321         return ISC_R_SUCCESS;
5322 }
5323
5324 static isc_result_t failover_message_dereference (failover_message_t **mp,
5325                                                   const char *file, int line)
5326 {
5327         failover_message_t *m;
5328         m = (*mp);
5329         m -> refcnt--;
5330         if (m -> refcnt == 0) {
5331                 if (m -> next)
5332                         failover_message_dereference (&m -> next,
5333                                                       file, line);
5334                 if (m -> chaddr.data)
5335                         dfree (m -> chaddr.data, file, line);
5336                 if (m -> client_identifier.data)
5337                         dfree (m -> client_identifier.data, file, line);
5338                 if (m -> hba.data)
5339                         dfree (m -> hba.data, file, line);
5340                 if (m -> message.data)
5341                         dfree (m -> message.data, file, line);
5342                 if (m -> reply_options.data)
5343                         dfree (m -> reply_options.data, file, line);
5344                 if (m -> request_options.data)
5345                         dfree (m -> request_options.data, file, line);
5346                 if (m -> vendor_class.data)
5347                         dfree (m -> vendor_class.data, file, line);
5348                 if (m -> vendor_options.data)
5349                         dfree (m -> vendor_options.data, file, line);
5350                 if (m -> ddns.data)
5351                         dfree (m -> ddns.data, file, line);
5352                 dfree (*mp, file, line);
5353         }
5354         *mp = 0;
5355         return ISC_R_SUCCESS;
5356 }
5357
5358 OMAPI_OBJECT_ALLOC (dhcp_failover_state, dhcp_failover_state_t,
5359                     dhcp_type_failover_state)
5360 OMAPI_OBJECT_ALLOC (dhcp_failover_listener, dhcp_failover_listener_t,
5361                     dhcp_type_failover_listener)
5362 OMAPI_OBJECT_ALLOC (dhcp_failover_link, dhcp_failover_link_t,
5363                     dhcp_type_failover_link)
5364 #endif /* defined (FAILOVER_PROTOCOL) */
5365
5366 const char *binding_state_print (enum failover_state state)
5367 {
5368         switch (state) {
5369               case FTS_FREE:
5370                 return "free";
5371                 break;
5372
5373               case FTS_ACTIVE:
5374                 return "active";
5375                 break;
5376
5377               case FTS_EXPIRED:
5378                 return "expired";
5379                 break;
5380
5381               case FTS_RELEASED:
5382                 return "released";
5383                 break;
5384
5385               case FTS_ABANDONED:
5386                 return "abandoned";
5387                 break;
5388
5389               case FTS_RESET:
5390                 return "reset";
5391                 break;
5392
5393               case FTS_BACKUP:
5394                 return "backup";
5395                 break;
5396
5397               default:
5398                 return "unknown";
5399                 break;
5400         }
5401 }