]> git.ipfire.org Git - thirdparty/dhcp.git/blame - server/failover.c
Miscellaneous bug fixes.
[thirdparty/dhcp.git] / server / failover.c
CommitLineData
c68d2a87
TL
1/* failover.c
2
3 Failover protocol support code... */
4
5/*
49733f31
TL
6 * Copyright (c) 1999-2000 Internet Software Consortium.
7 * All rights reserved.
c68d2a87 8 *
49733f31
TL
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
c68d2a87 12 *
49733f31
TL
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of The Internet Software Consortium nor the names
19 * of its contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
c68d2a87 21 *
49733f31
TL
22 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * This software has been written for the Internet Software Consortium
37 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
38 * To learn more about the Internet Software Consortium, see
39 * ``http://www.isc.org/''. To learn more about Vixie Enterprises,
40 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
41 * ``http://www.nominum.com''.
c68d2a87
TL
42 */
43
44#ifndef lint
45static char copyright[] =
25a69ca1 46"$Id: failover.c,v 1.12 2000/05/03 23:07:54 mellon Exp $ Copyright (c) 1999-2000 The Internet Software Consortium. All rights reserved.\n";
c68d2a87
TL
47#endif /* not lint */
48
49#include "dhcpd.h"
d8a417b2 50#include "version.h"
d9eefc5d 51#include <omapip/omapip_p.h>
c68d2a87
TL
52
53#if defined (FAILOVER_PROTOCOL)
54static struct hash_table *failover_hash;
d9eefc5d
TL
55static dhcp_failover_state_t *failover_states;
56static isc_result_t do_a_failover_option (omapi_object_t *,
d50d0b82 57 dhcp_failover_link_t *);
c68d2a87 58
85a53735
TL
59void dhcp_failover_startup ()
60{
61 dhcp_failover_state_t *state;
62 isc_result_t status;
63
64 for (state = failover_states; state; state = state -> next) {
9f3da356 65 dhcp_failover_state_transition (state, "startup");
85a53735
TL
66 if (state -> i_am == secondary) {
67 status = (dhcp_failover_listen
68 ((omapi_object_t *)state));
9f3da356
TL
69 if (status != ISC_R_SUCCESS) {
70 add_timeout (cur_time + 90,
71 dhcp_failover_listener_restart,
72 state);
73 }
85a53735
TL
74 } else {
75 status = (dhcp_failover_link_initiate
76 ((omapi_object_t *)state));
9f3da356
TL
77 if (status != ISC_R_SUCCESS) {
78 add_timeout (cur_time + 90,
79 dhcp_failover_reconnect, state);
80 }
85a53735
TL
81 }
82 if (status != ISC_R_SUCCESS) {
83 log_error ("failover peer %s: %s", state -> name,
84 isc_result_totext (status));
85 }
86 }
87}
88
9f3da356
TL
89void dhcp_failover_write_all_states ()
90{
91 dhcp_failover_state_t *state;
92
93 for (state = failover_states; state; state = state -> next) {
94 write_failover_state (state);
95 }
96}
97
85a53735 98isc_result_t enter_failover_peer (peer)
dd53dc5a 99 dhcp_failover_state_t *peer;
c68d2a87 100{
85a53735
TL
101 dhcp_failover_state_t *dup = (dhcp_failover_state_t *)0;
102 isc_result_t status;
103
104 status = find_failover_peer (&dup, peer -> name);
105 if (status == ISC_R_NOTFOUND) {
106 if (failover_states) {
107 omapi_object_reference
108 ((omapi_object_t **)&peer -> next,
109 (omapi_object_t *)failover_states, MDL);
110 omapi_object_dereference
111 ((omapi_object_t **)&failover_states, MDL);
112 }
113 omapi_object_reference ((omapi_object_t **)&failover_states,
114 (omapi_object_t *)peer, MDL);
115 return ISC_R_SUCCESS;
116 }
117 if (status == ISC_R_SUCCESS)
118 return ISC_R_EXISTS;
119
120 return status;
c68d2a87
TL
121}
122
dd53dc5a
TL
123isc_result_t find_failover_peer (peer, name)
124 dhcp_failover_state_t **peer;
85a53735 125 const char *name;
c68d2a87 126{
dd53dc5a 127 dhcp_failover_state_t *p;
c68d2a87 128
dd53dc5a
TL
129 for (p = failover_states; p; p = p -> next)
130 if (!strcmp (name, p -> name))
131 break;
132 if (p)
133 return omapi_object_reference ((omapi_object_t **)peer,
4bd8800e 134 (omapi_object_t *)p, MDL);
dd53dc5a 135 return ISC_R_NOTFOUND;
c68d2a87
TL
136}
137
d50d0b82
TL
138/* The failover protocol has three objects associated with it. For
139 each failover partner declaration in the dhcpd.conf file, primary
140 or secondary, there is a failover_state object. For any primary or
141 secondary state object that has a connection to its peer, there is
142 also a failover_link object, which has its own input state seperate
143 from the failover protocol state for managing the actual bytes
144 coming in off the wire. Finally, there will be one listener object
145 for every distinct port number associated with a secondary
146 failover_state object. Normally all secondary failover_state
147 objects are expected to listen on the same port number, so there
148 need be only one listener object, but if different port numbers are
149 specified for each failover object, there could be as many as one
150 listener object for each secondary failover_state object. */
151
152/* This, then, is the implemention of the failover link object. */
153
154isc_result_t dhcp_failover_link_initiate (omapi_object_t *h)
155{
156 isc_result_t status;
157 dhcp_failover_link_t *obj;
d9eefc5d 158 omapi_value_t *value = (omapi_value_t *)0;
dd53dc5a
TL
159 dhcp_failover_state_t *state;
160 omapi_object_t *o;
161 int i;
162 struct data_string ds;
85a53735
TL
163 omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
164 omapi_addr_t local_addr;
d50d0b82 165
dd53dc5a
TL
166 /* Find the failover state in the object chain. */
167 for (o = h; o -> outer; o = o -> outer)
168 ;
169 for (; o; o = o -> inner) {
170 if (o -> type == dhcp_type_failover_state)
171 break;
d9eefc5d 172 }
dd53dc5a 173 if (!o)
d50d0b82 174 return ISC_R_INVALIDARG;
dd53dc5a 175 state = (dhcp_failover_state_t *)o;
d50d0b82 176
4bd8800e 177 obj = (dhcp_failover_link_t *)dmalloc (sizeof *obj, MDL);
d50d0b82
TL
178 if (!obj)
179 return ISC_R_NOMEMORY;
180 memset (obj, 0, sizeof *obj);
181 obj -> refcnt = 1;
182 obj -> type = dhcp_type_failover_link;
4bd8800e 183 option_cache_reference (&obj -> peer_address, state -> address, MDL);
dd53dc5a 184 obj -> peer_port = state -> port;
85a53735
TL
185 omapi_object_reference ((omapi_object_t **)&obj -> state_object,
186 (omapi_object_t *)state, MDL);
dd53dc5a
TL
187
188 memset (&ds, 0, sizeof ds);
189 if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
190 (struct option_state *)0,
191 (struct option_state *)0,
4bd8800e 192 &global_scope, obj -> peer_address, MDL)) {
4bd8800e 193 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
dd53dc5a 194 return ISC_R_UNEXPECTED;
d50d0b82 195 }
dd53dc5a 196
85a53735
TL
197 /* Make an omapi address list out of a buffer containing zero or more
198 IPv4 addresses. */
199 status = omapi_addr_list_new (&addrs, ds.len / 4, MDL);
200 if (status != ISC_R_SUCCESS) {
201 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
202 return status;
203 }
204
205 for (i = 0; i < addrs -> count; i++) {
206 addrs -> addresses [i].addrtype = AF_INET;
207 addrs -> addresses [i].addrlen = sizeof (struct in_addr);
208 memcpy (addrs -> addresses [i].address,
209 &ds.data [i * 4], sizeof (struct in_addr));
210 addrs -> addresses [i].port = obj -> peer_port;
dd53dc5a 211 }
4bd8800e 212 data_string_forget (&ds, MDL);
dd53dc5a 213
85a53735
TL
214 /* Now figure out the local address that we're supposed to use. */
215 if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
216 (struct option_state *)0,
217 (struct option_state *)0,
218 &global_scope, state -> server_addr,
219 MDL)) {
220 memset (&local_addr, 0, sizeof local_addr);
221 local_addr.addrtype = AF_INET;
222 local_addr.addrlen = sizeof (struct in_addr);
9f3da356
TL
223 if (!state -> server_identifier.len) {
224 log_fatal ("failover peer %s: no identifier.",
225 state -> name);
226 }
85a53735
TL
227 } else {
228 if (ds.len != sizeof (struct in_addr)) {
229 data_string_forget (&ds, MDL);
230 omapi_object_dereference ((omapi_object_t **)&obj,
231 MDL);
232 omapi_addr_list_dereference (&addrs, MDL);
233 return ISC_R_INVALIDARG;
234 }
235 local_addr.addrtype = AF_INET;
236 local_addr.addrlen = ds.len;
237 memcpy (local_addr.address, ds.data, ds.len);
9f3da356
TL
238 if (!state -> server_identifier.len)
239 data_string_copy (&state -> server_identifier,
240 &ds, MDL);
85a53735 241 data_string_forget (&ds, MDL);
9f3da356 242 local_addr.port = 0; /* Let the O.S. choose. */
85a53735
TL
243 }
244
245 status = omapi_connect_list ((omapi_object_t *)obj,
246 addrs, &local_addr);
247 omapi_addr_list_dereference (&addrs, MDL);
248
249 /* If we didn't succeed in starting to connect, fail. */
d50d0b82 250 if (status != ISC_R_SUCCESS) {
85a53735 251 loselose:
4bd8800e 252 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
d50d0b82
TL
253 return status;
254 }
4bd8800e 255 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
d50d0b82
TL
256 return ISC_R_SUCCESS;
257}
258
259isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
260 const char *name, va_list ap)
261{
262 isc_result_t status;
263 dhcp_failover_link_t *link;
264 omapi_object_t *c;
265 u_int16_t nlen;
266 u_int32_t vlen;
9f3da356 267 dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
d50d0b82
TL
268
269 if (h -> type != dhcp_type_failover_link) {
270 /* XXX shouldn't happen. Put an assert here? */
271 return ISC_R_UNEXPECTED;
272 }
273 link = (dhcp_failover_link_t *)h;
274
85a53735 275 if (!strcmp (name, "connect")) {
9f3da356 276 /* If we're primary, send the connect message. */
85a53735
TL
277 status = dhcp_failover_send_connect (h);
278 if (status != ISC_R_SUCCESS)
9f3da356 279 omapi_disconnect (h -> outer, 1);
85a53735
TL
280 return status;
281 }
282
9f3da356
TL
283 if (!strcmp (name, "disconnect")) {
284 if (link -> state_object &&
285 link -> state_object -> i_am == primary) {
286 add_timeout (cur_time + 90, dhcp_failover_reconnect,
287 link -> state_object);
288 /* XXX this is a reference! */
289 }
290 return ISC_R_SUCCESS;
291 }
292
d50d0b82
TL
293 /* Not a signal we recognize? */
294 if (strcmp (name, "ready")) {
d9eefc5d 295 if (h -> inner && h -> inner -> type -> signal_handler)
9f3da356
TL
296 return (*(h -> inner -> type -> signal_handler))
297 (h -> inner, name, ap);
d50d0b82
TL
298 return ISC_R_NOTFOUND;
299 }
300
d9eefc5d 301 if (!h -> outer || h -> outer -> type != omapi_type_connection)
d50d0b82 302 return ISC_R_INVALIDARG;
d9eefc5d 303 c = h -> outer;
d50d0b82
TL
304
305 /* We get here because we requested that we be woken up after
306 some number of bytes were read, and that number of bytes
307 has in fact been read. */
d9eefc5d 308 switch (link -> state) {
d50d0b82 309 case dhcp_flink_start:
d9eefc5d 310 link -> state = dhcp_flink_message_length_wait;
d50d0b82
TL
311 if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS)
312 break;
313 case dhcp_flink_message_length_wait:
25a69ca1 314 next_message:
d9eefc5d 315 link -> state = dhcp_flink_message_wait;
4bd8800e 316 link -> imsg = dmalloc (sizeof (failover_message_t), MDL);
d9eefc5d 317 if (!link -> imsg) {
d50d0b82 318 dhcp_flink_fail:
d9eefc5d 319 if (link -> imsg) {
4bd8800e 320 dfree (link -> imsg, MDL);
d9eefc5d
TL
321 link -> imsg = (failover_message_t *)0;
322 }
d50d0b82
TL
323 link -> state = dhcp_flink_disconnected;
324 omapi_disconnect (c, 1);
325 /* XXX just blow away the protocol state now?
326 XXX or will disconnect blow it away? */
327 return ISC_R_UNEXPECTED;
328 }
25a69ca1 329 memset (link -> imsg, 0, sizeof (failover_message_t));
d9eefc5d
TL
330 /* Get the length: */
331 omapi_connection_get_uint16 (c, &link -> imsg_len);
332 link -> imsg_count = 0; /* Bytes read. */
333
334 /* Maximum of 2048 bytes in any failover message. */
335 if (link -> imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE)
336 goto dhcp_flink_fail;
d50d0b82 337
9f3da356 338 if ((omapi_connection_require (c, link -> imsg_len - 2U)) !=
d50d0b82
TL
339 ISC_R_SUCCESS)
340 break;
341 case dhcp_flink_message_wait:
342 /* Read in the message. At this point we have the
343 entire message in the input buffer. For each
344 incoming value ID, set a bit in the bitmask
345 indicating that we've gotten it. Maybe flag an
346 error message if the bit is already set. Once
347 we're done reading, we can check the bitmask to
348 make sure that the required fields for each message
349 have been included. */
350
351 link -> imsg_count += 2; /* Count the length as read. */
352
353 /* Get message type. */
354 omapi_connection_copyout (&link -> imsg -> type, c, 1);
355 link -> imsg_count++;
356
357 /* Get message payload offset. */
358 omapi_connection_copyout (&link -> imsg_payoff, c, 1);
359 link -> imsg_count++;
360
361 /* Get message time. */
362 omapi_connection_get_uint32 (c, &link -> imsg -> time);
363 link -> imsg_count += 4;
364
365 /* Get transaction ID. */
366 omapi_connection_get_uint32 (c, &link -> imsg -> xid);
367 link -> imsg_count += 4;
368
369 /* Skip over any portions of the message header that we
370 don't understand. */
d9eefc5d 371 if (link -> imsg_payoff - link -> imsg_count) {
d50d0b82 372 omapi_connection_copyout ((unsigned char *)0, c,
d9eefc5d 373 (link -> imsg_payoff -
d50d0b82 374 link -> imsg_count));
d9eefc5d 375 link -> imsg_count = link -> imsg_payoff;
d50d0b82
TL
376 }
377
d50d0b82
TL
378 /* Now start sucking options off the wire. */
379 while (link -> imsg_count < link -> imsg_len) {
380 if (do_a_failover_option (c, link) != ISC_R_SUCCESS)
381 goto dhcp_flink_fail;
382 }
383
9f3da356
TL
384 /* If it's a connect message, try to associate it with
385 a state object. */
386 /* XXX this should be authenticated! */
387 if (link -> imsg -> type == FTM_CONNECT) {
388 /* See if we can find a failover_state object that
389 matches this connection. This message should only
390 be received by a secondary from a primary. */
391 for (s = failover_states; s; s = s -> next) {
392 if (dhcp_failover_state_match
393 (s, (u_int8_t *)&link -> imsg -> server_addr,
394 sizeof link -> imsg -> server_addr))
395 state = s;
396 }
397
398 /* If we can't find a failover protocol state
399 for this remote host, drop the connection */
400 if (!state) {
401 /* XXX Send a refusal message first?
402 XXX Look in protocol spec for guidance. */
403 log_error ("Failover CONNECT from %s %d.%d.%d.%d",
404 "unknown server",
405 ((u_int8_t *)
406 (&link -> imsg -> server_addr)) [0],
407 ((u_int8_t *)
408 (&link -> imsg -> server_addr)) [1],
409 ((u_int8_t *)
410 (&link -> imsg -> server_addr)) [2],
411 ((u_int8_t *)
412 (&link -> imsg -> server_addr)) [3]);
413 dhcp_failover_send_connectack
414 ((omapi_object_t *)link,
415 FTR_INVALID_PARTNER);
416 omapi_disconnect (c, 0);
25a69ca1 417 link -> state = dhcp_flink_disconnected;
9f3da356
TL
418 return ISC_R_SUCCESS;
419 }
420
421 if (!link -> state_object)
422 omapi_object_reference
423 ((omapi_object_t **)&link -> state_object,
424 (omapi_object_t *)state, MDL);
425 option_cache_reference
426 (&link -> peer_address, state -> address, MDL);
427 }
428
429 /* If we don't have a state object at this point, it's
430 some kind of bogus situation, so just drop the
431 connection. */
432 if (!link -> state_object) {
433 omapi_disconnect (c, 1);
25a69ca1 434 link -> state = dhcp_flink_disconnected;
9f3da356
TL
435 return ISC_R_INVALIDARG;
436 }
437
d50d0b82
TL
438 /* Once we have the entire message, and we've validated
439 it as best we can here, pass it to the parent. */
9f3da356
TL
440 omapi_signal ((omapi_object_t *)link -> state_object,
441 "message", link);
25a69ca1
TL
442 link -> state = dhcp_flink_message_length_wait;
443 dfree (link -> imsg, MDL);
444 link -> imsg = (failover_message_t *)0;
445 /* XXX This is dangerous because we could get into a tight
446 XXX loop reading input without servicing any other stuff.
447 XXX There needs to be a way to relinquish control but
448 XXX get it back immediately if there's no other work to
449 XXX do. */
450 if ((omapi_connection_require (c, 2)) == ISC_R_SUCCESS)
451 goto next_message;
d50d0b82
TL
452 break;
453
454 default:
455 /* XXX should never get here. Assertion? */
456 break;
457 }
458 return ISC_R_SUCCESS;
459}
460
461static isc_result_t do_a_failover_option (c, link)
d9eefc5d 462 omapi_object_t *c;
d50d0b82
TL
463 dhcp_failover_link_t *link;
464{
465 u_int16_t option_code;
466 u_int16_t option_len;
d9eefc5d
TL
467 unsigned char *op;
468 unsigned op_size;
469 unsigned op_count;
470 int i;
9f3da356 471 isc_result_t status;
d50d0b82
TL
472
473 if (link -> imsg_count + 2 > link -> imsg_len) {
474 log_error ("FAILOVER: message overflow at option code.");
475 return ISC_R_PROTOCOLERROR;
476 }
477
478 /* Get option code. */
479 omapi_connection_get_uint16 (c, &option_code);
480 link -> imsg_count += 2;
481
9f3da356
TL
482 if (link -> imsg_count + 2 > link -> imsg_len) {
483 log_error ("FAILOVER: message overflow at length.");
484 return ISC_R_PROTOCOLERROR;
485 }
486
d50d0b82
TL
487 /* Get option length. */
488 omapi_connection_get_uint16 (c, &option_len);
489 link -> imsg_count += 2;
490
491 if (link -> imsg_count + option_len > link -> imsg_len) {
9f3da356 492 log_error ("FAILOVER: message overflow at data.");
d50d0b82
TL
493 return ISC_R_PROTOCOLERROR;
494 }
495
496 /* If it's an unknown code, skip over it. */
497 if (option_code > FTO_MAX) {
498#if defined (FAILOVER_PROTOCOL_DEBUG) && defined (FAILOVER_DEBUG_VERBOSE)
499 log_debug (" option code %d len %d (not recognized)",
500 option_code, option_len);
501#endif
502 omapi_connection_copyout ((unsigned char *)0, c, option_len);
d9eefc5d 503 link -> imsg_count += option_len;
d50d0b82
TL
504 return ISC_R_SUCCESS;
505 }
506
507 /* If it's the digest, do it now. */
508 if (ft_options [option_code].type == FT_DIGEST) {
509 link -> imsg_count += option_len;
510 if (link -> imsg_count != link -> imsg_len) {
511 log_error ("FAILOVER: digest not at end of message");
512 return ISC_R_PROTOCOLERROR;
513 }
514#if defined (FAILOVER_PROTOCOL_DEBUG) && defined (FAILOVER_DEBUG_VERBOSE)
515 log_debug (" option %s len %d",
516 ft_options [option_code].name, option_len);
517#endif
518 /* For now, just dump it. */
519 omapi_connection_copyout ((unsigned char *)0, c, option_len);
520 return ISC_R_SUCCESS;
521 }
522
523 /* Only accept an option once. */
d9eefc5d 524 if (link -> imsg -> options_present & ft_options [option_code].bit) {
d50d0b82
TL
525 log_error ("FAILOVER: duplicate option %s",
526 ft_options [option_code].name);
527 return ISC_R_PROTOCOLERROR;
528 }
529
530 /* Make sure the option is appropriate for this type of message.
531 Really, any option is generally allowed for any message, and the
532 cases where this is not true are too complicated to represent in
533 this way - what this code is doing is to just avoid saving the
534 value of an option we don't have any way to use, which allows
535 us to make the failover_message structure smaller. */
536 if (ft_options [option_code].bit &&
9f3da356
TL
537 !(fto_allowed [link -> imsg -> type] &
538 ft_options [option_code].bit)) {
d50d0b82 539 omapi_connection_copyout ((unsigned char *)0, c, option_len);
d9eefc5d 540 link -> imsg_count += option_len;
d50d0b82
TL
541 return ISC_R_SUCCESS;
542 }
543
544 /* Figure out how many elements, how big they are, and where
545 to store them. */
546 if (ft_options [option_code].num_present) {
547 /* If this option takes a fixed number of elements,
548 we expect the space for them to be preallocated,
549 and we can just read the data in. */
550
9f3da356 551 op = ((unsigned char *)link -> imsg) +
d9eefc5d 552 ft_options [option_code].offset;
d50d0b82
TL
553 op_size = ft_sizes [ft_options [option_code].type];
554 op_count = ft_options [option_code].num_present;
555
d9eefc5d 556 if (option_len != op_size * op_count) {
d50d0b82 557 log_error ("FAILOVER: option size (%d:%d), option %s",
d9eefc5d 558 option_len,
d50d0b82
TL
559 (ft_sizes [ft_options [option_code].type] *
560 ft_options [option_code].num_present),
561 ft_options [option_code].name);
562 return ISC_R_PROTOCOLERROR;
563 }
564 } else {
d9eefc5d 565 failover_option_t *fo;
d50d0b82
TL
566
567 /* FT_DDNS* are special - one or two bytes of status
568 followed by the client FQDN. */
569 if (ft_options [option_code].type == FT_DDNS1 ||
570 ft_options [option_code].type == FT_DDNS1) {
d9eefc5d
TL
571 ddns_fqdn_t *ddns =
572 ((ddns_fqdn_t *)
d50d0b82
TL
573 (((char *)&link -> imsg) +
574 ft_options [option_code].offset));
575
576 op_count = (ft_options [option_code].type == FT_DDNS1
577 ? 1 : 2);
578
579 omapi_connection_copyout (&ddns -> codes [0],
580 c, op_count);
9f3da356 581 link -> imsg_count += op_count;
d50d0b82
TL
582 if (op_count == 1)
583 ddns -> codes [1] = 0;
584 op_size = 1;
d9eefc5d 585 op_count = option_len - op_count;
d50d0b82
TL
586
587 ddns -> length = op_count;
4bd8800e 588 ddns -> data = dmalloc (op_count, MDL);
d50d0b82
TL
589 if (!ddns -> data) {
590 log_error ("FAILOVER: no memory getting%s(%d)",
591 " DNS data ", op_count);
592
593 /* Actually, NO_MEMORY, but if we lose here
594 we have to drop the connection. */
595 return ISC_R_PROTOCOLERROR;
596 }
597 omapi_connection_copyout (ddns -> data, c, op_count);
598 goto out;
599 }
d9eefc5d 600
d50d0b82
TL
601 /* A zero for num_present means that any number of
602 elements can appear, so we have to figure out how
603 many we got from the length of the option, and then
604 fill out a failover_option structure describing the
605 data. */
606 op_size = ft_sizes [ft_options [option_code].type];
607
608 /* Make sure that option data length is a multiple of the
609 size of the data type being sent. */
d9eefc5d
TL
610 if (op_size > 1 && option_len % op_size) {
611 log_error ("FAILOVER: option_len %d not %s%d",
612 option_len, "multiple of ", op_size);
d50d0b82
TL
613 return ISC_R_PROTOCOLERROR;
614 }
615
d9eefc5d 616 op_count = option_len / op_size;
d50d0b82 617
d9eefc5d 618 fo = ((failover_option_t *)
d50d0b82
TL
619 (((char *)&link -> imsg) +
620 ft_options [option_code].offset));
621
622 fo -> count = op_count;
4bd8800e 623 fo -> data = dmalloc (option_len, MDL);
d50d0b82
TL
624 if (!fo -> data) {
625 log_error ("FAILOVER: no memory getting %s (%d)",
626 "option data", op_count);
627
628 return ISC_R_PROTOCOLERROR;
629 }
630 op = fo -> data;
631 }
632
633 /* For single-byte message values and multi-byte values that
634 don't need swapping, just read them in all at once. */
635 if (op_size == 1 || ft_options [option_code].type == FT_IPADDR) {
636 omapi_connection_copyout ((unsigned char *)op, c, option_len);
9f3da356 637 link -> imsg_count += option_len;
d50d0b82
TL
638 goto out;
639 }
640
641 /* For values that require swapping, read them in one at a time
642 using routines that swap bytes. */
643 for (i = 0; i < op_count; i++) {
644 switch (ft_options [option_code].type) {
645 case FT_UINT32:
d9eefc5d 646 omapi_connection_get_uint32 (c, (u_int32_t *)op);
d50d0b82 647 op += 4;
9f3da356 648 link -> imsg_count += 4;
d50d0b82
TL
649 break;
650
651 case FT_UINT16:
d9eefc5d 652 omapi_connection_get_uint16 (c, (u_int16_t *)op);
d50d0b82 653 op += 2;
9f3da356 654 link -> imsg_count += 2;
d50d0b82
TL
655 break;
656
657 default:
658 /* Everything else should have been handled
659 already. */
660 log_error ("FAILOVER: option %s: bad type %d",
661 ft_options [option_code].name,
662 ft_options [option_code].type);
663 return ISC_R_PROTOCOLERROR;
664 }
665 }
666 out:
667 /* Remember that we got this option. */
d9eefc5d 668 link -> imsg -> options_present |= ft_options [option_code].bit;
d50d0b82
TL
669 return ISC_R_SUCCESS;
670}
671
672isc_result_t dhcp_failover_link_set_value (omapi_object_t *h,
673 omapi_object_t *id,
674 omapi_data_string_t *name,
675 omapi_typed_data_t *value)
676{
677 if (h -> type != omapi_type_protocol)
678 return ISC_R_INVALIDARG;
679
680 /* Never valid to set these. */
d9eefc5d 681 if (!omapi_ds_strcmp (name, "link-port") ||
d50d0b82
TL
682 !omapi_ds_strcmp (name, "link-name") ||
683 !omapi_ds_strcmp (name, "link-state"))
684 return ISC_R_NOPERM;
685
686 if (h -> inner && h -> inner -> type -> set_value)
687 return (*(h -> inner -> type -> set_value))
688 (h -> inner, id, name, value);
689 return ISC_R_NOTFOUND;
690}
691
692isc_result_t dhcp_failover_link_get_value (omapi_object_t *h,
693 omapi_object_t *id,
694 omapi_data_string_t *name,
695 omapi_value_t **value)
696{
697 dhcp_failover_link_t *link;
698
699 if (h -> type != omapi_type_protocol)
700 return ISC_R_INVALIDARG;
701 link = (dhcp_failover_link_t *)h;
702
d9eefc5d
TL
703 if (!omapi_ds_strcmp (name, "link-port")) {
704 return omapi_make_int_value (value, name,
4bd8800e 705 (int)link -> peer_port, MDL);
d50d0b82
TL
706 } else if (!omapi_ds_strcmp (name, "link-state")) {
707 if (link -> state < 0 ||
d9eefc5d 708 link -> state >= dhcp_flink_state_max)
4bd8800e
TL
709 return omapi_make_string_value (value, name,
710 "invalid link state",
711 MDL);
d50d0b82
TL
712 return omapi_make_string_value
713 (value, name,
4bd8800e 714 dhcp_flink_state_names [link -> state], MDL);
d50d0b82
TL
715 }
716
717 if (h -> inner && h -> inner -> type -> get_value)
718 return (*(h -> inner -> type -> get_value))
719 (h -> inner, id, name, value);
720 return ISC_R_NOTFOUND;
721}
722
4bd8800e
TL
723isc_result_t dhcp_failover_link_destroy (omapi_object_t *h,
724 const char *file, int line)
d50d0b82 725{
d9eefc5d 726 dhcp_failover_link_t *link;
d50d0b82
TL
727 if (h -> type != dhcp_type_failover_link)
728 return ISC_R_INVALIDARG;
d9eefc5d
TL
729 link = (dhcp_failover_link_t *)h;
730 if (link -> imsg) {
4bd8800e 731 dfree (link -> imsg, file, line);
d9eefc5d
TL
732 link -> imsg = (failover_message_t *)0;
733 }
85a53735
TL
734 if (link -> state_object)
735 omapi_object_dereference
736 ((omapi_object_t **)&link -> state_object, MDL);
d50d0b82
TL
737 return ISC_R_SUCCESS;
738}
739
740/* Write all the published values associated with the object through the
741 specified connection. */
742
743isc_result_t dhcp_failover_link_stuff_values (omapi_object_t *c,
744 omapi_object_t *id,
d9eefc5d 745 omapi_object_t *l)
d50d0b82
TL
746{
747 dhcp_failover_link_t *link;
d9eefc5d 748 isc_result_t status;
d50d0b82 749
d9eefc5d 750 if (l -> type != dhcp_type_failover_link)
d50d0b82 751 return ISC_R_INVALIDARG;
d9eefc5d 752 link = (dhcp_failover_link_t *)l;
d50d0b82 753
d9eefc5d 754 status = omapi_connection_put_name (c, "link-port");
d50d0b82
TL
755 if (status != ISC_R_SUCCESS)
756 return status;
d9eefc5d 757 status = omapi_connection_put_uint32 (c, sizeof (int));
d50d0b82
TL
758 if (status != ISC_R_SUCCESS)
759 return status;
d9eefc5d 760 status = omapi_connection_put_uint32 (c, link -> peer_port);
d50d0b82
TL
761 if (status != ISC_R_SUCCESS)
762 return status;
763
d50d0b82
TL
764 status = omapi_connection_put_name (c, "link-state");
765 if (status != ISC_R_SUCCESS)
766 return status;
767 if (link -> state < 0 ||
d9eefc5d 768 link -> state >= dhcp_flink_state_max)
d50d0b82
TL
769 status = omapi_connection_put_string (c, "invalid link state");
770 else
771 status = (omapi_connection_put_string
d8a417b2 772 (c, dhcp_flink_state_names [link -> state]));
d50d0b82
TL
773 if (status != ISC_R_SUCCESS)
774 return status;
775
d9eefc5d
TL
776 if (link -> inner && link -> inner -> type -> stuff_values)
777 return (*(link -> inner -> type -> stuff_values)) (c, id,
778 link -> inner);
d50d0b82
TL
779 return ISC_R_SUCCESS;
780}
781
782/* Set up a listener for the omapi protocol. The handle stored points to
783 a listener object, not a protocol object. */
784
d9eefc5d 785isc_result_t dhcp_failover_listen (omapi_object_t *h)
d50d0b82
TL
786{
787 isc_result_t status;
d9eefc5d 788 dhcp_failover_listener_t *obj;
d50d0b82 789 omapi_value_t *value = (omapi_value_t *)0;
85a53735
TL
790 omapi_addr_t local_addr;
791 unsigned long port;
d50d0b82
TL
792
793 status = omapi_get_value_str (h, (omapi_object_t *)0,
794 "local-port", &value);
795 if (status != ISC_R_SUCCESS)
796 return status;
d9eefc5d 797 if (!value -> value) {
4bd8800e 798 omapi_value_dereference (&value, MDL);
d9eefc5d
TL
799 return ISC_R_INVALIDARG;
800 }
d50d0b82 801
d9eefc5d 802 status = omapi_get_int_value (&port, value -> value);
4bd8800e 803 omapi_value_dereference (&value, MDL);
d50d0b82
TL
804 if (status != ISC_R_SUCCESS)
805 return status;
85a53735
TL
806 local_addr.port = port;
807
808 status = omapi_get_value_str (h, (omapi_object_t *)0,
809 "local-address", &value);
810 if (status != ISC_R_SUCCESS)
811 return status;
812 if (!value -> value) {
813 nogood:
814 omapi_value_dereference (&value, MDL);
815 return ISC_R_INVALIDARG;
816 }
817
818 if (value -> value -> type != omapi_datatype_data ||
819 value -> value -> u.buffer.len != sizeof (struct in_addr))
820 goto nogood;
821
822 memcpy (local_addr.address, value -> value -> u.buffer.value,
823 value -> value -> u.buffer.len);
824 local_addr.addrlen = value -> value -> u.buffer.len;
825 local_addr.addrtype = AF_INET;
826
827 omapi_value_dereference (&value, MDL);
d50d0b82 828
4bd8800e 829 obj = (dhcp_failover_listener_t *)dmalloc (sizeof *obj, MDL);
d50d0b82
TL
830 if (!obj)
831 return ISC_R_NOMEMORY;
832 memset (obj, 0, sizeof *obj);
833 obj -> refcnt = 1;
834 obj -> type = dhcp_type_failover_listener;
85a53735 835 obj -> local_port = local_addr.port;
d50d0b82 836
85a53735 837 status = omapi_listen_addr ((omapi_object_t *)obj, &local_addr, 1);
d50d0b82
TL
838 if (status != ISC_R_SUCCESS)
839 return status;
840
4bd8800e
TL
841 status = omapi_object_reference (&h -> outer,
842 (omapi_object_t *)obj, MDL);
d50d0b82 843 if (status != ISC_R_SUCCESS) {
4bd8800e 844 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
d50d0b82
TL
845 return status;
846 }
4bd8800e 847 status = omapi_object_reference (&obj -> inner, h, MDL);
d50d0b82 848 if (status != ISC_R_SUCCESS) {
4bd8800e 849 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
d50d0b82
TL
850 return status;
851 }
852
85a53735 853 status = omapi_object_dereference ((omapi_object_t **)&obj, MDL);
d50d0b82
TL
854 return status;
855}
856
857/* Signal handler for protocol listener - if we get a connect signal,
858 create a new protocol connection, otherwise pass the signal down. */
859
860isc_result_t dhcp_failover_listener_signal (omapi_object_t *o,
861 const char *name, va_list ap)
862{
863 isc_result_t status;
864 omapi_connection_object_t *c;
d9eefc5d
TL
865 dhcp_failover_link_t *obj;
866 dhcp_failover_listener_t *p;
85a53735 867 dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
d50d0b82
TL
868
869 if (!o || o -> type != dhcp_type_failover_listener)
870 return ISC_R_INVALIDARG;
d9eefc5d 871 p = (dhcp_failover_listener_t *)o;
d50d0b82
TL
872
873 /* Not a signal we recognize? */
874 if (strcmp (name, "connect")) {
875 if (p -> inner && p -> inner -> type -> signal_handler)
876 return (*(p -> inner -> type -> signal_handler))
877 (p -> inner, name, ap);
878 return ISC_R_NOTFOUND;
879 }
880
881 c = va_arg (ap, omapi_connection_object_t *);
882 if (!c || c -> type != omapi_type_connection)
883 return ISC_R_INVALIDARG;
884
4bd8800e 885 obj = (dhcp_failover_link_t *)dmalloc (sizeof *obj, MDL);
d50d0b82
TL
886 if (!obj)
887 return ISC_R_NOMEMORY;
888 memset (obj, 0, sizeof *obj);
889 obj -> refcnt = 1;
d9eefc5d 890 obj -> type = dhcp_type_failover_link;
d9eefc5d 891 obj -> peer_port = ntohs (c -> remote_addr.sin_port);
d50d0b82 892
4bd8800e
TL
893 status = omapi_object_reference (&obj -> outer,
894 (omapi_object_t *)c, MDL);
d50d0b82
TL
895 if (status != ISC_R_SUCCESS) {
896 lose:
4bd8800e 897 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
d9eefc5d 898 omapi_disconnect ((omapi_object_t *)c, 1);
d50d0b82
TL
899 return status;
900 }
901
4bd8800e
TL
902 status = omapi_object_reference (&c -> inner,
903 (omapi_object_t *)obj, MDL);
d50d0b82
TL
904 if (status != ISC_R_SUCCESS)
905 goto lose;
906
4bd8800e 907 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
d50d0b82
TL
908 return status;
909}
910
911isc_result_t dhcp_failover_listener_set_value (omapi_object_t *h,
912 omapi_object_t *id,
913 omapi_data_string_t *name,
914 omapi_typed_data_t *value)
915{
916 if (h -> type != dhcp_type_failover_listener)
917 return ISC_R_INVALIDARG;
918
919 if (h -> inner && h -> inner -> type -> set_value)
920 return (*(h -> inner -> type -> set_value))
921 (h -> inner, id, name, value);
922 return ISC_R_NOTFOUND;
923}
924
925isc_result_t dhcp_failover_listener_get_value (omapi_object_t *h,
926 omapi_object_t *id,
927 omapi_data_string_t *name,
928 omapi_value_t **value)
929{
930 if (h -> type != dhcp_type_failover_listener)
931 return ISC_R_INVALIDARG;
932
933 if (h -> inner && h -> inner -> type -> get_value)
934 return (*(h -> inner -> type -> get_value))
935 (h -> inner, id, name, value);
936 return ISC_R_NOTFOUND;
937}
938
939isc_result_t dhcp_failover_listener_destroy (omapi_object_t *h,
4bd8800e 940 const char *file, int line)
d50d0b82
TL
941{
942 if (h -> type != dhcp_type_failover_listener)
943 return ISC_R_INVALIDARG;
944 return ISC_R_SUCCESS;
945}
946
947/* Write all the published values associated with the object through the
948 specified connection. */
949
950isc_result_t dhcp_failover_listener_stuff (omapi_object_t *c,
951 omapi_object_t *id,
952 omapi_object_t *p)
953{
954 int i;
955
956 if (p -> type != dhcp_type_failover_listener)
957 return ISC_R_INVALIDARG;
958
959 if (p -> inner && p -> inner -> type -> stuff_values)
960 return (*(p -> inner -> type -> stuff_values)) (c, id,
961 p -> inner);
962 return ISC_R_SUCCESS;
963}
964
965/* Set up master state machine for the failover protocol. */
966
d9eefc5d 967isc_result_t dhcp_failover_register (omapi_object_t *h)
d50d0b82
TL
968{
969 isc_result_t status;
d9eefc5d 970 dhcp_failover_state_t *obj;
d50d0b82
TL
971 unsigned long port;
972 omapi_value_t *value = (omapi_value_t *)0;
973
974 status = omapi_get_value_str (h, (omapi_object_t *)0,
975 "local-port", &value);
976 if (status != ISC_R_SUCCESS)
977 return status;
d9eefc5d 978 if (!value -> value) {
4bd8800e 979 omapi_value_dereference (&value, MDL);
d9eefc5d
TL
980 return ISC_R_INVALIDARG;
981 }
d50d0b82 982
d9eefc5d 983 status = omapi_get_int_value (&port, value -> value);
4bd8800e 984 omapi_value_dereference (&value, MDL);
d50d0b82
TL
985 if (status != ISC_R_SUCCESS)
986 return status;
987
4bd8800e 988 obj = (dhcp_failover_state_t *)dmalloc (sizeof *obj, MDL);
d50d0b82
TL
989 if (!obj)
990 return ISC_R_NOMEMORY;
991 memset (obj, 0, sizeof *obj);
992 obj -> refcnt = 1;
993 obj -> type = dhcp_type_failover_state;
d9eefc5d 994 obj -> listen_port = port;
d50d0b82
TL
995
996 status = omapi_listen ((omapi_object_t *)obj, port, 1);
4bd8800e 997 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
d50d0b82
TL
998 if (status != ISC_R_SUCCESS)
999 return status;
1000
1001 status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj,
4bd8800e 1002 MDL);
d50d0b82 1003 if (status != ISC_R_SUCCESS) {
4bd8800e 1004 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
d50d0b82
TL
1005 return status;
1006 }
4bd8800e 1007 status = omapi_object_reference (&obj -> inner, h, MDL);
d50d0b82 1008 if (status != ISC_R_SUCCESS) {
4bd8800e 1009 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
d50d0b82
TL
1010 return status;
1011 }
1012
1013 return status;
1014}
1015
1016/* Signal handler for protocol state machine. */
1017
1018isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
1019 const char *name, va_list ap)
1020{
1021 isc_result_t status;
1022 omapi_connection_object_t *c;
1023 omapi_protocol_object_t *obj;
d9eefc5d 1024 dhcp_failover_state_t *state;
9f3da356 1025 dhcp_failover_link_t *link;
d50d0b82
TL
1026 char *peer_name;
1027
1028 if (!o || o -> type != dhcp_type_failover_state)
1029 return ISC_R_INVALIDARG;
d9eefc5d 1030 state = (dhcp_failover_state_t *)o;
d50d0b82
TL
1031
1032 /* Not a signal we recognize? */
9f3da356 1033 if (strcmp (name, "disconnect") &&
d50d0b82 1034 strcmp (name, "message")) {
d9eefc5d
TL
1035 if (state -> inner && state -> inner -> type -> signal_handler)
1036 return (*(state -> inner -> type -> signal_handler))
1037 (state -> inner, name, ap);
d50d0b82
TL
1038 return ISC_R_NOTFOUND;
1039 }
1040
85a53735
TL
1041 /* Handle connect signals by seeing what state we're in
1042 and potentially doing a state transition. */
9f3da356
TL
1043 if (!strcmp (name, "disconnect")) {
1044 link = va_arg (ap, dhcp_failover_link_t *);
1045
1046 omapi_object_dereference (&state -> link_to_peer, MDL);
1047 dhcp_failover_state_transition (state, "disconnect");
1048 if (state -> i_am == primary)
1049 add_timeout (cur_time + 90, dhcp_failover_reconnect,
1050 state); /* XXX this is a reference! */
1051 } else if (!strcmp (name, "message")) {
1052 link = va_arg (ap, dhcp_failover_link_t *);
1053
1054 if (link -> imsg -> type == FTM_CONNECT) {
1055 /* If we already have a link to the peer, it must be
1056 dead, so drop it.
1057 XXX Is this the right thing to do? */
1058 if (state -> link_to_peer) {
1059 omapi_disconnect ((omapi_object_t *)
1060 state -> link_to_peer, 1);
1061 omapi_object_dereference
1062 (&state -> link_to_peer, MDL);
1063 dhcp_failover_state_transition (state,
1064 "disconnect");
1065 }
1066 omapi_object_reference (&state -> link_to_peer,
1067 (omapi_object_t *)link, MDL);
1068 status = (dhcp_failover_send_connectack
1069 ((omapi_object_t *)link, 0));
1070 if (status != ISC_R_SUCCESS) {
1071 omapi_object_dereference
1072 (&state -> link_to_peer, MDL);
1073 omapi_disconnect (link -> outer, 1);
1074 return ISC_R_SUCCESS;
1075 }
1076 dhcp_failover_state_transition (state, "connect");
1077 } else if (link -> imsg -> type == FTM_CONNECTACK) {
1078 if (link -> imsg -> reject_reason) {
1079 log_error ("Failover CONNECTACK from %d.%d.%d.%d%s%s",
1080 ((u_int8_t *)
1081 (&link -> imsg -> server_addr)) [0],
1082 ((u_int8_t *)
1083 (&link -> imsg -> server_addr)) [1],
1084 ((u_int8_t *)
1085 (&link -> imsg -> server_addr)) [2],
1086 ((u_int8_t *)
1087 (&link -> imsg -> server_addr)) [3],
1088 " rejected: ",
1089 (dhcp_failover_reject_reason_print
1090 (link -> imsg -> reject_reason)));
1091 omapi_disconnect (link -> outer, 1);
1092 return ISC_R_SUCCESS;
1093 }
1094
1095 if (!dhcp_failover_state_match
1096 (state,
1097 (u_int8_t *)&link -> imsg -> server_addr,
1098 sizeof link -> imsg -> server_addr)) {
25a69ca1 1099 log_error ("Failover CONNECTACK from %s %d.%d.%d.%d",
9f3da356
TL
1100 "unknown server",
1101 ((u_int8_t *)
1102 (&link -> imsg -> server_addr)) [0],
1103 ((u_int8_t *)
1104 (&link -> imsg -> server_addr)) [1],
1105 ((u_int8_t *)
1106 (&link -> imsg -> server_addr)) [2],
1107 ((u_int8_t *)
1108 (&link -> imsg -> server_addr)) [3]);
1109 dhcp_failover_send_disconnect ((omapi_object_t *)link,
1110 FTR_INVALID_PARTNER, 0);
1111 omapi_disconnect (link -> outer, 0);
1112 }
25a69ca1
TL
1113
1114 if (state -> link_to_peer) {
1115 log_error ("Failover CONNECTACK %s %d.%d.%d.%d",
1116 "while already connected",
1117 ((u_int8_t *)
1118 (&link -> imsg -> server_addr)) [0],
1119 ((u_int8_t *)
1120 (&link -> imsg -> server_addr)) [1],
1121 ((u_int8_t *)
1122 (&link -> imsg -> server_addr)) [2],
1123 ((u_int8_t *)
1124 (&link -> imsg -> server_addr)) [3]);
1125 dhcp_failover_send_disconnect ((omapi_object_t *)link,
1126 FTR_DUP_CONNECTION, 0);
1127 omapi_disconnect (link -> outer, 0);
1128 }
1129 omapi_object_reference (&state -> link_to_peer,
1130 (omapi_object_t *)link, MDL);
9f3da356
TL
1131 dhcp_failover_state_transition (state, "connect");
1132 } else if (link -> imsg -> type == FTM_DISCONNECT) {
1133 if (link -> imsg -> reject_reason) {
1134 log_error ("Failover DISCONNECT from %d.%d.%d.%d%s%s",
1135 ((u_int8_t *)
1136 (&link -> imsg -> server_addr)) [0],
1137 ((u_int8_t *)
1138 (&link -> imsg -> server_addr)) [1],
1139 ((u_int8_t *)
1140 (&link -> imsg -> server_addr)) [2],
1141 ((u_int8_t *)
1142 (&link -> imsg -> server_addr)) [3],
1143 ": ",
1144 (dhcp_failover_reject_reason_print
1145 (link -> imsg -> reject_reason)));
1146 }
1147 omapi_disconnect (link -> outer, 1);
1148 return ISC_R_SUCCESS;
1149 } else if (link -> imsg -> type == FTM_BNDUPD) {
1150 dhcp_failover_process_bindupdate (state, link -> imsg);
1151 } else if (link -> imsg -> type == FTM_BNDACK) {
1152 dhcp_failover_process_bindack (state, link -> imsg);
85a53735 1153 }
85a53735
TL
1154 }
1155
d9eefc5d
TL
1156 /* Handle all the events we care about... */
1157 return ISC_R_SUCCESS;
d50d0b82
TL
1158}
1159
85a53735
TL
1160isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state,
1161 const char *name)
1162{
1163 /* XXX Check these state transitions against the spec! */
1164 if (!strcmp (name, "disconnect")) {
1165 switch (state -> my_state) {
1166 case potential_conflict_nic:
1167 case partner_down:
1168 case communications_interrupted:
1169 /* Already in the right state? */
1170 return ISC_R_SUCCESS;
1171
1172 case potential_conflict:
1173 return dhcp_failover_set_state
1174 (state, potential_conflict_nic);
1175
1176 case recover:
9f3da356
TL
1177 /* XXX I don't think it makes sense to make a
1178 XXX transition from recover to communications-
1179 XXX interrupted, because then when the connect
1180 XXX occurred, we'd make a transition into
1181 XXX normal, not recover. */
85a53735
TL
1182 case normal:
1183 return dhcp_failover_set_state
1184 (state, communications_interrupted);
1185
1186 /* XXX ??? */
9f3da356 1187 case unknown_state:
85a53735
TL
1188 default:
1189 return dhcp_failover_set_state
1190 (state, potential_conflict_nic);
1191
85a53735
TL
1192 }
1193 } else if (!strcmp (name, "connect")) {
1194 switch (state -> my_state) {
1195 case communications_interrupted:
9f3da356
TL
1196 return dhcp_failover_set_state (state, normal);
1197
85a53735
TL
1198 case partner_down:
1199 return dhcp_failover_set_state (state, recover);
1200
9f3da356
TL
1201 case potential_conflict_nic:
1202 return dhcp_failover_set_state (state,
1203 potential_conflict);
1204
1205 /* We should never be in these states when we make
1206 a "connect" transition. */
85a53735
TL
1207 case recover:
1208 case potential_conflict:
9f3da356
TL
1209 case normal:
1210 return ISC_R_INVALIDARG;
85a53735 1211
9f3da356
TL
1212 /* We should never be in the unknown_state or an
1213 unknown state either. */
1214 case unknown_state:
85a53735
TL
1215 default:
1216 return dhcp_failover_set_state (state,
1217 potential_conflict);
9f3da356
TL
1218 }
1219 } else if (!strcmp (name, "startup")) {
1220 switch (state -> my_state) {
1221 case communications_interrupted:
1222 case partner_down:
1223 case potential_conflict_nic:
1224 return ISC_R_SUCCESS;
1225
85a53735 1226 case normal:
9f3da356
TL
1227 case recover:
1228 return dhcp_failover_set_state
1229 (state, communications_interrupted);
1230
1231 case potential_conflict:
1232 return dhcp_failover_set_state
1233 (state, potential_conflict_nic);
1234
1235 case unknown_state:
1236 return dhcp_failover_set_state
1237 (state, communications_interrupted);
1238
1239 default:
1240 return dhcp_failover_set_state
1241 (state, potential_conflict_nic);
85a53735 1242 }
85a53735
TL
1243 }
1244 return ISC_R_INVALIDARG;
1245}
1246
1247isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state,
1248 enum failover_state new_state)
1249{
9f3da356
TL
1250 enum failover_state saved_state;
1251 TIME saved_stos;
1252
85a53735
TL
1253 /* First make the transition out of the current state. */
1254 switch (state -> my_state) {
1255 case normal:
1256 case recover:
9f3da356 1257 case potential_conflict:
85a53735
TL
1258 /* Any updates that haven't been acked yet, we have to
1259 resend, just in case. */
1260 if (state -> ack_queue_tail) {
25a69ca1
TL
1261 struct lease *lp;
1262
1263 /* Zap the flags. */
1264 for (lp = state -> ack_queue_head; lp; lp = lp -> next_pending)
1265 lp -> flags = ((lp -> flags & ~ON_ACK_QUEUE) |
1266 ON_UPDATE_QUEUE);
1267
1268 /* Now hook the ack queue to the beginning of the update
1269 queue. */
85a53735
TL
1270 if (state -> update_queue_head) {
1271 omapi_object_reference
1272 ((omapi_object_t **)
1273 &state -> ack_queue_tail -> next_pending,
1274 (omapi_object_t *)state -> update_queue_head,
1275 MDL);
1276 omapi_object_dereference ((omapi_object_t **)
1277 &state -> update_queue_head,
1278 MDL);
1279 }
1280 omapi_object_reference ((omapi_object_t **)
1281 &state -> update_queue_head,
1282 (omapi_object_t *)state -> ack_queue_head,
1283 MDL);
1284 if (!state -> update_queue_tail)
1285 omapi_object_reference
1286 ((omapi_object_t **)
1287 &state -> update_queue_tail,
1288 (omapi_object_t *)state -> ack_queue_tail, MDL);
1289 omapi_object_dereference ((omapi_object_t **)
1290 &state -> ack_queue_tail, MDL);
1291 omapi_object_dereference ((omapi_object_t **)
1292 &state -> ack_queue_head, MDL);
1293 state -> cur_unacked_updates = 0;
1294 }
1295 cancel_timeout (dhcp_failover_keepalive, state);
1296 break;
1297
1298 case partner_down:
1299 case communications_interrupted:
1300 case potential_conflict_nic:
85a53735
TL
1301 default:
1302 break;
1303 }
1304
9f3da356
TL
1305 /* Tentatively make the transition. */
1306 saved_state = state -> my_state;
1307 saved_stos = state -> my_stos;
1308 state -> my_stos = cur_time;
1309 state -> my_state = new_state;
1310
1311 if (!write_failover_state (state) || !commit_leases ()) {
1312 /* XXX What to do? What to do? */
1313 log_error ("Unable to record current failover state for %s",
1314 state -> name);
1315 /* XXX for now, we don't make the state transition, but this is
1316 XXX kind of a scary choice. */
1317 state -> my_state = saved_state;
1318 state -> my_stos = saved_stos;
1319 return ISC_R_IOERROR;
1320 }
1321
1322 log_info ("failover peer %s moves from %s to %s",
1323 state -> name, dhcp_failover_state_name_print (saved_state),
1324 dhcp_failover_state_name_print (state -> my_state));
1325
85a53735
TL
1326 switch (new_state)
1327 {
9f3da356
TL
1328 case normal:
1329 case recover:
1330 case potential_conflict:
1331 /* If we go into communications-interrupted and then back into
1332 potential-conflict, do we need to start over, or just continue
1333 with the reconciliation process? This came up at one of the
1334 teleconferences, so it's probably answered in the draft. */
1335 if (state -> max_flying_updates > state -> cur_unacked_updates &&
1336 state -> update_queue_head) {
1337 dhcp_failover_send_updates (state);
1338 }
1339 break;
1340 default:
1341 break;
85a53735 1342 }
9f3da356 1343
85a53735
TL
1344 return ISC_R_SUCCESS;
1345}
1346
9f3da356
TL
1347isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *state)
1348{
84fe3bd0 1349 struct lease *lp = (struct lease *)0;
9f3da356
TL
1350 isc_result_t status;
1351
1352 while (state -> max_flying_updates > state -> cur_unacked_updates &&
1353 state -> update_queue_head) {
25a69ca1 1354 /* Grab the head of the update queue. */
9f3da356
TL
1355 omapi_object_reference ((omapi_object_t **)&lp,
1356 (omapi_object_t *)
1357 state -> update_queue_head, MDL);
84fe3bd0
TL
1358
1359 /* Send the update to the peer. */
1360 status = dhcp_failover_send_bind_update (state, lp);
1361 if (status != ISC_R_SUCCESS) {
1362 omapi_object_dereference ((omapi_object_t **)lp, MDL);
1363 return status;
1364 }
25a69ca1 1365 lp -> flags &= ~ON_UPDATE_QUEUE;
84fe3bd0 1366
25a69ca1
TL
1367 /* Take it off the head of the update queue and put the next
1368 item in the update queue at the head. */
9f3da356 1369 omapi_object_dereference ((omapi_object_t **)
25a69ca1 1370 &state -> update_queue_head, MDL);
9f3da356
TL
1371 if (lp -> next_pending) {
1372 omapi_object_reference ((omapi_object_t **)
1373 &state -> update_queue_head,
1374 (omapi_object_t *)
1375 lp -> next_pending, MDL);
1376 omapi_object_dereference ((omapi_object_t **)
1377 lp -> next_pending, MDL);
1378 } else {
1379 omapi_object_dereference ((omapi_object_t **)
1380 &state -> update_queue_tail,
1381 MDL);
1382 }
84fe3bd0 1383
9f3da356
TL
1384 if (state -> ack_queue_head) {
1385 omapi_object_reference
1386 ((omapi_object_t **)
1387 &state -> ack_queue_tail -> next_pending,
1388 (omapi_object_t *)lp, MDL);
1389 omapi_object_dereference ((omapi_object_t **)
1390 &state -> ack_queue_tail,
1391 MDL);
1392 } else {
1393 omapi_object_reference ((omapi_object_t **)
1394 &state -> ack_queue_head,
1395 (omapi_object_t *)lp, MDL);
1396 }
1397 omapi_object_reference ((omapi_object_t **)
1398 &state -> ack_queue_tail,
1399 (omapi_object_t *)lp, MDL);
25a69ca1 1400 lp -> flags |= ON_ACK_QUEUE;
84fe3bd0 1401 omapi_object_dereference ((omapi_object_t **)lp, MDL);
9f3da356
TL
1402
1403 /* Count the object as an unacked update. */
1404 state -> cur_unacked_updates++;
1405 }
1406 return ISC_R_SUCCESS;
1407}
1408
1409/* Queue an update for a lease. Always returns 1 at this point - it's
1410 not an error for this to be called on a lease for which there's no
1411 failover peer. */
1412
1413int dhcp_failover_queue_update (struct lease *lease)
1414{
1415 dhcp_failover_state_t *state;
1416
1417 if (!lease -> pool ||
1418 !lease -> pool -> failover_peer)
1419 return 1;
1420
25a69ca1
TL
1421 /* If it's already on the update queue, leave it there. */
1422 if (lease -> flags & ON_UPDATE_QUEUE)
1423 return 1;
1424
1425 /* Get the failover state structure for this lease. */
9f3da356 1426 state = lease -> pool -> failover_peer;
25a69ca1
TL
1427
1428 /* If it's on the ack queue, take it off. */
1429 if (lease -> flags & ON_ACK_QUEUE)
1430 dhcp_failover_ack_queue_remove (state, lease);
1431
9f3da356
TL
1432 if (state -> update_queue_head) {
1433 omapi_object_reference
1434 ((omapi_object_t **)
1435 &state -> update_queue_tail -> next_pending,
1436 (omapi_object_t *)lease, MDL);
1437 omapi_object_dereference ((omapi_object_t **)
1438 &state -> update_queue_tail, MDL);
1439 } else {
1440 omapi_object_reference ((omapi_object_t **)
1441 &state -> update_queue_head,
1442 (omapi_object_t *)lease, MDL);
1443 }
1444 omapi_object_reference ((omapi_object_t **)
1445 &state -> update_queue_tail,
1446 (omapi_object_t *)lease, MDL);
25a69ca1 1447 lease -> flags |= ON_UPDATE_QUEUE;
9f3da356
TL
1448 dhcp_failover_send_updates (state);
1449 return 1;
1450}
1451
25a69ca1
TL
1452void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *state,
1453 struct lease *lease)
1454{
1455 struct lease *lp;
1456
1457 if (state -> ack_queue_head == lease) {
1458 omapi_object_dereference ((omapi_object_t **)
1459 &state -> ack_queue_head, MDL);
1460 if (lease -> next_pending) {
1461 omapi_object_reference ((omapi_object_t **)
1462 &state -> ack_queue_head,
1463 (omapi_object_t *)
1464 lease -> next_pending, MDL);
1465 } else {
1466 omapi_object_dereference ((omapi_object_t **)
1467 &state -> ack_queue_tail,
1468 MDL);
1469 }
1470 lease -> flags &= ~ON_ACK_QUEUE;
1471 return;
1472 }
1473 for (lp = state -> ack_queue_head;
1474 lp -> next_pending != lease; lp = lp -> next_pending)
1475 ;
1476 if (lp) {
1477 omapi_object_dereference ((omapi_object_t **)
1478 &lp -> next_pending, MDL);
1479 if (lease -> next_pending)
1480 omapi_object_reference ((omapi_object_t **)
1481 lp -> next_pending,
1482 (omapi_object_t *)
1483 lease -> next_pending, MDL);
1484 else {
1485 omapi_object_dereference ((omapi_object_t **)
1486 state -> ack_queue_tail,
1487 MDL);
1488 omapi_object_reference ((omapi_object_t **)
1489 state -> ack_queue_tail,
1490 (omapi_object_t *)lp, MDL);
1491 }
1492 }
1493 lease -> flags &= ~ON_ACK_QUEUE;
1494}
1495
d50d0b82 1496isc_result_t dhcp_failover_state_set_value (omapi_object_t *h,
85a53735
TL
1497 omapi_object_t *id,
1498 omapi_data_string_t *name,
1499 omapi_typed_data_t *value)
d50d0b82
TL
1500{
1501 if (h -> type != dhcp_type_failover_state)
1502 return ISC_R_INVALIDARG;
1503
1504 if (h -> inner && h -> inner -> type -> set_value)
1505 return (*(h -> inner -> type -> set_value))
1506 (h -> inner, id, name, value);
1507 return ISC_R_NOTFOUND;
1508}
1509
85a53735
TL
1510void dhcp_failover_keepalive (void *vs)
1511{
1512 dhcp_failover_state_t *state = vs;
1513}
1514
9f3da356
TL
1515void dhcp_failover_reconnect (void *vs)
1516{
1517 dhcp_failover_state_t *state = vs;
1518 isc_result_t status;
1519
1520 status = dhcp_failover_link_initiate ((omapi_object_t *)state);
1521 if (status != ISC_R_SUCCESS) {
1522 log_info ("failover peer %s: %s", state -> name,
1523 isc_result_totext (status));
1524 add_timeout (cur_time + 90,
1525 dhcp_failover_listener_restart, state);
1526 }
1527}
1528
1529void dhcp_failover_listener_restart (void *vs)
1530{
1531 dhcp_failover_state_t *state = vs;
1532 isc_result_t status;
1533
1534 status = dhcp_failover_listen ((omapi_object_t *)state);
1535 if (status != ISC_R_SUCCESS) {
1536 log_info ("failover peer %s: %s", state -> name,
1537 isc_result_totext (status));
1538 add_timeout (cur_time + 90,
1539 dhcp_failover_listener_restart, state);
1540 }
1541}
1542
d50d0b82 1543isc_result_t dhcp_failover_state_get_value (omapi_object_t *h,
85a53735
TL
1544 omapi_object_t *id,
1545 omapi_data_string_t *name,
1546 omapi_value_t **value)
d50d0b82 1547{
85a53735
TL
1548 dhcp_failover_state_t *s;
1549 struct option_cache *oc;
1550 struct data_string ds;
1551 isc_result_t status;
1552
d50d0b82
TL
1553 if (h -> type != dhcp_type_failover_state)
1554 return ISC_R_INVALIDARG;
85a53735 1555 s = (dhcp_failover_state_t *)h;
d50d0b82 1556
85a53735
TL
1557 if (!omapi_ds_strcmp (name, "name")) {
1558 if (s -> name)
1559 return omapi_make_string_value (value,
1560 name, s -> name, MDL);
1561 return ISC_R_NOTFOUND;
1562 } else if (!omapi_ds_strcmp (name, "partner-address")) {
1563 oc = s -> address;
1564 getaddr:
1565 memset (&ds, 0, sizeof ds);
1566 if (!evaluate_option_cache (&ds, (struct packet *)0,
1567 (struct lease *)0,
1568 (struct option_state *)0,
1569 (struct option_state *)0,
1570 &global_scope, oc, MDL)) {
1571 return ISC_R_NOTFOUND;
1572 }
1573 status = omapi_make_const_value (value,
1574 name, ds.data, ds.len, MDL);
9f3da356
TL
1575 /* Disgusting kludge: */
1576 if (oc == s -> server_addr && !s -> server_identifier.len)
1577 data_string_copy (&s -> server_identifier, &ds, MDL);
85a53735
TL
1578 data_string_forget (&ds, MDL);
1579 return status;
1580 } else if (!omapi_ds_strcmp (name, "local-address")) {
1581 oc = s -> server_addr;
1582 goto getaddr;
1583 } else if (!omapi_ds_strcmp (name, "partner-port")) {
1584 return omapi_make_int_value (value, name, s -> port, MDL);
1585 } else if (!omapi_ds_strcmp (name, "local-port")) {
1586 return omapi_make_int_value (value,
1587 name, s -> listen_port, MDL);
1588 } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
1589 return omapi_make_uint_value (value, name,
1590 s -> max_flying_updates, MDL);
1591 } else if (!omapi_ds_strcmp (name, "mclt")) {
1592 return omapi_make_uint_value (value, name, s -> mclt, MDL);
1593 } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
1594 return omapi_make_int_value (value, name,
1595 s -> load_balance_max_secs, MDL);
1596 } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
1597 if (s -> hba)
1598 return omapi_make_const_value (value, name,
1599 s -> hba, 32, MDL);
1600 return ISC_R_NOTFOUND;
1601 } else if (!omapi_ds_strcmp (name, "partner-state")) {
1602 return omapi_make_uint_value (value, name,
1603 s -> partner_state, MDL);
1604 } else if (!omapi_ds_strcmp (name, "local-state")) {
1605 return omapi_make_uint_value (value, name,
1606 s -> my_state, MDL);
1607 } else if (!omapi_ds_strcmp (name, "partner-stos")) {
1608 return omapi_make_int_value (value, name,
1609 s -> partner_stos, MDL);
1610 } else if (!omapi_ds_strcmp (name, "local-stos")) {
1611 return omapi_make_int_value (value, name,
1612 s -> my_stos, MDL);
1613 } else if (!omapi_ds_strcmp (name, "hierarchy")) {
1614 return omapi_make_uint_value (value, name, s -> i_am, MDL);
1615 } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
1616 return omapi_make_int_value (value, name,
1617 s -> last_packet_sent, MDL);
1618 } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
1619 return omapi_make_int_value (value, name,
1620 s -> last_timestamp_received,
1621 MDL);
1622 } else if (!omapi_ds_strcmp (name, "skew")) {
1623 return omapi_make_int_value (value, name, s -> skew, MDL);
1624 } else if (!omapi_ds_strcmp (name, "max-transmit-idle")) {
1625 return omapi_make_uint_value (value, name,
1626 s -> max_transmit_idle, MDL);
1627 } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
1628 return omapi_make_uint_value (value, name,
1629 s -> max_response_delay, MDL);
1630 } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
1631 return omapi_make_int_value (value, name,
1632 s -> cur_unacked_updates, MDL);
1633 }
1634
d50d0b82
TL
1635 if (h -> inner && h -> inner -> type -> get_value)
1636 return (*(h -> inner -> type -> get_value))
1637 (h -> inner, id, name, value);
1638 return ISC_R_NOTFOUND;
1639}
1640
1641isc_result_t dhcp_failover_state_destroy (omapi_object_t *h,
4bd8800e 1642 const char *file, int line)
d50d0b82 1643{
85a53735
TL
1644 dhcp_failover_state_t *s;
1645
d50d0b82
TL
1646 if (h -> type != dhcp_type_failover_state)
1647 return ISC_R_INVALIDARG;
85a53735
TL
1648 s = (dhcp_failover_state_t *)h;
1649 if (s -> link_to_peer)
1650 omapi_object_dereference (&s -> link_to_peer, MDL);
1651 if (s -> name)
1652 dfree (s -> name, MDL);
1653 if (s -> address)
1654 option_cache_dereference (&s -> address, MDL);
1655 if (s -> server_addr)
1656 option_cache_dereference (&s -> server_addr, MDL);
1657
d50d0b82
TL
1658 return ISC_R_SUCCESS;
1659}
1660
1661/* Write all the published values associated with the object through the
1662 specified connection. */
1663
1664isc_result_t dhcp_failover_state_stuff (omapi_object_t *c,
d8a417b2 1665 omapi_object_t *id,
85a53735 1666 omapi_object_t *h)
d50d0b82 1667{
85a53735
TL
1668 dhcp_failover_state_t *s;
1669 omapi_connection_object_t *conn;
1670 isc_result_t status;
d50d0b82 1671
85a53735 1672 if (c -> type != omapi_type_connection)
d50d0b82 1673 return ISC_R_INVALIDARG;
85a53735 1674 conn = (omapi_connection_object_t *)c;
d50d0b82 1675
85a53735
TL
1676 if (h -> type != dhcp_type_failover_state)
1677 return ISC_R_INVALIDARG;
1678 s = (dhcp_failover_state_t *)h;
1679
1680 status = omapi_connection_put_name (c, "name");
1681 if (status != ISC_R_SUCCESS)
1682 return status;
1683 status = omapi_connection_put_string (c, s -> name);
1684 if (status != ISC_R_SUCCESS)
1685 return status;
1686
1687 status = omapi_connection_put_name (c, "partner-address");
1688 if (status != ISC_R_SUCCESS)
1689 return status;
1690 status = omapi_connection_put_uint32 (c, sizeof s -> address);
1691 if (status != ISC_R_SUCCESS)
1692 return status;
1693 status = omapi_connection_copyin (c, (u_int8_t *)&s -> address,
1694 sizeof s -> address);
1695 if (status != ISC_R_SUCCESS)
1696 return status;
1697
1698 status = omapi_connection_put_name (c, "partner-port");
1699 if (status != ISC_R_SUCCESS)
1700 return status;
1701 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1702 if (status != ISC_R_SUCCESS)
1703 return status;
1704 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> port);
1705 if (status != ISC_R_SUCCESS)
1706 return status;
1707
1708 status = omapi_connection_put_name (c, "local-address");
1709 if (status != ISC_R_SUCCESS)
1710 return status;
1711 status = omapi_connection_put_uint32 (c, sizeof s -> server_addr);
1712 if (status != ISC_R_SUCCESS)
1713 return status;
1714 status = omapi_connection_copyin (c, (u_int8_t *)&s -> server_addr,
1715 sizeof s -> server_addr);
1716 if (status != ISC_R_SUCCESS)
1717 return status;
1718
1719 status = omapi_connection_put_name (c, "local-port");
1720 if (status != ISC_R_SUCCESS)
1721 return status;
1722 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1723 if (status != ISC_R_SUCCESS)
1724 return status;
1725 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> listen_port);
1726 if (status != ISC_R_SUCCESS)
1727 return status;
1728
1729 status = omapi_connection_put_name (c, "max-outstanding-updates");
1730 if (status != ISC_R_SUCCESS)
1731 return status;
1732 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1733 if (status != ISC_R_SUCCESS)
1734 return status;
1735 status = omapi_connection_put_uint32 (c, s -> max_flying_updates);
1736 if (status != ISC_R_SUCCESS)
1737 return status;
1738
1739 status = omapi_connection_put_name (c, "mclt");
1740 if (status != ISC_R_SUCCESS)
1741 return status;
1742 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1743 if (status != ISC_R_SUCCESS)
1744 return status;
1745 status = omapi_connection_put_uint32 (c, s -> mclt);
1746 if (status != ISC_R_SUCCESS)
1747 return status;
1748
1749 status = omapi_connection_put_name (c, "load-balance-max-secs");
1750 if (status != ISC_R_SUCCESS)
1751 return status;
1752 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1753 if (status != ISC_R_SUCCESS)
1754 return status;
1755 status = (omapi_connection_put_uint32
1756 (c, (u_int32_t)s -> load_balance_max_secs));
1757 if (status != ISC_R_SUCCESS)
1758 return status;
1759
1760
1761 if (s -> hba) {
1762 status = omapi_connection_put_name (c, "load-balance-hba");
1763 if (status != ISC_R_SUCCESS)
1764 return status;
1765 status = omapi_connection_put_uint32 (c, 32);
1766 if (status != ISC_R_SUCCESS)
1767 return status;
1768 status = omapi_connection_copyin (c, s -> hba, 32);
1769 if (status != ISC_R_SUCCESS)
1770 return status;
1771 }
1772
1773 status = omapi_connection_put_name (c, "partner-state");
1774 if (status != ISC_R_SUCCESS)
1775 return status;
1776 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1777 if (status != ISC_R_SUCCESS)
1778 return status;
1779 status = omapi_connection_put_uint32 (c, s -> partner_state);
1780 if (status != ISC_R_SUCCESS)
1781 return status;
1782
1783 status = omapi_connection_put_name (c, "local-state");
1784 if (status != ISC_R_SUCCESS)
1785 return status;
1786 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1787 if (status != ISC_R_SUCCESS)
1788 return status;
1789 status = omapi_connection_put_uint32 (c, s -> my_state);
1790 if (status != ISC_R_SUCCESS)
1791 return status;
1792
1793 status = omapi_connection_put_name (c, "partner-stos");
1794 if (status != ISC_R_SUCCESS)
1795 return status;
1796 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1797 if (status != ISC_R_SUCCESS)
1798 return status;
1799 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> partner_stos);
1800 if (status != ISC_R_SUCCESS)
1801 return status;
1802
1803 status = omapi_connection_put_name (c, "local-stos");
1804 if (status != ISC_R_SUCCESS)
1805 return status;
1806 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1807 if (status != ISC_R_SUCCESS)
1808 return status;
1809 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> my_stos);
1810 if (status != ISC_R_SUCCESS)
1811 return status;
1812
1813 status = omapi_connection_put_name (c, "hierarchy");
1814 if (status != ISC_R_SUCCESS)
1815 return status;
1816 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1817 if (status != ISC_R_SUCCESS)
1818 return status;
1819 status = omapi_connection_put_uint32 (c, s -> i_am);
1820 if (status != ISC_R_SUCCESS)
1821 return status;
1822
1823 status = omapi_connection_put_name (c, "last-packet-sent");
1824 if (status != ISC_R_SUCCESS)
1825 return status;
1826 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1827 if (status != ISC_R_SUCCESS)
1828 return status;
1829 status = (omapi_connection_put_uint32
1830 (c, (u_int32_t)s -> last_packet_sent));
1831 if (status != ISC_R_SUCCESS)
1832 return status;
1833
1834 status = omapi_connection_put_name (c, "last-timestamp-received");
1835 if (status != ISC_R_SUCCESS)
1836 return status;
1837 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1838 if (status != ISC_R_SUCCESS)
1839 return status;
1840 status = (omapi_connection_put_uint32
1841 (c, (u_int32_t)s -> last_timestamp_received));
1842 if (status != ISC_R_SUCCESS)
1843 return status;
1844
1845 status = omapi_connection_put_name (c, "skew");
1846 if (status != ISC_R_SUCCESS)
1847 return status;
1848 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1849 if (status != ISC_R_SUCCESS)
1850 return status;
1851 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> skew);
1852 if (status != ISC_R_SUCCESS)
1853 return status;
1854
1855 status = omapi_connection_put_name (c, "max-transmit-idle");
1856 if (status != ISC_R_SUCCESS)
1857 return status;
1858 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1859 if (status != ISC_R_SUCCESS)
1860 return status;
1861 status = (omapi_connection_put_uint32
1862 (c, (u_int32_t)s -> max_transmit_idle));
1863 if (status != ISC_R_SUCCESS)
1864 return status;
1865
1866 status = omapi_connection_put_name (c, "max-response-delay");
1867 if (status != ISC_R_SUCCESS)
1868 return status;
1869 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1870 if (status != ISC_R_SUCCESS)
1871 return status;
1872 status = (omapi_connection_put_uint32
1873 (c, (u_int32_t)s -> max_response_delay));
1874 if (status != ISC_R_SUCCESS)
1875 return status;
1876
1877 status = omapi_connection_put_name (c, "cur-unacked-updates");
1878 if (status != ISC_R_SUCCESS)
1879 return status;
1880 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1881 if (status != ISC_R_SUCCESS)
1882 return status;
1883 status = (omapi_connection_put_uint32
1884 (c, (u_int32_t)s -> cur_unacked_updates));
1885 if (status != ISC_R_SUCCESS)
1886 return status;
1887
1888 if (h -> inner && h -> inner -> type -> stuff_values)
1889 return (*(h -> inner -> type -> stuff_values)) (c, id,
1890 h -> inner);
d50d0b82
TL
1891 return ISC_R_SUCCESS;
1892}
1893
d8a417b2
TL
1894isc_result_t dhcp_failover_state_lookup (omapi_object_t **sp,
1895 omapi_object_t *id,
1896 omapi_object_t *ref)
1897{
1898 omapi_value_t *tv = (omapi_value_t *)0;
1899 isc_result_t status;
1900 dhcp_failover_state_t *s;
1901
1902 /* First see if we were sent a handle. */
1903 status = omapi_get_value_str (ref, id, "handle", &tv);
1904 if (status == ISC_R_SUCCESS) {
1905 status = omapi_handle_td_lookup (sp, tv -> value);
1906
4bd8800e 1907 omapi_value_dereference (&tv, MDL);
d8a417b2
TL
1908 if (status != ISC_R_SUCCESS)
1909 return status;
1910
1911 /* Don't return the object if the type is wrong. */
1912 if ((*sp) -> type != dhcp_type_failover_state) {
4bd8800e 1913 omapi_object_dereference (sp, MDL);
d8a417b2
TL
1914 return ISC_R_INVALIDARG;
1915 }
1916 }
1917
dd53dc5a 1918 /* Look the failover state up by peer name. */
d8a417b2
TL
1919 status = omapi_get_value_str (ref, id, "peer_name", &tv);
1920 if (status == ISC_R_SUCCESS) {
1921 for (s = failover_states; s; s = s -> next) {
dd53dc5a 1922 unsigned l = strlen (s -> name);
d8a417b2 1923 if (l == tv -> value -> u.buffer.len ||
dd53dc5a 1924 !memcmp (s -> name,
d8a417b2
TL
1925 tv -> value -> u.buffer.value, l))
1926 break;
1927 }
4bd8800e 1928 omapi_value_dereference (&tv, MDL);
d8a417b2
TL
1929
1930 /* If we already have a lease, and it's not the same one,
1931 then the query was invalid. */
1932 if (*sp && *sp != (omapi_object_t *)s) {
4bd8800e 1933 omapi_object_dereference (sp, MDL);
d8a417b2
TL
1934 return ISC_R_KEYCONFLICT;
1935 } else if (!s) {
1936 if (*sp)
4bd8800e 1937 omapi_object_dereference (sp, MDL);
d8a417b2
TL
1938 return ISC_R_NOTFOUND;
1939 } else if (!*sp)
1940 /* XXX fix so that hash lookup itself creates
1941 XXX the reference. */
4bd8800e 1942 omapi_object_reference (sp, (omapi_object_t *)s, MDL);
d8a417b2
TL
1943 }
1944
1945 /* If we get to here without finding a lease, no valid key was
1946 specified. */
1947 if (!*sp)
1948 return ISC_R_NOKEYS;
1949 return ISC_R_SUCCESS;
1950}
1951
1952isc_result_t dhcp_failover_state_create (omapi_object_t **sp,
1953 omapi_object_t *id)
1954{
1955 return ISC_R_NOTIMPLEMENTED;
1956}
1957
1958isc_result_t dhcp_failover_state_remove (omapi_object_t *sp,
1959 omapi_object_t *id)
1960{
1961 return ISC_R_NOTIMPLEMENTED;
1962}
1963
9f3da356
TL
1964int dhcp_failover_state_match (dhcp_failover_state_t *state,
1965 u_int8_t *addr, unsigned addrlen)
1966{
1967 struct option_cache *oc;
1968 struct data_string ds;
1969 int i;
1970
1971 memset (&ds, 0, sizeof ds);
1972 if (evaluate_option_cache (&ds, (struct packet *)0,
1973 (struct lease *)0,
1974 (struct option_state *)0,
1975 (struct option_state *)0,
1976 &global_scope,
1977 state -> address, MDL)) {
1978 for (i = 0; i + addrlen - 1 < ds.len; i += addrlen) {
1979 if (!memcmp (&ds.data [i],
1980 addr, addrlen)) {
1981 data_string_forget (&ds, MDL);
1982 return 1;
1983 }
1984 }
1985 data_string_forget (&ds, MDL);
1986 }
1987 return 0;
1988}
1989
1990const char *dhcp_failover_reject_reason_print (int reason)
1991{
1992 switch (reason) {
1993 case FTR_ILLEGAL_IP_ADDR:
1994 return "Illegal IP address (not part of any address pool).";
1995
1996 case FTR_FATAL_CONFLICT:
1997 return "Fatal conflict exists: address in use by other client.";
1998
1999 case FTR_MISSING_BINDINFO:
2000 return "Missing binding information.";
2001
2002 case FTR_TIMEMISMATCH:
2003 return "Connection rejected, time mismatch too great.";
2004
2005 case FTR_INVALID_MCLT:
2006 return "Connection rejected, invalid MCLT.";
2007
2008 case FTR_MISC_REJECT:
2009 return "Connection rejected, unknown reason.";
2010
2011 case FTR_DUP_CONNECTION:
2012 return "Connection rejected, duplicate connection.";
2013
2014 case FTR_INVALID_PARTNER:
2015 return "Connection rejected, invalid failover partner.";
2016
2017 case FTR_TLS_UNSUPPORTED:
2018 return "TLS not supported.";
2019
2020 case FTR_TLS_UNCONFIGURED:
2021 return "TLS supported but not configured.";
2022
2023 case FTR_TLS_REQUIRED:
2024 return "TLS required but not supported by partner.";
2025
2026 case FTR_DIGEST_UNSUPPORTED:
2027 return "Message digest not supported.";
2028
2029 case FTR_DIGEST_UNCONFIGURED:
2030 return "Message digest not configured.";
2031
2032 case FTR_VERSION_MISMATCH:
2033 return "Protocol version mismatch.";
2034
2035 case FTR_MISSING_BIND_INFO:
2036 return "Missing binding information.";
2037
2038 case FTR_OUTDATED_BIND_INFO:
2039 return "Outdated binding information.";
2040
2041 case FTR_LESS_CRIT_BIND_INFO:
2042 return "Less critical binding information.";
2043
2044 case FTR_NO_TRAFFIC:
2045 return "No traffic within sufficient time.";
2046
2047 case FTR_HBA_CONFLICT:
2048 return "Hash bucket assignment conflict.";
2049
2050 default:
2051 case FTR_UNKNOWN:
2052 return "Unknown: Error occurred but does not match any reason code.";
2053 }
2054}
2055
2056const char *dhcp_failover_state_name_print (enum failover_state state)
2057{
2058 switch (state) {
2059 default:
2060 case unknown_state:
2061 return "unknown-state";
2062
2063 case partner_down:
2064 return "partner-down";
2065
2066 case normal:
2067 return "normal";
2068
2069 case communications_interrupted:
2070 return "communications-interrupted";
2071
2072 case potential_conflict_nic:
2073 return "potential-conflict-nic";
2074
2075 case potential_conflict:
2076 return "potential-conflict";
2077
2078 case recover:
2079 return "recover";
2080 }
2081}
2082
d8a417b2
TL
2083failover_option_t *dhcp_failover_make_option (unsigned code,
2084 char *obuf, unsigned *obufix,
2085 unsigned obufmax, ...)
2086{
2087 va_list va;
2088 struct failover_option_info *info;
2089 int i;
2090 unsigned size, count;
2091 unsigned val;
85a53735
TL
2092 u_int8_t *iaddr;
2093 unsigned ilen;
d8a417b2
TL
2094 u_int8_t *bval;
2095 char *fmt;
2096#if defined (DEBUG_FAILOVER_MESSAGES)
2097 char tbuf [256];
2098#endif
2099 /* Note that the failover_option structure is used differently on
2100 input than on output - on input, count is an element count, and
2101 on output it's the number of bytes total in the option, including
2102 the option code and option length. */
2103 failover_option_t option, *op;
2104
2105
2106 /* Bogus option code? */
2107 if (code < 1 || code > FTO_MAX || ft_options [code].type == FT_UNDEF) {
2108 return &null_failover_option;
2109 }
2110 info = &ft_options [code];
2111
2112 va_start (va, obufmax);
2113
2114 /* Get the number of elements and the size of the buffer we need
2115 to allocate. */
2116 if (info -> type == FT_DDNS || info -> type == FT_DDNS1) {
2117 count = info -> type == FT_DDNS ? 1 : 2;
2118 size = va_arg (va, int) + count;
2119 } else {
2120 /* Find out how many items in this list. */
2121 if (info -> num_present)
2122 count = info -> num_present;
2123 else
2124 count = va_arg (va, int);
2125
2126 /* Figure out size. */
2127 switch (info -> type) {
2128 case FT_UINT8:
2129 case FT_BYTES:
2130 case FT_DIGEST:
2131 size = count;
2132 break;
2133
2134 case FT_TEXT_OR_BYTES:
2135 case FT_TEXT:
2136 fmt = va_arg (va, char *);
2137#if defined (HAVE_SNPRINTF)
2138 /* Presumably if we have snprintf, we also have
2139 vsnprintf. */
2140 vsnprintf (tbuf, sizeof tbuf, fmt, va);
2141#else
2142 vsprintf (tbuf, fmt, va);
2143#endif
2144 size = strlen (tbuf);
2145 count = size;
2146 break;
2147
2148 case FT_IPADDR:
9f3da356
TL
2149 ilen = va_arg (va, unsigned);
2150 size = count * ilen;
2151
d8a417b2
TL
2152 case FT_UINT32:
2153 size = count * 4;
2154 break;
2155
2156 case FT_UINT16:
2157 size = count * 2;
2158 break;
2159
2160 default:
2161 /* shouldn't get here. */
2162 log_fatal ("bogus type in failover_make_option: %d",
2163 info -> type);
2164 break;
2165 }
2166 }
2167
2168 size += 4;
2169
2170 /* Allocate a buffer for the option. */
2171 option.count = size;
4bd8800e 2172 option.data = dmalloc (option.count, MDL);
d8a417b2
TL
2173 if (!option.data)
2174 return &null_failover_option;
2175
2176 /* Put in the option code and option length. */
2177 putUShort (option.data, code);
9f3da356 2178 putUShort (&option.data [2], size - 4);
d8a417b2
TL
2179
2180#if defined (DEBUG_FAILOVER_MESSAGES)
2181 sprintf (tbuf, " (%s<%d>", info -> name, option.count);
2182 failover_print (obuf, obufix, obufmax, tbuf);
2183#endif
2184
2185 /* Now put in the data. */
2186 switch (info -> type) {
2187 case FT_UINT8:
2188 for (i = 0; i < count; i++) {
2189 val = va_arg (va, unsigned);
2190#if defined (DEBUG_FAILOVER_MESSAGES)
2191 sprintf (tbuf, " %d", val);
2192 failover_print (obuf, obufix, obufmax, tbuf);
2193#endif
2194 option.data [i + 4] = val;
2195 }
2196 break;
2197
2198 case FT_IPADDR:
2199 for (i = 0; i < count; i++) {
85a53735
TL
2200 iaddr = va_arg (va, u_int8_t *);
2201 if (ilen != 4) {
4bd8800e 2202 dfree (option.data, MDL);
d8a417b2 2203 log_error ("IP addrlen=%d, should be 4.",
85a53735 2204 ilen);
d8a417b2
TL
2205 return &null_failover_option;
2206 }
2207
2208#if defined (DEBUG_FAILOVER_MESSAGES)
85a53735
TL
2209 sprintf (tbuf, " %u.%u.%u.%u", iaddr [0], iaddr [1],
2210 iaddr [2], iaddr [3]);
d8a417b2
TL
2211 failover_print (obuf, obufix, obufmax, tbuf);
2212#endif
9f3da356 2213 memcpy (&option.data [4 + i * ilen], iaddr, ilen);
d8a417b2
TL
2214 }
2215 break;
2216
2217 case FT_UINT32:
2218 for (i = 0; i < count; i++) {
2219 val = va_arg (va, unsigned);
2220#if defined (DEBUG_FAILOVER_MESSAGES)
2221 sprintf (tbuf, " %d", val);
2222 failover_print (obuf, obufix, obufmax, tbuf);
2223#endif
2224 putULong (&option.data [4 + i * 4], val);
2225 }
2226 break;
2227
2228 case FT_BYTES:
2229 case FT_DIGEST:
2230 bval = va_arg (va, u_int8_t *);
2231#if defined (DEBUG_FAILOVER_MESSAGES)
2232 for (i = 0; i < count; i++) {
2233 sprintf (tbuf, " %d", bval [i]);
2234 failover_print (obuf, obufix, obufmax, tbuf);
2235 }
2236#endif
2237 memcpy (&option.data [4], bval, count);
2238 break;
2239
2240 /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
2241 terminated. Note that the caller should be careful not to
2242 provide a format and data that amount to more than 256 bytes
2243 of data, since it will be truncated on platforms that
2244 support snprintf, and will mung the stack on those platforms
2245 that do not support snprintf. Also, callers should not pass
2246 data acquired from the network without specifically checking
2247 it to make sure it won't bash the stack. */
2248 case FT_TEXT_OR_BYTES:
2249 case FT_TEXT:
2250#if defined (DEBUG_FAILOVER_MESSAGES)
2251 failover_print (obuf, obufix, obufmax, " \"");
2252 failover_print (obuf, obufix, obufmax, tbuf);
2253 failover_print (obuf, obufix, obufmax, "\"");
2254#endif
2255 memcpy (&option.data [4], tbuf, count);
2256 break;
2257
2258 case FT_DDNS:
2259 case FT_DDNS1:
2260 option.data [4] = va_arg (va, unsigned);
2261 if (count == 2)
2262 option.data [5] = va_arg (va, unsigned);
2263 bval = va_arg (va, u_int8_t *);
2264 memcpy (&option.data [4 + count], bval, size - count - 4);
2265#if defined (DEBUG_FAILOVER_MESSAGES)
2266 for (i = 4; i < size; i++) {
2267 sprintf (tbuf, " %d", option.data [i]);
2268 failover_print (obuf, obufix, obufmax, tbuf);
2269 }
2270#endif
2271 break;
2272
2273 case FT_UINT16:
2274 for (i = 0; i < count; i++) {
2275 val = va_arg (va, u_int32_t);
2276#if defined (DEBUG_FAILOVER_MESSAGES)
2277 sprintf (tbuf, " %d", val);
2278 failover_print (obuf, obufix, obufmax, tbuf);
2279#endif
2280 putUShort (&option.data [4 + i * 2], val);
2281 }
2282 break;
2283
2284 case FT_UNDEF:
2285 default:
2286 }
2287
2288 failover_print (obuf, obufix, obufmax, ")");
2289
2290 /* Now allocate a place to store what we just set up. */
4bd8800e 2291 op = dmalloc (sizeof (failover_option_t), MDL);
d8a417b2 2292 if (!op) {
4bd8800e 2293 dfree (option.data, MDL);
d8a417b2
TL
2294 return &null_failover_option;
2295 }
2296
2297 *op = option;
2298 return op;
2299}
2300
2301/* Send a failover message header. */
2302
2303isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link,
2304 omapi_object_t *connection,
2305 int msg_type, ...)
2306{
2307 unsigned count = 0;
2308 unsigned size = 0;
2309 int bad_option = 0;
2310 int opix = 0;
2311 va_list list;
2312 failover_option_t *option;
2313 unsigned char *opbuf;
2314 isc_result_t status = ISC_R_SUCCESS;
2315 unsigned char cbuf;
2316
2317 /* Run through the argument list once to compute the length of
2318 the option portion of the message. */
2319 va_start (list, msg_type);
2320 while ((option = va_arg (list, failover_option_t *))) {
dd53dc5a
TL
2321 if (option != &skip_failover_option)
2322 size += option -> count;
d8a417b2
TL
2323 if (option == &null_failover_option)
2324 bad_option = 1;
2325 }
2326 va_end (list);
2327
2328 /* Allocate an option buffer, unless we got an error. */
2329 if (!bad_option) {
4bd8800e 2330 opbuf = dmalloc (size, MDL);
d8a417b2
TL
2331 if (!opbuf)
2332 status = ISC_R_NOMEMORY;
2333 }
2334
2335 va_start (list, msg_type);
2336 while ((option = va_arg (list, failover_option_t *))) {
dd53dc5a
TL
2337 if (option == &skip_failover_option)
2338 continue;
d8a417b2
TL
2339 if (!bad_option && opbuf)
2340 memcpy (&opbuf [opix],
2341 option -> data, option -> count);
85a53735
TL
2342 if (option != &null_failover_option &&
2343 option != &skip_failover_option) {
2344 opix += option -> count;
2345 dfree (option -> data, MDL);
2346 dfree (option, MDL);
2347 }
d8a417b2
TL
2348 }
2349
9f3da356
TL
2350 if (bad_option)
2351 return ISC_R_INVALIDARG;
d8a417b2
TL
2352
2353 /* Now send the message header. */
2354
2355 /* Message length. */
2356 status = omapi_connection_put_uint16 (connection, size + 12);
2357 if (status != ISC_R_SUCCESS)
2358 goto err;
2359
2360 /* Message type. */
2361 cbuf = msg_type;
2362 status = omapi_connection_copyin (connection, &cbuf, 1);
2363 if (status != ISC_R_SUCCESS)
2364 goto err;
2365
2366 /* Payload offset. */
2367 cbuf = 12;
2368 status = omapi_connection_copyin (connection, &cbuf, 1);
2369 if (status != ISC_R_SUCCESS)
2370 goto err;
2371
2372 /* Current time. */
2373 status = omapi_connection_put_uint32 (connection, (u_int32_t)cur_time);
2374 if (status != ISC_R_SUCCESS)
2375 goto err;
2376
2377 /* Transaction ID. */
2378 status = omapi_connection_put_uint32 (connection, link -> xid++);
2379 if (status != ISC_R_SUCCESS)
2380 goto err;
2381
2382
2383 /* Payload. */
2384 status = omapi_connection_copyin (connection, opbuf, size);
2385 if (status != ISC_R_SUCCESS)
2386 goto err;
4bd8800e 2387 dfree (opbuf, MDL);
d8a417b2
TL
2388 return status;
2389
2390 err:
4bd8800e 2391 dfree (opbuf, MDL);
d8a417b2
TL
2392 omapi_disconnect (connection, 1);
2393 return status;
9f3da356 2394}
d8a417b2
TL
2395
2396/* Send a connect message. */
2397
2398isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
2399{
2400 dhcp_failover_link_t *link;
2401 dhcp_failover_state_t *state;
2402 isc_result_t status;
2403#if defined (DEBUG_FAILOVER_MESSAGES)
2404 char obuf [64];
2405 unsigned obufix = 0;
2406
2407# define FMA obuf, &obufix, sizeof obuf
2408 failover_print (FMA, "(connect");
2409#else
2410# define FMA (unsigned char *)0, (unsigned *)0, 0
2411#endif
2412
d8a417b2
TL
2413 if (!l || l -> type != dhcp_type_failover_link)
2414 return ISC_R_INVALIDARG;
2415 link = (dhcp_failover_link_t *)l;
2416 state = link -> state_object;
2417 if (!l -> outer || l -> outer -> type != omapi_type_connection)
2418 return ISC_R_INVALIDARG;
2419
2420 status = (dhcp_failover_put_message
2421 (link, l -> outer,
2422 FTM_CONNECT,
2423 dhcp_failover_make_option (FTO_SERVER_ADDR, FMA,
9f3da356
TL
2424 state -> server_identifier.len,
2425 state -> server_identifier.data),
d8a417b2
TL
2426 dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
2427 state -> max_flying_updates),
2428 dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
dd53dc5a 2429 state -> max_response_delay),
d8a417b2
TL
2430 dhcp_failover_make_option (FTO_VENDOR_CLASS, FMA,
2431 "isc-%s", DHCP_VERSION),
2432 dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
2433 DHCP_FAILOVER_VERSION),
2434 dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
2435 0, 0),
2436 dhcp_failover_make_option (FTO_MCLT, FMA,
2437 state -> mclt),
9f3da356 2438 dhcp_failover_make_option (FTO_HBA, FMA, 32, state -> hba),
d8a417b2
TL
2439 (failover_option_t *)0));
2440
2441#if defined (DEBUG_FAILOVER_MESSAGES)
2442 if (status != ISC_R_SUCCESS)
2443 failover_print (FMA, " (failed)");
2444 failover_print (FMA, ")");
2445 if (obufix) {
2446 log_debug ("%s", obuf);
2447 }
2448#endif
2449 return status;
2450}
2451
9f3da356
TL
2452isc_result_t dhcp_failover_send_connectack (omapi_object_t *l, int reason)
2453{
2454 dhcp_failover_link_t *link;
2455 dhcp_failover_state_t *state;
2456 isc_result_t status;
2457#if defined (DEBUG_FAILOVER_MESSAGES)
2458 char obuf [64];
2459 unsigned obufix = 0;
2460
2461# define FMA obuf, &obufix, sizeof obuf
2462 failover_print (FMA, "(connectack");
2463#else
2464# define FMA (unsigned char *)0, (unsigned *)0, 0
2465#endif
2466
2467 if (!l || l -> type != dhcp_type_failover_link)
2468 return ISC_R_INVALIDARG;
2469 link = (dhcp_failover_link_t *)l;
2470 state = link -> state_object;
2471 if (!l -> outer || l -> outer -> type != omapi_type_connection)
2472 return ISC_R_INVALIDARG;
dd53dc5a 2473
9f3da356
TL
2474 status = (dhcp_failover_put_message
2475 (link, l -> outer,
2476 FTM_CONNECTACK,
2477 dhcp_failover_make_option (FTO_SERVER_ADDR, FMA,
2478 state -> server_identifier.len,
2479 state -> server_identifier.data),
2480 dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
2481 state -> max_flying_updates),
2482 dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
2483 state -> max_response_delay),
2484 dhcp_failover_make_option (FTO_VENDOR_CLASS, FMA,
2485 "isc-%s", DHCP_VERSION),
2486 dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
2487 DHCP_FAILOVER_VERSION),
2488 dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
2489 0, 0),
2490 (reason
2491 ? dhcp_failover_make_option (FTO_REJECT_REASON,
2492 FMA, reason)
2493 : &skip_failover_option),
2494 (failover_option_t *)0));
2495
2496#if defined (DEBUG_FAILOVER_MESSAGES)
2497 if (status != ISC_R_SUCCESS)
2498 failover_print (FMA, " (failed)");
2499 failover_print (FMA, ")");
2500 if (obufix) {
2501 log_debug ("%s", obuf);
2502 }
2503#endif
2504 return status;
2505}
2506
2507isc_result_t dhcp_failover_send_disconnect (omapi_object_t *l,
2508 int reason,
2509 const char *message)
dd53dc5a
TL
2510{
2511 dhcp_failover_link_t *link;
2512 dhcp_failover_state_t *state;
2513 isc_result_t status;
2514#if defined (DEBUG_FAILOVER_MESSAGES)
2515 char obuf [64];
2516 unsigned obufix = 0;
2517
2518# define FMA obuf, &obufix, sizeof obuf
9f3da356 2519 failover_print (FMA, "(disconnect");
dd53dc5a
TL
2520#else
2521# define FMA (unsigned char *)0, (unsigned *)0, 0
2522#endif
2523
9f3da356
TL
2524 if (!l || l -> type != dhcp_type_failover_link)
2525 return ISC_R_INVALIDARG;
2526 link = (dhcp_failover_link_t *)l;
2527 state = link -> state_object;
2528 if (!l -> outer || l -> outer -> type != omapi_type_connection)
dd53dc5a
TL
2529 return ISC_R_INVALIDARG;
2530
9f3da356
TL
2531 status = (dhcp_failover_put_message
2532 (link, l -> outer,
2533 FTM_DISCONNECT,
2534 dhcp_failover_make_option (FTO_REJECT_REASON,
2535 FMA, reason),
2536 (message
2537 ? dhcp_failover_make_option (FTO_MESSAGE, FMA, message)
2538 : (reason
2539 ? (dhcp_failover_make_option
2540 (FTO_MESSAGE,
2541 FMA, dhcp_failover_reject_reason_print (reason)))
2542 : &skip_failover_option)),
2543 (failover_option_t *)0));
2544
2545#if defined (DEBUG_FAILOVER_MESSAGES)
2546 if (status != ISC_R_SUCCESS)
2547 failover_print (FMA, " (failed)");
2548 failover_print (FMA, ")");
2549 if (obufix) {
2550 log_debug ("%s", obuf);
2551 }
2552#endif
2553 return status;
2554}
2555
2556/* Send a Bind Update message. */
2557
2558isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
2559 struct lease *lease)
2560{
2561 dhcp_failover_link_t *link;
2562 isc_result_t status;
2563#if defined (DEBUG_FAILOVER_MESSAGES)
2564 char obuf [64];
2565 unsigned obufix = 0;
2566
2567# define FMA obuf, &obufix, sizeof obuf
2568 failover_print (FMA, "(bndupd");
2569#else
2570# define FMA (unsigned char *)0, (unsigned *)0, 0
2571#endif
2572
2573 if (!state -> link_to_peer ||
2574 state -> link_to_peer -> type != dhcp_type_failover_link)
dd53dc5a 2575 return ISC_R_INVALIDARG;
9f3da356 2576 link = (dhcp_failover_link_t *)state -> link_to_peer;
dd53dc5a
TL
2577
2578 if (!link -> outer || link -> outer -> type != omapi_type_connection)
2579 return ISC_R_INVALIDARG;
2580
2581 status = (dhcp_failover_put_message
2582 (link, link -> outer,
2583 FTM_BNDUPD,
2584 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
85a53735 2585 lease -> ip_addr.len,
dd53dc5a
TL
2586 lease -> ip_addr.iabuf),
2587 dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
2588 0 /* ??? */),
2589 lease -> uid_len
2590 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
25a69ca1
TL
2591 lease -> uid_len,
2592 lease -> uid)
dd53dc5a
TL
2593 : &skip_failover_option,
2594 dhcp_failover_make_option (FTO_CHADDR, FMA,
84fe3bd0 2595 lease -> hardware_addr.hlen,
dd53dc5a
TL
2596 lease -> hardware_addr.hbuf),
2597 dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
2598 lease -> ends),
2599 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
2600 lease -> tstp),
2601 dhcp_failover_make_option (FTO_STOS, FMA,
2602 lease -> starts),
2603 dhcp_failover_make_option (FTO_CLTT, FMA,
2604 lease -> cltt),
2605 &skip_failover_option, /* XXX DDNS */
2606 &skip_failover_option, /* XXX request options */
2607 &skip_failover_option, /* XXX reply options */
2608 (failover_option_t *)0));
2609
2610#if defined (DEBUG_FAILOVER_MESSAGES)
2611 if (status != ISC_R_SUCCESS)
2612 failover_print (FMA, " (failed)");
2613 failover_print (FMA, ")");
2614 if (obufix) {
2615 log_debug ("%s", obuf);
2616 }
2617#endif
2618 return status;
2619}
2620
9f3da356
TL
2621isc_result_t dhcp_failover_process_bindupdate (dhcp_failover_state_t *state,
2622 failover_message_t *msg) {
2623 log_info ("failover: bind update.");
2624 return ISC_R_SUCCESS;
2625}
2626
2627isc_result_t dhcp_failover_process_bindack (dhcp_failover_state_t *state,
2628 failover_message_t *msg) {
2629 log_info ("failover: bind ack.");
2630 return ISC_R_SUCCESS;
2631}
2632
d8a417b2
TL
2633#if defined (DEBUG_FAILOVER_MESSAGES)
2634/* Print hunks of failover messages, doing line breaks as appropriate.
2635 Note that this assumes syslog is being used, rather than, e.g., the
2636 Windows NT logging facility, where just dumping the whole message in
2637 one hunk would be more appropriate. */
2638
2639void failover_print (char *obuf,
2640 unsigned *obufix, unsigned obufmax, const char *s)
2641{
2642 int len = strlen (s);
2643
9f3da356
TL
2644 while (len + *obufix + 1 >= obufmax) {
2645 log_debug ("%s", obuf);
d8a417b2
TL
2646 if (!*obufix) {
2647 log_debug ("%s", s);
9f3da356 2648 *obufix = 0;
d8a417b2
TL
2649 return;
2650 }
d8a417b2 2651 *obufix = 0;
d8a417b2 2652 }
9f3da356
TL
2653 strcpy (&obuf [*obufix], s);
2654 *obufix += len;
d8a417b2
TL
2655}
2656#endif /* defined (DEBUG_FAILOVER_MESSAGES) */
d50d0b82 2657
dd53dc5a
TL
2658void update_partner (struct lease *lease)
2659{
2660}
85a53735
TL
2661
2662/* Taken from draft-ietf-dhc-loadb-01.txt: */
2663/* A "mixing table" of 256 distinct values, in pseudo-random order. */
2664unsigned char loadb_mx_tbl[256] = {
2665 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
2666 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
2667 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
2668 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
2669 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
2670 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
2671 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
2672 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
2673 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
2674 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
2675 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
2676 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
2677 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
2678 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
2679 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
2680 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
2681 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
2682 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
2683 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
2684 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
2685 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
2686 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
2687 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
2688 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
2689 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
2690 170, 68, 6, 169, 234, 151 };
2691
2692static unsigned char loadb_p_hash (const unsigned char *, unsigned);
2693static unsigned char loadb_p_hash (const unsigned char *key, unsigned len)
2694{
2695 unsigned char hash = len;
2696 int i;
2697 for(i = len; i > 0; )
2698 hash = loadb_mx_tbl [hash ^ (key [--i])];
2699 return hash;
2700}
2701
2702int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
2703{
2704 struct option_cache *oc;
2705 struct data_string ds;
2706 unsigned char hbaix;
2707 int hm;
2708
2709 if (state -> load_balance_max_secs < ntohs (packet -> raw -> secs)) {
2710 return 1;
2711 }
2712
2713 oc = lookup_option (&dhcp_universe, packet -> options,
2714 DHO_DHCP_CLIENT_IDENTIFIER);
2715 memset (&ds, 0, sizeof ds);
2716 if (oc &&
2717 evaluate_option_cache (&ds, packet, (struct lease *)0,
2718 packet -> options, (struct option_state *)0,
2719 &global_scope, oc, MDL)) {
2720 hbaix = loadb_p_hash (ds.data, ds.len);
2721 } else {
2722 hbaix = loadb_p_hash (packet -> raw -> chaddr,
2723 packet -> raw -> hlen);
2724 }
2725 hm = (state -> hba [hbaix / 8] & (1 << (hbaix & 3)));
2726 if (state -> i_am == primary)
2727 return hm;
2728 else
2729 return !hm;
2730}
c68d2a87 2731#endif /* defined (FAILOVER_PROTOCOL) */