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                    &nbs